int testQueryBuilder()

in storage/ndb/ndbapi-examples/ndbapi_multi_cursor/main.cpp [272:1044]


int testQueryBuilder(Ndb &myNdb)
{
  const NdbDictionary::Table *manager, *employee, *salary;
  int res;
  NdbTransaction* myTransaction = NULL;
  NdbQuery* myQuery = NULL;

  const char* dept_no = "d005";
  Uint32 emp_no = 110567;

  ManagerRow  managerRow;
  EmployeeRow employeeRow;

  printf("\n -- Building query --\n");

  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();
  manager = myDict->getTable("dept_manager");
  employee= myDict->getTable("employees");
  salary  = myDict->getTable("salaries");

  if (!employee || !manager || !salary) 
    APIERROR(myDict->getNdbError());

  ////////////////////////////////////////////////
  // Prepare alternatine non-default NdbRecords for MANAGER table
  ////////////////////////////////////////////////
  NdbRecord *rowManagerRecord;

  {
    const NdbDictionary::Column *manager_dept_no;
    const NdbDictionary::Column *manager_emp_no;
    const NdbDictionary::Column *manager_from_date;
    const NdbDictionary::Column *manager_to_date;

    manager_dept_no = manager->getColumn("dept_no");
    if (manager_dept_no == NULL) APIERROR(myDict->getNdbError());
    manager_emp_no = manager->getColumn("emp_no");
    if (manager_emp_no == NULL) APIERROR(myDict->getNdbError());
    manager_from_date = manager->getColumn("from_date");
    if (manager_from_date == NULL) APIERROR(myDict->getNdbError());
    manager_to_date = manager->getColumn("to_date");
    if (manager_to_date == NULL) APIERROR(myDict->getNdbError());

    const NdbDictionary::RecordSpecification mngSpec[] = {
      {manager_emp_no,    offsetof(ManagerRow, emp_no),    0,0},
//      {manager_dept_no,   offsetof(ManagerRow, dept_no),   0,0},
//      {manager_from_date, offsetof(ManagerRow, from_date), 0,0},
      {manager_to_date,   offsetof(ManagerRow, to_date),   0,0}
    };

    rowManagerRecord = 
      myDict->createRecord(manager, mngSpec, 2, sizeof(mngSpec[0]));
    if (rowManagerRecord == NULL) APIERROR(myDict->getNdbError());
  }


  /**
   * Some very basic examples which are actually not Query*Trees*, but rather
   * single QueryOperation defined with the NdbQueryBuilder.
   * Mainly to illustrate how the NdbQueryOperand may be specified
   * either as a constant or a parameter value - A combination
   * thereoff would also be sensible.
   *
   * Main purpose is to examplify how NdbQueryBuilder is used to prepare
   * reusable query object - no ::execute() is performed yet.
   */
  NdbQueryBuilder* const myBuilder = NdbQueryBuilder::create(myNdb);

#if 0
  printf("Compare with old API interface\n");

  {
    myTransaction= myNdb.startTransaction();
    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());

    // Lookup Primary key for manager table
    const NdbDictionary::Index *myPIndex= myDict->getIndex("PRIMARY", manager->getName());
    if (myPIndex == NULL)
      APIERROR(myDict->getNdbError());

    NdbIndexScanOperation* ixScan = 
      myTransaction->scanIndex(myPIndex->getDefaultRecord(),
                               manager->getDefaultRecord());

    if (ixScan == NULL)
      APIERROR(myTransaction->getNdbError());


    /* Add a bound
     */
    ManagerPKRow low={0,"d005"};
    ManagerPKRow high={110567,"d005"};

    NdbIndexScanOperation::IndexBound bound;
    bound.low_key=(char*)&low;
    bound.low_key_count=2;
    bound.low_inclusive=true;
    bound.high_key=(char*)&high;
    bound.high_key_count=2;
    bound.high_inclusive=false;
    bound.range_no=0;

    if (ixScan->setBound(myPIndex->getDefaultRecord(), bound))
      APIERROR(myTransaction->getNdbError());
  }
#endif

#if 1
  /* qt1 is 'const defined' */
  printf("q1\n");
  const NdbQueryDef* q1 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

    const NdbQueryOperand* managerKey[] =  // Manager is indexed om {"dept_no", "emp_no"}
    {  qb->constValue("d005"),             // dept_no = "d005"
       qb->constValue(110567),             // emp_no  = 110567
       0
    };
    const NdbQueryLookupOperationDef *readManager = qb->readTuple(manager, managerKey);
    if (readManager == NULL) APIERROR(qb->getNdbError());

    q1 = qb->prepare();
    if (q1 == NULL) APIERROR(qb->getNdbError());

    // Some operations are intentionally disallowed through private declaration 
//  delete readManager;
//  NdbQueryLookupOperationDef illegalAssign = *readManager;
//  NdbQueryLookupOperationDef *illegalCopy1 = new NdbQueryLookupOperationDef(*readManager);
//  NdbQueryLookupOperationDef illegalCopy2(*readManager);
  }

  printf("q2\n");
  const NdbQueryDef* q2 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

    // Manager key defined as parameter 
    const NdbQueryOperand* managerKey[] =       // Manager is indexed om {"dept_no", "emp_no"}
    {  qb->paramValue(),         // dept_no parameter,
       qb->paramValue("emp"),    // emp_no parameter - param naming is optional
       0
    };
    // Lookup on a single tuple with key define by 'managerKey' param. tuple
    const NdbQueryLookupOperationDef* readManager = qb->readTuple(manager, managerKey);
    if (readManager == NULL) APIERROR(qb->getNdbError());

    q2 = qb->prepare();
    if (q2 == NULL) APIERROR(qb->getNdbError());
  }

/**** UNFINISHED...
  printf("q3\n");
  const NdbQueryDef* q3 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

    const NdbQueryIndexBound* managerBound =       // Manager is indexed om {"dept_no", "emp_no"}
    {  ....
    };
    // Lookup on a single tuple with key define by 'managerKey' param. tuple
    const NdbQueryScanNode *scanManager = qb->scanIndex(manager, managerKey);
    if (scanManager == NULL) APIERROR(qb->getNdbError());

    q3 = qb->prepare();
    if (q3 == NULL) APIERROR(qb->getNdbError());
  }
*****/
#endif


#if 1
{
  /* Composite operations building real *trees* aka. linked operations.
   * (First part is identical to building 'qt2' above)
   *
   * The related SQL query which this simulates would be something like:
   *
   * select * from dept_manager join employees using(emp_no)
   *  where dept_no = 'd005' and emp_no = 110567;
   */
  printf("q4\n");
  const NdbQueryDef* q4 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

    const NdbQueryOperand* constManagerKey[] =  // Manager is indexed om {"dept_no", "emp_no"}
    {  qb->constValue("d005"),   // dept_no = "d005"
       qb->constValue(110567),   // emp_no  = 110567
       0
    };
    const NdbQueryOperand* paramManagerKey[] =       // Manager is indexed om {"dept_no", "emp_no"}
    {  qb->paramValue(),         // dept_no parameter,
       qb->paramValue("emp"),    // emp_no parameter - param naming is optional
       0
    };
    // Lookup a single tuple with key define by 'managerKey' param. tuple
    const NdbQueryLookupOperationDef *readManager = qb->readTuple(manager, paramManagerKey);
    //const NdbQueryLookupOperationDef *readManager = qb->readTuple(manager, constManagerKey);
    if (readManager == NULL) APIERROR(qb->getNdbError());

    // THEN: employee table is joined:
    //    A linked value is used to let employee lookup refer values
    //    from the parent operation on manger.

    const NdbQueryOperand* joinEmployeeKey[] =       // Employee is indexed om {"emp_no"}
    {  qb->linkedValue(readManager, "emp_no"),  // where '= readManger.emp_no'
       0
    };
    const NdbQueryLookupOperationDef* readEmployee = qb->readTuple(employee, joinEmployeeKey);
    if (readEmployee == NULL) APIERROR(qb->getNdbError());

    q4 = qb->prepare();
    if (q4 == NULL) APIERROR(qb->getNdbError());
  }

  ///////////////////////////////////////////////////
  // q4 may later be executed as:
  // (Possibly multiple ::execute() or multiple NdbQueryDef instances 
  // within the same NdbTransaction::execute(). )
  ////////////////////////////////////////////////////
  NdbQueryParamValue paramList[] = {dept_no, emp_no};

  myTransaction= myNdb.startTransaction();
  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());

  myQuery = myTransaction->createQuery(q4,paramList);
  if (myQuery == NULL)
    APIERROR(myTransaction->getNdbError());

#ifdef USE_RECATTR
  const NdbRecAttr *key[2][2];

  for (Uint32 i=0; i<myQuery->getNoOfOperations(); ++i)
  {
    NdbQueryOperation* op = myQuery->getQueryOperation(i);
    const NdbDictionary::Table* table = op->getQueryOperationDef().getTable();

    key[i][0] =  op->getValue(table->getColumn(0));
    key[i][1] =  op->getValue(table->getColumn(1));
  }

#else
{
  memset (&managerRow,  0, sizeof(managerRow));
  memset (&employeeRow, 0, sizeof(employeeRow));
  const NdbRecord* rowManagerRecord = manager->getDefaultRecord();
  if (rowManagerRecord == NULL) APIERROR(myDict->getNdbError());

  const NdbRecord* rowEmployeeRecord = employee->getDefaultRecord();
  if (rowEmployeeRecord == NULL) APIERROR(myDict->getNdbError());

  assert(myQuery->getNoOfOperations()==2);
  NdbQueryOperation* op0 = myQuery->getQueryOperation(0U);
  NdbQueryOperation* op1 = myQuery->getQueryOperation(1U);

  op0->setResultRowBuf(rowManagerRecord, (char*)&managerRow);
  op1->setResultRowBuf(rowEmployeeRecord, (char*)&employeeRow);
}
#endif

  printf("Start execute\n");
  if (myTransaction->execute(NdbTransaction::NoCommit) != 0 ||
      myQuery->getNdbError().code)
  {
    APIERROR(myQuery->getNdbError());
  }
  printf("Done executed\n");

  // All NdbQuery operations are handled as scans with cursor placed 'before'
  // first record: Fetch next to retrieve result:
  res = myQuery->nextResult();
  if (res == NdbQuery::NextResult_error)
    APIERROR(myQuery->getNdbError());

#ifdef USE_RECATTR
  printf("manager  emp_no: %d\n", key[0][1]->u_32_value());
  printf("employee emp_no: %d\n", key[1][0]->u_32_value());
  assert(!key[0][1]->isNULL() && key[0][1]->u_32_value()==emp_no);
  assert(!key[1][0]->isNULL() && key[1][0]->u_32_value()==emp_no);
#else
  // NOW: Result is available in 'managerRow' buffer
  printf("manager  emp_no: %d\n", managerRow.emp_no);
  printf("employee emp_no: %d\n", employeeRow.emp_no);
  assert(managerRow.emp_no==emp_no);
  assert(employeeRow.emp_no==emp_no);
#endif

  myQuery->close();

  myNdb.closeTransaction(myTransaction);
  myTransaction = 0;
}
#endif


#if 1
{
  //////////////////////////////////////////////////
  printf("q4_1\n");
  const NdbQueryDef* q4_1 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

    const NdbQueryOperand* empKey[] =       // Employee is indexed om {"emp_no"}
    {
       //qb->constValue(110567),   // emp_no  = 110567
       qb->paramValue(),
       0
    };
    const NdbQueryLookupOperationDef* readEmployee = qb->readTuple(employee, empKey);
    if (readEmployee == NULL) APIERROR(qb->getNdbError());

    const NdbQueryOperand* joinManagerKey[] =  // Manager is indexed om {"dept_no", "emp_no"}
    {
      qb->paramValue(),
      //qb->constValue(1005),   // dept_no = "d005"
      //qb->linkedValue(readEmployee,"dept_no"),
      qb->linkedValue(readEmployee,"emp_no"),   // emp_no  = 110567
      //qb->constValue(110567),
      //qb->paramValue(),
      0
    };

    // Join with a single tuple with key defined by linked employee fields
    const NdbQueryLookupOperationDef *readManager = qb->readTuple(manager, joinManagerKey);
    if (readManager == NULL) APIERROR(qb->getNdbError());

    q4_1 = qb->prepare();
    if (q4_1 == NULL) APIERROR(qb->getNdbError());
  }

  ///////////////////////////////////////////////////
  // q4 may later be executed as:
  // (Possibly multiple ::execute() or multiple NdbQueryDef instances 
  // within the same NdbTransaction::execute(). )
  ////////////////////////////////////////////////////

//NdbQueryParamValue paramList_q4[] = {emp_no};
//NdbQueryParamValue paramList_q4[] = {dept_no};
  NdbQueryParamValue paramList_q4[] = {emp_no, dept_no};

  myTransaction= myNdb.startTransaction();
  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());

  myQuery = myTransaction->createQuery(q4_1,paramList_q4);
  if (myQuery == NULL)
    APIERROR(myTransaction->getNdbError());

#ifdef USE_RECATTR
  const NdbRecAttr *value_q4[2][2];

  for (Uint32 i=0; i<myQuery->getNoOfOperations(); ++i)
  {
    NdbQueryOperation* op = myQuery->getQueryOperation(i);
    const NdbDictionary::Table* table = op->getQueryOperationDef().getTable();

    value_q4[i][0] =  op->getValue(table->getColumn(0));
    value_q4[i][1] =  op->getValue(table->getColumn(1));
  }
#else
{
  memset (&managerRow,  0, sizeof(managerRow));
  memset (&employeeRow, 0, sizeof(employeeRow));
  const NdbRecord* rowEmployeeRecord = employee->getDefaultRecord();
  if (rowEmployeeRecord == NULL) APIERROR(myDict->getNdbError());

  const NdbRecord* rowManagerRecord = manager->getDefaultRecord();
  if (rowManagerRecord == NULL) APIERROR(myDict->getNdbError());

  assert(myQuery->getNoOfOperations()==2);
  NdbQueryOperation* op0 = myQuery->getQueryOperation(0U);
  NdbQueryOperation* op1 = myQuery->getQueryOperation(1U);

  op0->setResultRowBuf(rowEmployeeRecord, (char*)&employeeRow);
  op1->setResultRowBuf(rowManagerRecord, (char*)&managerRow);
}
#endif

  printf("Start execute\n");
  if (myTransaction->execute(NdbTransaction::NoCommit) != 0 ||
      myQuery->getNdbError().code)
  {
    APIERROR(myQuery->getNdbError());
  }
  printf("Done executed\n");

  // All NdbQuery operations are handled as scans with cursor placed 'before'
  // first record: Fetch next to retrieve result:
  res = myQuery->nextResult();
  if (res == NdbQuery::NextResult_error)
    APIERROR(myQuery->getNdbError());

#ifdef USE_RECATTR
  printf("employee emp_no: %d\n", value_q4[0][0]->u_32_value());
  printf("manager  emp_no: %d\n", value_q4[1][1]->u_32_value());
  assert(!value_q4[0][0]->isNULL() && value_q4[0][0]->u_32_value()==emp_no);
  assert(!value_q4[1][1]->isNULL() && value_q4[1][1]->u_32_value()==emp_no);

#else
  printf("employee emp_no: %d\n", employeeRow.emp_no);
  printf("manager  emp_no: %d\n", managerRow.emp_no);
  assert(managerRow.emp_no==emp_no);
  assert(employeeRow.emp_no==emp_no);
#endif

  myQuery->close();

  myNdb.closeTransaction(myTransaction);
  myTransaction = 0;
}
#endif

  /////////////////////////////////////////////////

#if 1
{
  // Example: ::readTuple() using Index for unique key lookup
  printf("q5\n");

  const NdbQueryDef* q5 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

    // Lookup Primary key for manager table
    const NdbDictionary::Index *myPIndex= myDict->getIndex("MYINDEXNAME$unique", manager->getName());
    if (myPIndex == NULL)
      APIERROR(myDict->getNdbError());

    // Manager index-key defined as parameter, NB: Reversed order compared to hash key
    const NdbQueryOperand* managerKey[] =  // Manager PK index is {"emp_no","dept_no", }
    {
       //qb->constValue(110567),   // emp_no  = 110567
       qb->paramValue(),
       0
    };
    // Lookup on a single tuple with key define by 'managerKey' param. tuple
    const NdbQueryLookupOperationDef* readManager = qb->readTuple(myPIndex, manager, managerKey);
    if (readManager == NULL) APIERROR(qb->getNdbError());

    q5 = qb->prepare();
    if (q5 == NULL) APIERROR(qb->getNdbError());
  }

  myTransaction= myNdb.startTransaction();
  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());

  NdbQueryParamValue paramList_q5[] = {emp_no};
  myQuery = myTransaction->createQuery(q5,paramList_q5);
  if (myQuery == NULL)
    APIERROR(myTransaction->getNdbError());

#ifdef USE_RECATTR
  const NdbRecAttr *value_q5[2];

  NdbQueryOperation* op = myQuery->getQueryOperation(0U);
  const NdbDictionary::Table* table = op->getQueryOperationDef().getTable();

  value_q5[0] = op->getValue(table->getColumn(0));
  value_q5[1] = op->getValue(table->getColumn(1));
#else
{
  memset (&managerRow, 0, sizeof(managerRow));
  const NdbRecord* rowManagerRecord = manager->getDefaultRecord();
  if (rowManagerRecord == NULL) APIERROR(myDict->getNdbError());

  // Specify result handling NdbRecord style - need the (single) NdbQueryOperation:
  NdbQueryOperation* op = myQuery->getQueryOperation(0U);

  op->setResultRowBuf(rowManagerRecord, (char*)&managerRow);
}
#endif

  printf("Start execute\n");
  if (myTransaction->execute(NdbTransaction::NoCommit) != 0 ||
      myQuery->getNdbError().code)
  {
    APIERROR(myQuery->getNdbError());
  }
  printf("Done executed\n");

  // All NdbQuery operations are handled as scans with cursor placed 'before'
  // first record: Fetch next to retrieve result:
  res = myQuery->nextResult();
  if (res == NdbQuery::NextResult_error)
    APIERROR(myQuery->getNdbError());

#ifdef USE_RECATTR
  printf("employee emp_no: %d\n", value_q5[1]->u_32_value());
  assert(!value_q5[1]->isNULL() && value_q5[1]->u_32_value()==emp_no);
#else
  printf("employee emp_no: %d\n", managerRow.emp_no);
  assert(managerRow.emp_no==emp_no);
#endif

  myQuery->close();

  myNdb.closeTransaction(myTransaction);
  myTransaction = 0;
}
#endif

#if 1
{
  printf("q6: Table scan + linked lookup\n");

  const NdbQueryDef* q6 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

/****
    // Lookup Primary key for manager table
    const NdbDictionary::Index *myPIndex= myDict->getIndex("PRIMARY", manager->getName());
    if (myPIndex == NULL)
      APIERROR(myDict->getNdbError());

    const NdbQueryOperand* low[] =  // Manager PK index is {"emp_no","dept_no", }
    {  qb->constValue(110567),      // emp_no  = 110567
       0
    };
    const NdbQueryOperand* high[] =  // Manager PK index is {"emp_no","dept_no", }
    {  qb->constValue("illegal key"),
       0
    };
    const NdbQueryIndexBound  bound        (low, NULL);   // emp_no = [110567, oo]
    const NdbQueryIndexBound  bound_illegal(low, high);   // 'high' is char type -> illegal
    const NdbQueryIndexBound  boundEq(low);
****/
    // Lookup on a single tuple with key define by 'managerKey' param. tuple
//  const NdbQueryScanOperationDef* scanManager = qb->scanIndex(myPIndex, manager, &boundEq);
    const NdbQueryScanOperationDef* scanManager = qb->scanTable(manager);
    if (scanManager == NULL) APIERROR(qb->getNdbError());

    // THEN: employee table is joined:
    //    A linked value is used to let employee lookup refer values
    //    from the parent operation on manager.

    const NdbQueryOperand* empJoinKey[] =       // Employee is indexed om {"emp_no"}
    {  qb->linkedValue(scanManager, "emp_no"),  // where '= readManger.emp_no'
       0
    };
    const NdbQueryLookupOperationDef* readEmployee = qb->readTuple(employee, empJoinKey);
    if (readEmployee == NULL) APIERROR(qb->getNdbError());

    q6 = qb->prepare();
    if (q6 == NULL) APIERROR(qb->getNdbError());
  }

  myTransaction= myNdb.startTransaction();
  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());

  myQuery = myTransaction->createQuery(q6, (NdbQueryParamValue*)0);
  if (myQuery == NULL)
    APIERROR(myTransaction->getNdbError());

#ifdef USE_RECATTR
  const NdbRecAttr* value_q6[2][2];

  for (Uint32 i=0; i<myQuery->getNoOfOperations(); ++i)
  {
    NdbQueryOperation* op = myQuery->getQueryOperation(i);
    const NdbDictionary::Table* table = op->getQueryOperationDef().getTable();

    value_q6[i][0] =  op->getValue(table->getColumn(0));
    value_q6[i][1] =  op->getValue(table->getColumn(1));
  }
#else
{
  int err;
  const NdbRecord* rowManagerRecord = manager->getDefaultRecord();
  if (rowManagerRecord == NULL) APIERROR(myDict->getNdbError());

  assert(myQuery->getNoOfOperations()==2);
  NdbQueryOperation* op0 = myQuery->getQueryOperation(0U);
  err = op0->setResultRowBuf(rowManagerRecord, (char*)&managerRow);
  assert (err==0);
//if (err == NULL) APIERROR(op0->getNdbError());

  const NdbRecord* rowEmployeeRecord = employee->getDefaultRecord();
  if (rowEmployeeRecord == NULL) APIERROR(myDict->getNdbError());

  NdbQueryOperation* op1 = myQuery->getQueryOperation(1U);
  err = op1->setResultRowBuf(rowEmployeeRecord, (char*)&employeeRow);
  assert (err==0);
//if (err == NULL) APIERROR(op1->getNdbError());
}
#endif

  printf("Start execute\n");
  if (myTransaction->execute(NdbTransaction::NoCommit) != 0 ||
      myQuery->getNdbError().code)
  {
    APIERROR(myQuery->getNdbError());
  }

  int cnt = 0;
  while (true) {
    memset (&managerRow,  0, sizeof(managerRow));
    memset (&employeeRow, 0, sizeof(employeeRow));

    // All NdbQuery operations are handled as scans with cursor placed 'before'
    // first record: Fetch next to retrieve result:
    NdbQuery::NextResultOutcome res = myQuery->nextResult();

    if (res == NdbQuery::NextResult_error) {
      PRINT_APIERROR(myQuery->getNdbError());
      break;
    } else if (res!=NdbQuery::NextResult_gotRow) {
      break;
    }

#ifdef USE_RECATTR
    printf("manager  emp_no: %d, NULL:%d\n",
            value_q6[0][1]->u_32_value(), myQuery->getQueryOperation(0U)->isRowNULL());
    printf("employee emp_no: %d, NULL:%d\n",
            value_q6[1][0]->u_32_value(), myQuery->getQueryOperation(1U)->isRowNULL());
#else
    // NOW: Result is available in row buffers
    printf("manager  emp_no: %d, NULL:%d\n",
            managerRow.emp_no, myQuery->getQueryOperation(0U)->isRowNULL());
    printf("employee emp_no: %d, NULL:%d\n",
            employeeRow.emp_no, myQuery->getQueryOperation(1U)->isRowNULL());
#endif
    cnt++;
  };
  printf("EOF, %d rows\n", cnt);
  myQuery->close();

  myNdb.closeTransaction(myTransaction);
  myTransaction = 0;
}
#endif

#if 1
{
  printf("Ordered index scan + lookup\n");

  const NdbQueryDef* q6_1 = 0;
  {
    NdbQueryBuilder* qb = myBuilder; //myDict->getQueryBuilder();

    // Lookup Primary key for manager table
    const NdbDictionary::Index *myPIndex= myDict->getIndex("PRIMARY", manager->getName());
    if (myPIndex == NULL)
      APIERROR(myDict->getNdbError());

    const NdbQueryOperand* low[] =  // Manager PK index is {"emp_no","dept_no", }
    {
       qb->paramValue(),
//     qb->constValue(110567),      // emp_no  = 110567
       qb->constValue("d005"),      // dept_no = "d005"
       0
    };
    const NdbQueryOperand* high[] =  // Manager PK index is {"emp_no","dept_no", }
    {
       qb->constValue(110567),      // emp_no  = 110567
       qb->constValue("d005"),      // dept_no = "d005"
       0
    };
    const NdbQueryIndexBound  bound        (low, high);   // emp_no = [110567, oo]
    const NdbQueryIndexBound  boundEq(low);

    // Lookup on a single tuple with key define by 'managerKey' param. tuple
    const NdbQueryScanOperationDef* scanManager = qb->scanIndex(myPIndex, manager, &bound);
    if (scanManager == NULL) APIERROR(qb->getNdbError());

    // THEN: employee table is joined:
    //    A linked value is used to let employee lookup refer values
    //    from the parent operation on manager.

    const NdbQueryOperand* empJoinKey[] =       // Employee is indexed om {"emp_no"}
    {  qb->linkedValue(scanManager, "emp_no"),  // where '= readManger.emp_no'
       0
    };
    const NdbQueryLookupOperationDef* readEmployee = qb->readTuple(employee, empJoinKey);
    if (readEmployee == NULL) APIERROR(qb->getNdbError());

    q6_1 = qb->prepare();
    if (q6_1 == NULL) APIERROR(qb->getNdbError());
  }

  myTransaction= myNdb.startTransaction();
  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());

  NdbQueryParamValue paramList_q6_1[] = {emp_no};
  myQuery = myTransaction->createQuery(q6_1, paramList_q6_1);
  if (myQuery == NULL)
    APIERROR(myTransaction->getNdbError());

#ifdef USE_RECATTR
  const NdbRecAttr* value_q6_1[2][2];

  for (Uint32 i=0; i<myQuery->getNoOfOperations(); ++i)
  {
    NdbQueryOperation* op = myQuery->getQueryOperation(i);
    const NdbDictionary::Table* table = op->getQueryOperationDef().getTable();

    value_q6_1[i][1] =  op->getValue(table->getColumn(1));
    value_q6_1[i][0] =  op->getValue(table->getColumn(0));
  }
#else
{
  int err;
//int mask = 0x03;
  const NdbRecord* rowManagerRecord = manager->getDefaultRecord();
  if (rowManagerRecord == NULL) APIERROR(myDict->getNdbError());

  assert(myQuery->getNoOfOperations()==2);
  NdbQueryOperation* op0 = myQuery->getQueryOperation(0U);
  err = op0->setResultRowBuf(rowManagerRecord, (char*)&managerRow /*, (const unsigned char*)&mask*/);
  assert (err==0);
  if (err) APIERROR(myQuery->getNdbError());

  const NdbRecord* rowEmployeeRecord = employee->getDefaultRecord();
  if (rowEmployeeRecord == NULL) APIERROR(myDict->getNdbError());

  NdbQueryOperation* op1 = myQuery->getQueryOperation(1U);
  err = op1->setResultRowBuf(rowEmployeeRecord, (char*)&employeeRow  /*, (const unsigned char*)&mask*/);
  assert (err==0);
  if (err) APIERROR(myQuery->getNdbError());
}
#endif

  printf("Start execute\n");
  if (myTransaction->execute(NdbTransaction::NoCommit) != 0 ||
      myQuery->getNdbError().code)
  {
    APIERROR(myQuery->getNdbError());
  }
  printf("Done executed\n");

  int cnt = 0;
  while (true) {
    memset (&managerRow,  0, sizeof(managerRow));
    memset (&employeeRow, 0, sizeof(employeeRow));

    // All NdbQuery operations are handled as scans with cursor placed 'before'
    // first record: Fetch next to retrieve result:
    NdbQuery::NextResultOutcome res = myQuery->nextResult();

    if (res == NdbQuery::NextResult_error) {
      PRINT_APIERROR(myQuery->getNdbError());
      break;
    } else if (res!=NdbQuery::NextResult_gotRow) {
      break;
    }

#ifdef USE_RECATTR
    printf("manager  emp_no: %d, NULL:%d\n",
            value_q6_1[0][1]->u_32_value(), myQuery->getQueryOperation(0U)->isRowNULL());
    printf("employee emp_no: %d, NULL:%d\n",
            value_q6_1[1][0]->u_32_value(), myQuery->getQueryOperation(1U)->isRowNULL());
#else
    // NOW: Result is available in row buffers
    printf("manager  emp_no: %d, NULL:%d\n",
            managerRow.emp_no, myQuery->getQueryOperation(0U)->isRowNULL());
    printf("employee emp_no: %d, NULL:%d\n",
            employeeRow.emp_no, myQuery->getQueryOperation(1)->isRowNULL());
#endif
    cnt++;
  };

  printf("EOF, %d rows\n", cnt);
  myQuery->close();

  myNdb.closeTransaction(myTransaction);
  myTransaction = 0;
}
#endif

  myBuilder->destroy();
  return 0;
}