Documentation Home
MySQL NDB Cluster API Developer Guide
Related Documentation Download this Manual
PDF (US Ltr) - 3.7Mb
PDF (A4) - 3.7Mb


MySQL NDB Cluster API Developer Guide  / ...  / The NDB API  / NDB API Examples  /  NDB API Example Comparing RecAttr and NdbRecord

2.5.8 NDB API Example Comparing RecAttr and NdbRecord

This example illustrates the key differences between the old-styleNdbRecAttr API and the newer approach usingNdbRecord when performing some common tasks in an NDB API application.

The source code can be found can be found in the filestorage/ndb/ndbapi-examples/ndbapi_recattr_vs_record/main.cpp in the NDB Cluster source tree.

#include <mysql.h>#include <NdbApi.hpp>// Used for cout#include <stdio.h>#include <iostream>// Do we use old-style (NdbRecAttr?) or new style (NdbRecord?)enum ApiType {api_attr, api_record};static void run_application(MYSQL &, Ndb_cluster_connection &, ApiType);#define PRINT_ERROR(code,msg) \  std::cout << "Error in " << __FILE__ \            << ", line: " << __LINE__ \            << ", code: " << code \            << ", msg: " << msg << "." << std::endl#define MYSQLERROR(mysql) { \  PRINT_ERROR(mysql_errno(&mysql),mysql_error(&mysql)); \  exit(-1); }#define APIERROR(error) { \  PRINT_ERROR(error.code,error.message); \  exit(-1); }int main(int argc, char** argv){  if (argc != 4)  {    std::cout << "Arguments are <socket mysqld> "              << "<connect_string cluster> <attr|record>.\n";    exit(-1);  }  // ndb_init must be called first  ndb_init();  // connect to mysql server and cluster and run application  {    char * mysqld_sock  = argv[1];    const char *connection_string = argv[2];    ApiType accessType=api_attr;    // Object representing the cluster    Ndb_cluster_connection cluster_connection(connection_string);    // Connect to cluster management server (ndb_mgmd)    if (cluster_connection.connect(4 /* retries               */,   5 /* delay between retries */,   1 /* verbose               */))    {      std::cout << "Management server not ready within 30 sec.\n";      exit(-1);    }    // Optionally connect and wait for the storage nodes (ndbd's)    if (cluster_connection.wait_until_ready(30,0) < 0)    {      std::cout << "Cluster not ready within 30 sec.\n";      exit(-1);    }    // connect to mysql server    MYSQL mysql;    if ( !mysql_init(&mysql) ) {      std::cout << "mysql_init failed\n";      exit(-1);    }    if ( !mysql_real_connect(&mysql, "localhost", "root", "", "",     0, mysqld_sock, 0) )      MYSQLERROR(mysql);    if (0==strncmp("attr", argv[3], 4))    {      accessType=api_attr;    }    else if (0==strncmp("record", argv[3], 6))    {      accessType=api_record;    }    else    {      std::cout << "Bad access type argument : "                << argv[3] << "\n";      exit(-1);    }    // run the application code    run_application(mysql, cluster_connection, accessType);  }  ndb_end(0);  return 0;}static void init_ndbrecord_info(Ndb &);static void create_table(MYSQL &);static void do_insert(Ndb &, ApiType);static void do_update(Ndb &, ApiType);static void do_delete(Ndb &, ApiType);static void do_read(Ndb &, ApiType);static void do_mixed_read(Ndb &);static void do_mixed_update(Ndb &);static void do_scan(Ndb &, ApiType);static void do_mixed_scan(Ndb &);static void do_indexScan(Ndb &, ApiType);static void do_mixed_indexScan(Ndb&);static void do_read_and_delete(Ndb &);static void do_scan_update(Ndb&, ApiType);static void do_scan_delete(Ndb&, ApiType);static void do_scan_lock_reread(Ndb&, ApiType);static void do_all_extras_read(Ndb &myNdb);static void do_secondary_indexScan(Ndb &myNdb, ApiType accessType);static void do_secondary_indexScanEqual(Ndb &myNdb, ApiType accessType);static void do_interpreted_update(Ndb &myNdb, ApiType accessType);static void do_interpreted_scan(Ndb &myNdb, ApiType accessType);static void do_read_using_default(Ndb &myNdb);/* This structure is used describe how we want data read using * NDBRecord to be placed into memory.  This can make it easier * to work with data, but is not essential. */struct RowData{  int attr1;  int attr2;  int attr3;};/* Handy struct for representing the data in the * secondary index */struct IndexRow{  unsigned int attr3;  unsigned int attr2;};static void run_application(MYSQL &mysql,    Ndb_cluster_connection &cluster_connection,                            ApiType accessType){  /********************************************   * Connect to database via mysql-c          *   ********************************************/  mysql_query(&mysql, "CREATE DATABASE ndb_examples");  if (mysql_query(&mysql, "USE ndb_examples") != 0) MYSQLERROR(mysql);  create_table(mysql);  /********************************************   * Connect to database via NDB API           *   ********************************************/  // Object representing the database  Ndb myNdb( &cluster_connection, "ndb_examples" );  if (myNdb.init()) APIERROR(myNdb.getNdbError());  init_ndbrecord_info(myNdb);  /*   * Do different operations on database   */  do_insert(myNdb, accessType);  do_update(myNdb, accessType);  do_delete(myNdb, accessType);  do_read(myNdb, accessType);  do_mixed_read(myNdb);  do_mixed_update(myNdb);  do_read(myNdb, accessType);  do_scan(myNdb, accessType);  do_mixed_scan(myNdb);  do_indexScan(myNdb, accessType);  do_mixed_indexScan(myNdb);  do_read_and_delete(myNdb);  do_scan_update(myNdb, accessType);  do_scan_delete(myNdb, accessType);  do_scan_lock_reread(myNdb, accessType);  do_all_extras_read(myNdb);  do_secondary_indexScan(myNdb, accessType);  do_secondary_indexScanEqual(myNdb, accessType);  do_scan(myNdb, accessType);  do_interpreted_update(myNdb, accessType);  do_interpreted_scan(myNdb, accessType);  do_read_using_default(myNdb);  do_scan(myNdb, accessType);}/********************************************************* * Create a table named api_recattr_vs_record if it does not exist * *********************************************************/static void create_table(MYSQL &mysql){  if (mysql_query(&mysql,  "DROP TABLE IF EXISTS"  "  api_recattr_vs_record"))    MYSQLERROR(mysql);  if (mysql_query(&mysql,  "CREATE TABLE"  "  api_recattr_vs_record"  "    (ATTR1 INT UNSIGNED NOT NULL PRIMARY KEY,"  "     ATTR2 INT UNSIGNED NOT NULL,"                  "     ATTR3 INT UNSIGNED NOT NULL)"  "  ENGINE=NDB"))    MYSQLERROR(mysql);  /* Add ordered secondary index on 2 attributes, in reverse order */  if (mysql_query(&mysql,                  "CREATE INDEX"                  "  MYINDEXNAME"                  "  ON api_recattr_vs_record"                  "  (ATTR3, ATTR2)"))    MYSQLERROR(mysql);}/* Clunky statics for shared NdbRecord stuff */static const NdbDictionary::Column *pattr1Col;static const NdbDictionary::Column *pattr2Col;static const NdbDictionary::Column *pattr3Col;static const NdbRecord *pkeyColumnRecord;static const NdbRecord *pallColsRecord;static const NdbRecord *pkeyIndexRecord;static const NdbRecord *psecondaryIndexRecord;static int attr1ColNum;static int attr2ColNum;static int attr3ColNum;/************************************************************** * Initialise NdbRecord structures for table and index access * **************************************************************/static void init_ndbrecord_info(Ndb &myNdb){  /* Here we create various NdbRecord structures for accessing   * data using the tables and indexes on api_recattr_vs_record   * We could use the default NdbRecord structures, but then   * we wouldn't have the nice ability to read and write rows   * to and from the RowData and IndexRow structs   */  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                    myDict->getTable("api_recattr_vs_record");  NdbDictionary::RecordSpecification recordSpec[3];  if (myTable == NULL)    APIERROR(myDict->getNdbError());  pattr1Col = myTable->getColumn("ATTR1");  if (pattr1Col == NULL) APIERROR(myDict->getNdbError());  pattr2Col = myTable->getColumn("ATTR2");  if (pattr2Col == NULL) APIERROR(myDict->getNdbError());  pattr3Col = myTable->getColumn("ATTR3");  if (pattr3Col == NULL) APIERROR(myDict->getNdbError());  attr1ColNum = pattr1Col->getColumnNo();  attr2ColNum = pattr2Col->getColumnNo();  attr3ColNum = pattr3Col->getColumnNo();  // ATTR 1  recordSpec[0].column = pattr1Col;  recordSpec[0].offset = offsetof(RowData, attr1);  recordSpec[0].nullbit_byte_offset = 0; // Not nullable  recordSpec[0].nullbit_bit_in_byte = 0;  // ATTR 2  recordSpec[1].column = pattr2Col;  recordSpec[1].offset = offsetof(RowData, attr2);  recordSpec[1].nullbit_byte_offset = 0;   // Not nullable  recordSpec[1].nullbit_bit_in_byte = 0;  // ATTR 3  recordSpec[2].column = pattr3Col;  recordSpec[2].offset = offsetof(RowData, attr3);  recordSpec[2].nullbit_byte_offset = 0;   // Not nullable  recordSpec[2].nullbit_bit_in_byte = 0;  /* Create table record with just the primary key column */  pkeyColumnRecord =    myDict->createRecord(myTable, recordSpec, 1, sizeof(recordSpec[0]));  if (pkeyColumnRecord == NULL) APIERROR(myDict->getNdbError());  /* Create table record with all the columns */  pallColsRecord =    myDict->createRecord(myTable, recordSpec, 3, sizeof(recordSpec[0]));  if (pallColsRecord == NULL) APIERROR(myDict->getNdbError());  /* Create NdbRecord for primary index access */  const NdbDictionary::Index *myPIndex=                    myDict->getIndex("PRIMARY", "api_recattr_vs_record");  if (myPIndex == NULL)    APIERROR(myDict->getNdbError());  pkeyIndexRecord =    myDict->createRecord(myPIndex, recordSpec, 1, sizeof(recordSpec[0]));  if (pkeyIndexRecord == NULL) APIERROR(myDict->getNdbError());  /* Create Index NdbRecord for secondary index access   * Note that we use the columns from the table to define the index   * access record   */  const NdbDictionary::Index *mySIndex=                myDict->getIndex("MYINDEXNAME", "api_recattr_vs_record");  recordSpec[0].column= pattr3Col;  recordSpec[0].offset= offsetof(IndexRow, attr3);  recordSpec[0].nullbit_byte_offset=0;  recordSpec[0].nullbit_bit_in_byte=0;  recordSpec[1].column= pattr2Col;  recordSpec[1].offset= offsetof(IndexRow, attr2);  recordSpec[1].nullbit_byte_offset=0;  recordSpec[1].nullbit_bit_in_byte=1;  /* Create NdbRecord for accessing via secondary index */  psecondaryIndexRecord =    myDict->createRecord(mySIndex,                         recordSpec,                         2,                         sizeof(recordSpec[0]));  if (psecondaryIndexRecord == NULL)    APIERROR(myDict->getNdbError());}/************************************************************************** * Using 5 transactions, insert 10 tuples in table: (0,0),(1,1),...,(9,9) * **************************************************************************/static void do_insert(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                            myDict->getTable("api_recattr_vs_record");  std::cout << "Running do_insert\n";  if (myTable == NULL)    APIERROR(myDict->getNdbError());  for (int i = 0; i < 5; i++) {    NdbTransaction *myTransaction= myNdb.startTransaction();    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());    switch (accessType)    {    case api_attr :      {        NdbOperation *myOperation= myTransaction->getNdbOperation(myTable);        if (myOperation == NULL) APIERROR(myTransaction->getNdbError());        myOperation->insertTuple();        myOperation->equal("ATTR1", i);        myOperation->setValue("ATTR2", i);        myOperation->setValue("ATTR3", i);        myOperation= myTransaction->getNdbOperation(myTable);        if (myOperation == NULL) APIERROR(myTransaction->getNdbError());        myOperation->insertTuple();        myOperation->equal("ATTR1", i+5);        myOperation->setValue("ATTR2", i+5);        myOperation->setValue("ATTR3", i+5);        break;      }    case api_record :      {        RowData row;        row.attr1= row.attr2= row.attr3= i;        const NdbOperation *pop1=          myTransaction->insertTuple(pallColsRecord, (char *) &row);        if (pop1 == NULL) APIERROR(myTransaction->getNdbError());        row.attr1= row.attr2= row.attr3= i+5;        const NdbOperation *pop2=          myTransaction->insertTuple(pallColsRecord, (char *) &row);        if (pop2 == NULL) APIERROR(myTransaction->getNdbError());        break;      }    default :      {        std::cout << "Bad branch : " << accessType << "\n";        exit(-1);      }    }    if (myTransaction->execute( NdbTransaction::Commit ) == -1)      APIERROR(myTransaction->getNdbError());    myNdb.closeTransaction(myTransaction);  }  std::cout << "-------\n";}/***************************************************************** * Update the second attribute in half of the tuples (adding 10) * *****************************************************************/static void do_update(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                            myDict->getTable("api_recattr_vs_record");  std::cout << "Running do_update\n";  for (int i = 0; i < 10; i+=2) {    NdbTransaction *myTransaction= myNdb.startTransaction();    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());    switch (accessType)    {      case api_attr :      {        NdbOperation *myOperation= myTransaction->getNdbOperation(myTable);        if (myOperation == NULL) APIERROR(myTransaction->getNdbError());        myOperation->updateTuple();        myOperation->equal( "ATTR1", i );        myOperation->setValue( "ATTR2", i+10);        myOperation->setValue( "ATTR3", i+20);        break;      }      case api_record :      {        RowData row;        row.attr1=i;        row.attr2=i+10;        row.attr3=i+20;        /* Since we're using an NdbRecord with all columns in it to         * specify the updated columns, we need to create a mask to         * indicate that we are only updating attr2 and attr3.         */        unsigned char attrMask=(1<<attr2ColNum) | (1<<attr3ColNum);        const NdbOperation *pop =          myTransaction->updateTuple(pkeyColumnRecord, (char*) &row,                                     pallColsRecord, (char*) &row,                                     &attrMask);        if (pop==NULL) APIERROR(myTransaction->getNdbError());        break;      }    default :      {        std::cout << "Bad branch : " << accessType << "\n";        exit(-1);      }    }    if( myTransaction->execute( NdbTransaction::Commit ) == -1 )      APIERROR(myTransaction->getNdbError());    myNdb.closeTransaction(myTransaction);  }  std::cout << "-------\n";};/************************************************* * Delete one tuple (the one with primary key 3) * *************************************************/static void do_delete(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                            myDict->getTable("api_recattr_vs_record");  std::cout << "Running do_delete\n";  if (myTable == NULL)    APIERROR(myDict->getNdbError());  NdbTransaction *myTransaction= myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  switch (accessType)  {  case api_attr :    {      NdbOperation *myOperation= myTransaction->getNdbOperation(myTable);      if (myOperation == NULL) APIERROR(myTransaction->getNdbError());      myOperation->deleteTuple();      myOperation->equal( "ATTR1", 3 );      break;    }  case api_record :    {      RowData keyInfo;      keyInfo.attr1=3;      const NdbOperation *pop=        myTransaction->deleteTuple(pkeyColumnRecord,                                   (char*) &keyInfo,                                   pallColsRecord);      if (pop==NULL) APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if (myTransaction->execute(NdbTransaction::Commit) == -1)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/***************************************************************** * Update the second attribute in half of the tuples (adding 10) * *****************************************************************/static void do_mixed_update(Ndb &myNdb){  /* This method performs an update using a mix of NdbRecord   * supplied attributes, and extra setvalues provided by   * the OperationOptions structure.   */  std::cout << "Running do_mixed_update (NdbRecord only)\n";  for (int i = 0; i < 10; i+=2) {    NdbTransaction *myTransaction= myNdb.startTransaction();    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());    RowData row;    row.attr1=i;    row.attr2=i+30;    /* Only attr2 is updated vian NDBRecord */    unsigned char attrMask= (1<<attr2ColNum);    NdbOperation::SetValueSpec setvalspecs[1];    /* Value to set attr3 to */    Uint32 dataSource= i + 40;    setvalspecs[0].column = pattr3Col;    setvalspecs[0].value = &dataSource;    NdbOperation::OperationOptions opts;    opts.optionsPresent= NdbOperation::OperationOptions::OO_SETVALUE;    opts.extraSetValues= &setvalspecs[0];    opts.numExtraSetValues= 1;    // Define mixed operation in one call to NDBAPI    const NdbOperation *pop =      myTransaction->updateTuple(pkeyColumnRecord, (char*) &row,                                 pallColsRecord, (char*) &row,                                 &attrMask,                                 &opts);    if (pop==NULL) APIERROR(myTransaction->getNdbError());    if( myTransaction->execute( NdbTransaction::Commit ) == -1 )      APIERROR(myTransaction->getNdbError());    myNdb.closeTransaction(myTransaction);  }  std::cout << "-------\n";}/********************************************* * Read and print all tuples using PK access * *********************************************/static void do_read(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                            myDict->getTable("api_recattr_vs_record");  std::cout << "Running do_read\n";  if (myTable == NULL)    APIERROR(myDict->getNdbError());  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  for (int i = 0; i < 10; i++) {    NdbTransaction *myTransaction= myNdb.startTransaction();    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());    RowData rowData;    NdbRecAttr *myRecAttr;    NdbRecAttr *myRecAttr2;    switch (accessType)    {      case api_attr :      {        NdbOperation *myOperation= myTransaction->getNdbOperation(myTable);        if (myOperation == NULL) APIERROR(myTransaction->getNdbError());        myOperation->readTuple(NdbOperation::LM_Read);        myOperation->equal("ATTR1", i);        myRecAttr= myOperation->getValue("ATTR2", NULL);        if (myRecAttr == NULL) APIERROR(myTransaction->getNdbError());        myRecAttr2=myOperation->getValue("ATTR3", NULL);        if (myRecAttr2 == NULL) APIERROR(myTransaction->getNdbError());        break;      }      case api_record :      {        rowData.attr1=i;        const NdbOperation *pop=          myTransaction->readTuple(pkeyColumnRecord,                                   (char*) &rowData,                                   pallColsRecord,  // Read PK+ATTR2+ATTR3                                   (char*) &rowData);        if (pop==NULL) APIERROR(myTransaction->getNdbError());        break;      }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }    }    if(myTransaction->execute( NdbTransaction::Commit ) == -1)      APIERROR(myTransaction->getNdbError());    if (myTransaction->getNdbError().classification == NdbError::NoDataFound)      if (i == 3)        std::cout << "Deleted tuple does not exist." << std::endl;      elseAPIERROR(myTransaction->getNdbError());    switch (accessType)    {      case api_attr :      {        if (i != 3) {          printf(" %2d    %2d    %2d\n",                 i,                 myRecAttr->u_32_value(),                 myRecAttr2->u_32_value());        }        break;      }      case api_record :      {        if (i !=3) {          printf(" %2d    %2d    %2d\n",                 i,                 rowData.attr2,                 rowData.attr3);        }        break;      }      default :      {        std::cout << "Bad branch : " << accessType << "\n";        exit(-1);      }    }    myNdb.closeTransaction(myTransaction);  }  std::cout << "-------\n";}/***************************** * Read and print all tuples * *****************************/static void do_mixed_read(Ndb &myNdb){  std::cout << "Running do_mixed_read (NdbRecord only)\n";  std::cout << "ATTR1 ATTR2 ATTR3 COMMIT_COUNT" << std::endl;  for (int i = 0; i < 10; i++) {    NdbTransaction *myTransaction= myNdb.startTransaction();    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());    RowData rowData;    NdbRecAttr *myRecAttr3, *myRecAttrCC;    /* Start with NdbRecord read of ATTR2, and then add     * getValue NdbRecAttr read of ATTR3 and Commit count     */    NdbOperation::GetValueSpec extraCols[2];    extraCols[0].column=pattr3Col;    extraCols[0].appStorage=NULL;    extraCols[0].recAttr=NULL;    extraCols[1].column=NdbDictionary::Column::COMMIT_COUNT;    extraCols[1].appStorage=NULL;    extraCols[1].recAttr=NULL;    NdbOperation::OperationOptions opts;    opts.optionsPresent = NdbOperation::OperationOptions::OO_GETVALUE;    opts.extraGetValues= &extraCols[0];    opts.numExtraGetValues= 2;    /* We only read attr2 using the normal NdbRecord access */    unsigned char attrMask= (1<<attr2ColNum);    // Set PK search criteria    rowData.attr1= i;    const NdbOperation *pop=      myTransaction->readTuple(pkeyColumnRecord,                               (char*) &rowData,                               pallColsRecord,  // Read all with mask                               (char*) &rowData,                               NdbOperation::LM_Read,                               &attrMask, // result_mask                               &opts);    if (pop==NULL) APIERROR(myTransaction->getNdbError());    myRecAttr3= extraCols[0].recAttr;    myRecAttrCC= extraCols[1].recAttr;    if (myRecAttr3 == NULL) APIERROR(myTransaction->getNdbError());    if (myRecAttrCC == NULL) APIERROR(myTransaction->getNdbError());    if(myTransaction->execute( NdbTransaction::Commit ) == -1)      APIERROR(myTransaction->getNdbError());    if (myTransaction->getNdbError().classification == NdbError::NoDataFound)      if (i == 3)        std::cout << "Deleted tuple does not exist." << std::endl;      elseAPIERROR(myTransaction->getNdbError());    if (i !=3) {      printf(" %2d    %2d    %2d    %d\n",             rowData.attr1,             rowData.attr2,             myRecAttr3->u_32_value(),             myRecAttrCC->u_32_value()             );    }    myNdb.closeTransaction(myTransaction);  }  std::cout << "-------\n";}/******************************************** * Read and print all tuples via table scan * ********************************************/static void do_scan(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                            myDict->getTable("api_recattr_vs_record");  std::cout << "Running do_scan\n";  if (myTable == NULL)    APIERROR(myDict->getNdbError());  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbScanOperation *psop;  NdbRecAttr *recAttrAttr1;  NdbRecAttr *recAttrAttr2;  NdbRecAttr *recAttrAttr3;  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbScanOperation(myTable);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      if (psop->readTuples(NdbOperation::LM_Read) != 0)        APIERROR (myTransaction->getNdbError());      recAttrAttr1=psop->getValue("ATTR1");      recAttrAttr2=psop->getValue("ATTR2");      recAttrAttr3=psop->getValue("ATTR3");      break;    }    case api_record :    {      /* Note that no row ptr is passed to the NdbRecord scan operation       * The scan will fetch a batch and give the user a series of pointers       * to rows in the batch in nextResult() below       */      psop=myTransaction->scanTable(pallColsRecord,                                    NdbOperation::LM_Read);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      break;    }  default :  {    std::cout << "Bad branch : " << accessType << "\n";    exit(-1);  }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {  case api_attr :  {    while (psop->nextResult(true) == 0)    {      printf(" %2d    %2d    %2d\n",             recAttrAttr1->u_32_value(),             recAttrAttr2->u_32_value(),             recAttrAttr3->u_32_value());    }    psop->close();    break;  }  case api_record :  {    RowData *prowData; // Ptr to point to our data    int rc=0;    /* Ask nextResult to update out ptr to point to the next     * row from the scan     */    while ((rc = psop->nextResult((const char**) &prowData,                                  true,                                  false)) == 0)    {      printf(" %2d    %2d    %2d\n",             prowData->attr1,             prowData->attr2,             prowData->attr3);    }    if (rc != 1)  APIERROR(myTransaction->getNdbError());    psop->close(true);    break;  }  default :  {    std::cout << "Bad branch : " << accessType << "\n";    exit(-1);  }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/*********************************************************** * Read and print all tuples via table scan and mixed read * ***********************************************************/static void do_mixed_scan(Ndb &myNdb){  std::cout << "Running do_mixed_scan(NdbRecord only)\n";  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbScanOperation *psop;  NdbRecAttr *recAttrAttr3;  /* Set mask so that NdbRecord scan reads attr1 and attr2 only */  unsigned char attrMask=((1<<attr1ColNum) | (1<<attr2ColNum));  /* Define extra get value to get attr3 */  NdbOperation::GetValueSpec extraGets[1];  extraGets[0].column = pattr3Col;  extraGets[0].appStorage= 0;  extraGets[0].recAttr= 0;  NdbScanOperation::ScanOptions options;  options.optionsPresent= NdbScanOperation::ScanOptions::SO_GETVALUE;  options.extraGetValues= &extraGets[0];  options.numExtraGetValues= 1;  psop=myTransaction->scanTable(pallColsRecord,                                NdbOperation::LM_Read,                                &attrMask,                                &options,                                sizeof(NdbScanOperation::ScanOptions));  if (psop == NULL) APIERROR(myTransaction->getNdbError());  /* RecAttr for the extra get has been set by the operation definition */  recAttrAttr3 = extraGets[0].recAttr;  if (recAttrAttr3 == NULL) APIERROR(myTransaction->getNdbError());  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  RowData *prowData; // Ptr to point to our data  int rc=0;  while ((rc = psop->nextResult((const char**) &prowData,                                true,                                false)) == 0)  {    printf(" %2d    %2d    %2d\n",           prowData->attr1,           prowData->attr2,           recAttrAttr3->u_32_value());  }  if (rc != 1)  APIERROR(myTransaction->getNdbError());  psop->close(true);  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/************************************************************ * Read and print all tuples via primary ordered index scan * ************************************************************/static void do_indexScan(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Index *myPIndex=                  myDict->getIndex("PRIMARY", "api_recattr_vs_record");  std::cout << "Running do_indexScan\n";  if (myPIndex == NULL)    APIERROR(myDict->getNdbError());  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbIndexScanOperation *psop;  /* RecAttrs for NdbRecAttr Api */  NdbRecAttr *recAttrAttr1;  NdbRecAttr *recAttrAttr2;  NdbRecAttr *recAttrAttr3;  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbIndexScanOperation(myPIndex);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      /* Multi read range is not supported for the NdbRecAttr scan       * API, so we just read one range.       */      Uint32 scanFlags=        NdbScanOperation::SF_OrderBy |        NdbScanOperation::SF_MultiRange |        NdbScanOperation::SF_ReadRangeNo;      if (psop->readTuples(NdbOperation::LM_Read,                           scanFlags,                           (Uint32) 0,          // batch                           (Uint32) 0) != 0)    // parallel        APIERROR (myTransaction->getNdbError());      /* Add a bound       * Tuples where ATTR1 >=2 and < 4       * 2,[3 deleted]       */      Uint32 low=2;      Uint32 high=4;      if (psop->setBound("ATTR1",                        NdbIndexScanOperation::BoundLE, (char*)&low))        APIERROR(myTransaction->getNdbError());      if (psop->setBound("ATTR1",                        NdbIndexScanOperation::BoundGT, (char*)&high))        APIERROR(myTransaction->getNdbError());      if (psop->end_of_bound(0))        APIERROR(psop->getNdbError());      /* Second bound       * Tuples where ATTR1 > 5 and <=9       * 6,7,8,9       */      low=5;      high=9;      if (psop->setBound("ATTR1",                        NdbIndexScanOperation::BoundLT, (char*)&low))        APIERROR(myTransaction->getNdbError());      if (psop->setBound("ATTR1",                        NdbIndexScanOperation::BoundGE, (char*)&high))        APIERROR(myTransaction->getNdbError());      if (psop->end_of_bound(1))        APIERROR(psop->getNdbError());      /* Read all columns */      recAttrAttr1=psop->getValue("ATTR1");      recAttrAttr2=psop->getValue("ATTR2");      recAttrAttr3=psop->getValue("ATTR3");      break;    }    case api_record :    {      /* NdbRecord supports scanning multiple ranges using a       * single index scan operation       */      Uint32 scanFlags =        NdbScanOperation::SF_OrderBy |        NdbScanOperation::SF_MultiRange |        NdbScanOperation::SF_ReadRangeNo;      NdbScanOperation::ScanOptions options;      options.optionsPresent=NdbScanOperation::ScanOptions::SO_SCANFLAGS;      options.scan_flags=scanFlags;      psop=myTransaction->scanIndex(pkeyIndexRecord,                                    pallColsRecord,                                    NdbOperation::LM_Read,                                    NULL, // no mask; read all columns                                          // in result record                                    NULL, // bound defined later                                    &options,                                    sizeof(NdbScanOperation::ScanOptions));      if (psop == NULL) APIERROR(myTransaction->getNdbError());      /* Add a bound       * Tuples where ATTR1 >=2 and < 4       * 2,[3 deleted]       */      Uint32 low=2;      Uint32 high=4;      NdbIndexScanOperation::IndexBound bound;      bound.low_key=(char*)&low;      bound.low_key_count=1;      bound.low_inclusive=true;      bound.high_key=(char*)&high;      bound.high_key_count=1;      bound.high_inclusive=false;      bound.range_no=0;      if (psop->setBound(pkeyIndexRecord, bound))        APIERROR(myTransaction->getNdbError());      /* Second bound       * Tuples where ATTR1 > 5 and <=9       * 6,7,8,9       */      low=5;      high=9;      bound.low_key=(char*)&low;      bound.low_key_count=1;      bound.low_inclusive=false;      bound.high_key=(char*)&high;      bound.high_key_count=1;      bound.high_inclusive=true;      bound.range_no=1;      if (psop->setBound(pkeyIndexRecord, bound))        APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  if (myTransaction->getNdbError().code != 0)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      while (psop->nextResult(true) == 0)      {        printf(" %2d    %2d    %2d    Range no : %2d\n",               recAttrAttr1->u_32_value(),               recAttrAttr2->u_32_value(),               recAttrAttr3->u_32_value(),               psop->get_range_no());      }      psop->close();      break;    }    case api_record :    {      RowData *prowData; // Ptr to point to our data      int rc=0;      while ((rc = psop->nextResult((const char**) &prowData,                                    true,                                    false)) == 0)      {        // printf(" PTR : %d\n", (int) prowData);        printf(" %2d    %2d    %2d    Range no : %2d\n",               prowData->attr1,               prowData->attr2,               prowData->attr3,               psop->get_range_no());      }      if (rc != 1)  APIERROR(myTransaction->getNdbError());      psop->close(true);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/************************************************************************* * Read and print all tuples via index scan using mixed NdbRecord access * *************************************************************************/static void do_mixed_indexScan(Ndb &myNdb){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Index *myPIndex=                    myDict->getIndex("PRIMARY", "api_recattr_vs_record");  std::cout << "Running do_mixed_indexScan\n";  if (myPIndex == NULL)    APIERROR(myDict->getNdbError());  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbIndexScanOperation *psop;  NdbRecAttr *recAttrAttr3;  Uint32 scanFlags =    NdbScanOperation::SF_OrderBy |    NdbScanOperation::SF_MultiRange |    NdbScanOperation::SF_ReadRangeNo;  /* We'll get Attr3 via ScanOptions */  unsigned char attrMask=((1<<attr1ColNum) | (1<<attr2ColNum));  NdbOperation::GetValueSpec extraGets[1];  extraGets[0].column= pattr3Col;  extraGets[0].appStorage= NULL;  extraGets[0].recAttr= NULL;  NdbScanOperation::ScanOptions options;  options.optionsPresent=    NdbScanOperation::ScanOptions::SO_SCANFLAGS |    NdbScanOperation::ScanOptions::SO_GETVALUE;  options.scan_flags= scanFlags;  options.extraGetValues= &extraGets[0];  options.numExtraGetValues= 1;  psop=myTransaction->scanIndex(pkeyIndexRecord,                                pallColsRecord,                                NdbOperation::LM_Read,                                &attrMask, // mask                                NULL, // bound defined below                                &options,                                sizeof(NdbScanOperation::ScanOptions));  if (psop == NULL) APIERROR(myTransaction->getNdbError());  /* Grab RecAttr now */  recAttrAttr3= extraGets[0].recAttr;  /* Add a bound   * ATTR1 >= 2, < 4   * 2,[3 deleted]   */  Uint32 low=2;  Uint32 high=4;  NdbIndexScanOperation::IndexBound bound;  bound.low_key=(char*)&low;  bound.low_key_count=1;  bound.low_inclusive=true;  bound.high_key=(char*)&high;  bound.high_key_count=1;  bound.high_inclusive=false;  bound.range_no=0;  if (psop->setBound(pkeyIndexRecord, bound))    APIERROR(myTransaction->getNdbError());  /* Second bound   * ATTR1 > 5, <= 9   * 6,7,8,9   */  low=5;  high=9;  bound.low_key=(char*)&low;  bound.low_key_count=1;  bound.low_inclusive=false;  bound.high_key=(char*)&high;  bound.high_key_count=1;  bound.high_inclusive=true;  bound.range_no=1;  if (psop->setBound(pkeyIndexRecord, bound))    APIERROR(myTransaction->getNdbError());  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  RowData *prowData; // Ptr to point to our data  int rc=0;  while ((rc = psop->nextResult((const char**) &prowData,                                true,                                false)) == 0)  {    printf(" %2d    %2d    %2d    Range no : %2d\n",           prowData->attr1,           prowData->attr2,           recAttrAttr3->u_32_value(),           psop->get_range_no());  }  if (rc != 1)  APIERROR(myTransaction->getNdbError());  psop->close(true);  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/******************************************************** * Read + Delete one tuple (the one with primary key 8) * ********************************************************/static void do_read_and_delete(Ndb &myNdb){  /* This procedure performs a single operation, single round   * trip read and then delete of a tuple, specified by   * primary key   */  std::cout << "Running do_read_and_delete (NdbRecord only)\n";  NdbTransaction *myTransaction= myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  RowData row;  row.attr1=8;  row.attr2=0; // Don't care  row.attr3=0; // Don't care  /* We'll also read some extra columns while we're   * reading + deleting   */  NdbOperation::OperationOptions options;  NdbOperation::GetValueSpec extraGets[2];  extraGets[0].column = pattr3Col;  extraGets[0].appStorage = NULL;  extraGets[0].recAttr = NULL;  extraGets[1].column = NdbDictionary::Column::COMMIT_COUNT;  extraGets[1].appStorage = NULL;  extraGets[1].recAttr = NULL;  options.optionsPresent= NdbOperation::OperationOptions::OO_GETVALUE;  options.extraGetValues= &extraGets[0];  options.numExtraGetValues= 2;  unsigned char attrMask = (1<<attr2ColNum); // Only read Col2 into row  const NdbOperation *pop=    myTransaction->deleteTuple(pkeyColumnRecord, // Spec of key used                               (char*) &row, // Key information                               pallColsRecord, // Spec of columns to read                               (char*) &row, // Row to read values into                               &attrMask, // Cols to read as part of delete                               &options,                               sizeof(NdbOperation::OperationOptions));  if (pop==NULL) APIERROR(myTransaction->getNdbError());  if (myTransaction->execute(NdbTransaction::Commit) == -1)    APIERROR(myTransaction->getNdbError());  std::cout << "ATTR1 ATTR2 ATTR3 COMMITS" << std::endl;  printf(" %2d    %2d    %2d    %2d\n",         row.attr1,         row.attr2,         extraGets[0].recAttr->u_32_value(),         extraGets[1].recAttr->u_32_value());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/* Some handy consts for scan control */static const int GOT_ROW= 0;static const int NO_MORE_ROWS= 1;static const int NEED_TO_FETCH_ROWS= 2;/********************************************* * Read and update all tuples via table scan * *********************************************/static void do_scan_update(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                              myDict->getTable("api_recattr_vs_record");  if (myTable == NULL)    APIERROR(myDict->getNdbError());  std::cout << "Running do_scan_update\n";  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbScanOperation *psop;  NdbRecAttr *recAttrAttr1;  NdbRecAttr *recAttrAttr2;  NdbRecAttr *recAttrAttr3;  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbScanOperation(myTable);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      /* When we want to operate on the tuples returned from a       * scan, we need to request the tuple's keyinfo is       * returned, with SF_KeyInfo       */      if (psop->readTuples(NdbOperation::LM_Read,                           NdbScanOperation::SF_KeyInfo) != 0)        APIERROR (myTransaction->getNdbError());      recAttrAttr1=psop->getValue("ATTR1");      recAttrAttr2=psop->getValue("ATTR2");      recAttrAttr3=psop->getValue("ATTR3");      break;    }    case api_record :    {      NdbScanOperation::ScanOptions options;      options.optionsPresent= NdbScanOperation::ScanOptions::SO_SCANFLAGS;      options.scan_flags= NdbScanOperation::SF_KeyInfo;      psop=myTransaction->scanTable(pallColsRecord,                                    NdbOperation::LM_Read,                                    NULL,  // mask - read all columns                                    &options,                                    sizeof(NdbScanOperation::ScanOptions));      if (psop == NULL) APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      int result= NEED_TO_FETCH_ROWS;      Uint32 processed= 0;      while (result == NEED_TO_FETCH_ROWS)      {        bool fetch=true;        while ((result = psop->nextResult(fetch)) == GOT_ROW)        {          fetch= false;          Uint32 col2Value=recAttrAttr2->u_32_value();          NdbOperation *op=psop->updateCurrentTuple();          if (op==NULL)            APIERROR(myTransaction->getNdbError());          op->setValue("ATTR2", (10*col2Value));          processed++;        }        if (result < 0)          APIERROR(myTransaction->getNdbError());        if (processed !=0)        {          // Need to execute          if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)            APIERROR(myTransaction->getNdbError());          processed=0;        }      }      psop->close();      break;    }    case api_record :    {      RowData *prowData; // Ptr to point to our data      int result= NEED_TO_FETCH_ROWS;      Uint32 processed=0;      while (result == NEED_TO_FETCH_ROWS)      {        bool fetch= true;        while ((result = psop->nextResult((const char**) &prowData,                                          fetch, false)) == GOT_ROW)        {          fetch= false;          /* Copy row into a stack variable */          RowData r= *prowData;          /* Modify attr2 */          r.attr2*= 10;          /* Update it */          const NdbOperation *op = psop->updateCurrentTuple(myTransaction,                                                            pallColsRecord,                                                            (char*) &r);          if (op==NULL)            APIERROR(myTransaction->getNdbError());          processed ++;        }        if (result < 0)          APIERROR(myTransaction->getNdbError());        if (processed !=0)        {          /* To get here, there are no more cached scan results,           * and some row updates that we've not sent yet.           * Send them before we try to get another batch, or           * finish.           */          if (myTransaction->execute( NdbTransaction::NoCommit ) != 0)            APIERROR(myTransaction->getNdbError());          processed=0;        }      }      psop->close(true);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/************************************************** * Read all and delete some tuples via table scan * **************************************************/static void do_scan_delete(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                              myDict->getTable("api_recattr_vs_record");  if (myTable == NULL)    APIERROR(myDict->getNdbError());  std::cout << "Running do_scan_delete\n";  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbScanOperation *psop;  NdbRecAttr *recAttrAttr1;  /* Scan, retrieving first column.   * Delete particular records, based on first column   * Read third column as part of delete   */  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbScanOperation(myTable);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      /* Need KeyInfo when performing scanning delete */      if (psop->readTuples(NdbOperation::LM_Read,                           NdbScanOperation::SF_KeyInfo) != 0)        APIERROR (myTransaction->getNdbError());      recAttrAttr1=psop->getValue("ATTR1");      break;    }    case api_record :    {      NdbScanOperation::ScanOptions options;      options.optionsPresent=NdbScanOperation::ScanOptions::SO_SCANFLAGS;      /* Need KeyInfo when performing scanning delete */      options.scan_flags=NdbScanOperation::SF_KeyInfo;      psop=myTransaction->scanTable(pkeyColumnRecord,                                    NdbOperation::LM_Read,                                    NULL,  // mask                                    &options,                                    sizeof(NdbScanOperation::ScanOptions));      if (psop == NULL) APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      int result= NEED_TO_FETCH_ROWS;      Uint32 processed=0;      while (result == NEED_TO_FETCH_ROWS)      {        bool fetch=true;        while ((result = psop->nextResult(fetch)) == GOT_ROW)        {          fetch= false;          Uint32 col1Value=recAttrAttr1->u_32_value();          if (col1Value == 2)          {            /* Note : We cannot do a delete pre-read via             * the NdbRecAttr interface.  We can only             * delete here.             */            if (psop->deleteCurrentTuple())              APIERROR(myTransaction->getNdbError());            processed++;          }        }        if (result < 0)          APIERROR(myTransaction->getNdbError());        if (processed !=0)        {          /* To get here, there are no more cached scan results,           * and some row deletes that we've not sent yet.           * Send them before we try to get another batch, or           * finish.           */          if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)            APIERROR(myTransaction->getNdbError());          processed=0;        }      }      psop->close();      break;    }    case api_record :    {      RowData *prowData; // Ptr to point to our data      int result= NEED_TO_FETCH_ROWS;      Uint32 processed=0;      while (result == NEED_TO_FETCH_ROWS)      {        bool fetch=true;        const NdbOperation* theDeleteOp;        RowData readRow;        NdbRecAttr* attr3;        NdbRecAttr* commitCount;        while ((result = psop->nextResult((const char**) &prowData,                                          fetch,                                          false)) == GOT_ROW)        {          fetch = false;          /* Copy latest row to a stack local */          RowData r;          r= *prowData;          if (r.attr1 == 2)          {            /* We're going to perform a read+delete on this             * row.  We'll read attr1 and attr2 vian NDBRecord             * and Attr3 and the commit count via extra             * get values.             */            NdbOperation::OperationOptions options;            NdbOperation::GetValueSpec extraGets[2];            extraGets[0].column = pattr3Col;            extraGets[0].appStorage = NULL;            extraGets[0].recAttr = NULL;            extraGets[1].column = NdbDictionary::Column::COMMIT_COUNT;            extraGets[1].appStorage = NULL;            extraGets[1].recAttr = NULL;            options.optionsPresent= NdbOperation::OperationOptions::OO_GETVALUE;            options.extraGetValues= &extraGets[0];            options.numExtraGetValues= 2;            // Read cols 1 + 2 vian NDBRecord            unsigned char attrMask =                    (1<<attr1ColNum) | (1<<attr2ColNum);            theDeleteOp =              psop->deleteCurrentTuple(myTransaction,                                          pallColsRecord,                                          (char*) &readRow,                                          &attrMask,                                          &options,                                          sizeof(NdbOperation::OperationOptions)                                         );            if (theDeleteOp==NULL)              APIERROR(myTransaction->getNdbError());            /* Store extra Get RecAttrs */            attr3= extraGets[0].recAttr;            commitCount= extraGets[1].recAttr;            processed ++;          }        }        if (result < 0)          APIERROR(myTransaction->getNdbError());        if (processed !=0)        {          /* To get here, there are no more cached scan results,           * and some row deletes that we've not sent yet.           * Send them before we try to get another batch, or           * finish.           */          if (myTransaction->execute( NdbTransaction::NoCommit ) != 0)            APIERROR(myTransaction->getNdbError());          processed=0;          // Let's look at the data just read          printf("Deleted data\n");          printf("ATTR1  ATTR2  ATTR3 COMMITS\n");          printf("  %2d    %2d    %2d    %2d\n",                 readRow.attr1,                 readRow.attr2,                 attr3->u_32_value(),                 commitCount->u_32_value());        }      }      psop->close(true);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/*********************************************************** * Read all tuples via scan, reread one with lock takeover * ***********************************************************/static void do_scan_lock_reread(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                              myDict->getTable("api_recattr_vs_record");  if (myTable == NULL)    APIERROR(myDict->getNdbError());  std::cout << "Running do_scan_lock_reread\n";  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbScanOperation *psop;  NdbRecAttr *recAttrAttr1;  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbScanOperation(myTable);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      /* Need KeyInfo for lock takeover */      if (psop->readTuples(NdbOperation::LM_Read,                           NdbScanOperation::SF_KeyInfo) != 0)        APIERROR (myTransaction->getNdbError());      recAttrAttr1=psop->getValue("ATTR1");      break;    }    case api_record :    {      NdbScanOperation::ScanOptions options;      options.optionsPresent= NdbScanOperation::ScanOptions::SO_SCANFLAGS;      /* Need KeyInfo for lock takeover */      options.scan_flags= NdbScanOperation::SF_KeyInfo;      psop=myTransaction->scanTable(pkeyColumnRecord,                                    NdbOperation::LM_Read,                                    NULL,  // mask                                    &options,                                    sizeof(NdbScanOperation::ScanOptions));      if (psop == NULL) APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      int result= NEED_TO_FETCH_ROWS;      Uint32 processed=0;      NdbRecAttr *attr1, *attr2, *attr3, *commitCount;      while (result == NEED_TO_FETCH_ROWS)      {        bool fetch=true;        while ((result = psop->nextResult(fetch)) == GOT_ROW)        {          fetch= false;          Uint32 col1Value=recAttrAttr1->u_32_value();          if (col1Value == 9)          {            /* Let's read the rest of the info for it with             * a separate operation             */            NdbOperation *op= psop->lockCurrentTuple();            if (op==NULL)              APIERROR(myTransaction->getNdbError());            attr1=op->getValue("ATTR1");            attr2=op->getValue("ATTR2");            attr3=op->getValue("ATTR3");            commitCount=op->getValue(NdbDictionary::Column::COMMIT_COUNT);            processed++;          }        }        if (result < 0)          APIERROR(myTransaction->getNdbError());        if (processed !=0)        {          // Need to execute          if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)            APIERROR(myTransaction->getNdbError());          processed=0;          // Let's look at the whole row...          printf("Locked and re-read data:\n");          printf("ATTR1  ATTR2  ATTR3 COMMITS\n");          printf("  %2d    %2d    %2d    %2d\n",                 attr1->u_32_value(),                 attr2->u_32_value(),                 attr3->u_32_value(),                 commitCount->u_32_value());        }      }      psop->close();      break;    }    case api_record :    {      RowData *prowData; // Ptr to point to our data      int result= NEED_TO_FETCH_ROWS;      Uint32 processed=0;      RowData rereadData;      NdbRecAttr *attr3, *commitCount;      while (result == NEED_TO_FETCH_ROWS)      {        bool fetch=true;        while ((result = psop->nextResult((const char**) &prowData,                                          fetch,                                          false)) == GOT_ROW)        {          fetch = false;          /* Copy row to stack local */          RowData r;          r=*prowData;          if (r.attr1 == 9)          {            /* Perform extra read of this row via lockCurrentTuple             * Read all columns using NdbRecord for attr1 + attr2,             * and extra get values for attr3 and the commit count             */            NdbOperation::OperationOptions options;            NdbOperation::GetValueSpec extraGets[2];            extraGets[0].column = pattr3Col;            extraGets[0].appStorage = NULL;            extraGets[0].recAttr = NULL;            extraGets[1].column = NdbDictionary::Column::COMMIT_COUNT;            extraGets[1].appStorage = NULL;            extraGets[1].recAttr = NULL;            options.optionsPresent=NdbOperation::OperationOptions::OO_GETVALUE;            options.extraGetValues=&extraGets[0];            options.numExtraGetValues=2;            // Read cols 1 + 2 vian NDBRecord            unsigned char attrMask =                        (1<<attr1ColNum) | (1<<attr2ColNum);            const NdbOperation *lockOp =                psop->lockCurrentTuple(myTransaction,                                          pallColsRecord,                                          (char *) &rereadData,                                          &attrMask,                                          &options,                                          sizeof(NdbOperation::OperationOptions)                                         );            if (lockOp == NULL)              APIERROR(myTransaction->getNdbError());            attr3= extraGets[0].recAttr;            commitCount= extraGets[1].recAttr;            processed++;          }        }        if (result < 0)          APIERROR(myTransaction->getNdbError());        if (processed !=0)        {          // Need to execute          if (myTransaction->execute( NdbTransaction::NoCommit ) != 0)            APIERROR(myTransaction->getNdbError());          processed=0;          // Let's look at the whole row...          printf("Locked and re-read data:\n");          printf("ATTR1  ATTR2  ATTR3 COMMITS\n");          printf("  %2d    %2d    %2d    %2d\n",                 rereadData.attr1,                 rereadData.attr2,                 attr3->u_32_value(),                 commitCount->u_32_value());        }      }      psop->close(true);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/*************************************************************** * Read all tuples via primary key, using only extra getValues * ***************************************************************/static void do_all_extras_read(Ndb &myNdb){  std::cout << "Running do_all_extras_read(NdbRecord only)\n";  std::cout << "ATTR1 ATTR2 ATTR3 COMMIT_COUNT" << std::endl;  for (int i = 0; i < 10; i++) {    NdbTransaction *myTransaction= myNdb.startTransaction();    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());    RowData rowData;    NdbRecAttr *myRecAttr1, *myRecAttr2, *myRecAttr3, *myRecAttrCC;    /* We read nothing vian NDBRecord, and everything via     * 'extra' reads     */    NdbOperation::GetValueSpec extraCols[4];    extraCols[0].column=pattr1Col;    extraCols[0].appStorage=NULL;    extraCols[0].recAttr=NULL;    extraCols[1].column=pattr2Col;    extraCols[1].appStorage=NULL;    extraCols[1].recAttr=NULL;    extraCols[2].column=pattr3Col;    extraCols[2].appStorage=NULL;    extraCols[2].recAttr=NULL;    extraCols[3].column=NdbDictionary::Column::COMMIT_COUNT;    extraCols[3].appStorage=NULL;    extraCols[3].recAttr=NULL;    NdbOperation::OperationOptions opts;    opts.optionsPresent = NdbOperation::OperationOptions::OO_GETVALUE;    opts.extraGetValues=&extraCols[0];    opts.numExtraGetValues=4;    unsigned char attrMask= 0; // No row results required.    // Set PK search criteria    rowData.attr1= i;    const NdbOperation *pop=      myTransaction->readTuple(pkeyColumnRecord,                               (char*) &rowData,                               pkeyColumnRecord,                               NULL, // null result row                               NdbOperation::LM_Read,                               &attrMask,                               &opts);    if (pop==NULL) APIERROR(myTransaction->getNdbError());    myRecAttr1=extraCols[0].recAttr;    myRecAttr2=extraCols[1].recAttr;    myRecAttr3=extraCols[2].recAttr;    myRecAttrCC=extraCols[3].recAttr;    if (myRecAttr1 == NULL) APIERROR(myTransaction->getNdbError());    if (myRecAttr2 == NULL) APIERROR(myTransaction->getNdbError());    if (myRecAttr3 == NULL) APIERROR(myTransaction->getNdbError());    if (myRecAttrCC == NULL) APIERROR(myTransaction->getNdbError());    if(myTransaction->execute( NdbTransaction::Commit ) == -1)      APIERROR(myTransaction->getNdbError());    bool deleted= (myTransaction->getNdbError().classification ==                   NdbError::NoDataFound);    if (deleted)      printf("Detected that deleted tuple %d doesn't exist!\n", i);    else    {      printf(" %2d    %2d    %2d    %d\n",             myRecAttr1->u_32_value(),             myRecAttr2->u_32_value(),             myRecAttr3->u_32_value(),             myRecAttrCC->u_32_value()             );    }    myNdb.closeTransaction(myTransaction);  }  std::cout << "-------\n";}/****************************************************************** * Read and print some tuples via bounded scan of secondary index * ******************************************************************/static void do_secondary_indexScan(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Index *mySIndex=                myDict->getIndex("MYINDEXNAME", "api_recattr_vs_record");  std::cout << "Running do_secondary_indexScan\n";  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbIndexScanOperation *psop;  NdbRecAttr *recAttrAttr1;  NdbRecAttr *recAttrAttr2;  NdbRecAttr *recAttrAttr3;  Uint32 scanFlags =    NdbScanOperation::SF_OrderBy |    NdbScanOperation::SF_Descending |    NdbScanOperation::SF_MultiRange |    NdbScanOperation::SF_ReadRangeNo;  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbIndexScanOperation(mySIndex);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      if (psop->readTuples(NdbOperation::LM_Read,                           scanFlags,                           (Uint32) 0,          // batch                           (Uint32) 0) != 0)    // parallel        APIERROR (myTransaction->getNdbError());      /* Bounds :       * > ATTR3=6       * < ATTR3=42       */      Uint32 low=6;      Uint32 high=42;      if (psop->setBound("ATTR3",                        NdbIndexScanOperation::BoundLT, (char*)&low))        APIERROR(psop->getNdbError());      if (psop->setBound("ATTR3",                        NdbIndexScanOperation::BoundGT, (char*)&high))        APIERROR(psop->getNdbError());      recAttrAttr1=psop->getValue("ATTR1");      recAttrAttr2=psop->getValue("ATTR2");      recAttrAttr3=psop->getValue("ATTR3");      break;    }    case api_record :    {      NdbScanOperation::ScanOptions options;      options.optionsPresent=NdbScanOperation::ScanOptions::SO_SCANFLAGS;      options.scan_flags=scanFlags;      psop=myTransaction->scanIndex(psecondaryIndexRecord,                                    pallColsRecord,                                    NdbOperation::LM_Read,                                    NULL, // mask                                    NULL, // bound                                    &options,                                    sizeof(NdbScanOperation::ScanOptions));      if (psop == NULL) APIERROR(myTransaction->getNdbError());      /* Bounds :       * > ATTR3=6       * < ATTR3=42       */      Uint32 low=6;      Uint32 high=42;      NdbIndexScanOperation::IndexBound bound;      bound.low_key=(char*)&low;      bound.low_key_count=1;      bound.low_inclusive=false;      bound.high_key=(char*)&high;      bound.high_key_count=1;      bound.high_inclusive=false;      bound.range_no=0;      if (psop->setBound(psecondaryIndexRecord, bound))        APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  // Check rc anyway  if (myTransaction->getNdbError().status != NdbError::Success)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      while (psop->nextResult(true) == 0)      {        printf(" %2d    %2d    %2d    Range no : %2d\n",               recAttrAttr1->u_32_value(),               recAttrAttr2->u_32_value(),               recAttrAttr3->u_32_value(),               psop->get_range_no());      }      psop->close();      break;    }    case api_record :    {      RowData *prowData; // Ptr to point to our data      int rc=0;      while ((rc = psop->nextResult((const char**) &prowData,                                    true,                                    false)) == 0)      {        // printf(" PTR : %d\n", (int) prowData);        printf(" %2d    %2d    %2d    Range no : %2d\n",               prowData->attr1,               prowData->attr2,               prowData->attr3,               psop->get_range_no());      }      if (rc != 1)  APIERROR(myTransaction->getNdbError());      psop->close(true);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/*********************************************************************** * Index scan to read tuples from secondary index using equality bound * ***********************************************************************/static void do_secondary_indexScanEqual(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Index *mySIndex=                myDict->getIndex("MYINDEXNAME", "api_recattr_vs_record");  std::cout << "Running do_secondary_indexScanEqual\n";  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbIndexScanOperation *psop;  NdbRecAttr *recAttrAttr1;  NdbRecAttr *recAttrAttr2;  NdbRecAttr *recAttrAttr3;  Uint32 scanFlags = NdbScanOperation::SF_OrderBy;  Uint32 attr3Eq= 44;  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbIndexScanOperation(mySIndex);      if (psop == NULL) APIERROR(myTransaction->getNdbError());      if (psop->readTuples(NdbOperation::LM_Read,                           scanFlags,                           (Uint32) 0,          // batch                           (Uint32) 0) != 0)    // parallel        APIERROR (myTransaction->getNdbError());      if (psop->setBound("ATTR3",                        NdbIndexScanOperation::BoundEQ, (char*)&attr3Eq))        APIERROR(myTransaction->getNdbError());      recAttrAttr1=psop->getValue("ATTR1");      recAttrAttr2=psop->getValue("ATTR2");      recAttrAttr3=psop->getValue("ATTR3");      break;    }    case api_record :    {      NdbScanOperation::ScanOptions options;      options.optionsPresent= NdbScanOperation::ScanOptions::SO_SCANFLAGS;      options.scan_flags=scanFlags;      psop=myTransaction->scanIndex(psecondaryIndexRecord,                                    pallColsRecord, // Read all table rows back                                    NdbOperation::LM_Read,                                    NULL, // mask                                    NULL, // bound specified below                                    &options,                                    sizeof(NdbScanOperation::ScanOptions));      if (psop == NULL) APIERROR(myTransaction->getNdbError());      /* Set equality bound via two inclusive bounds */      NdbIndexScanOperation::IndexBound bound;      bound.low_key= (char*)&attr3Eq;      bound.low_key_count= 1;      bound.low_inclusive= true;      bound.high_key= (char*)&attr3Eq;      bound.high_key_count= 1;      bound.high_inclusive= true;      bound.range_no= 0;      if (psop->setBound(psecondaryIndexRecord, bound))        APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  // Check rc anyway  if (myTransaction->getNdbError().status != NdbError::Success)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      int res;      while ((res= psop->nextResult(true)) == GOT_ROW)      {        printf(" %2d    %2d    %2d\n",               recAttrAttr1->u_32_value(),               recAttrAttr2->u_32_value(),               recAttrAttr3->u_32_value());      }      if (res != NO_MORE_ROWS)        APIERROR(psop->getNdbError());      psop->close();      break;    }    case api_record :    {      RowData *prowData; // Ptr to point to our data      int rc=0;      while ((rc = psop->nextResult((const char**) &prowData,                                    true,   // fetch                                    false)) // forceSend             == GOT_ROW)      {        printf(" %2d    %2d    %2d\n",               prowData->attr1,               prowData->attr2,               prowData->attr3);      }      if (rc != NO_MORE_ROWS)        APIERROR(myTransaction->getNdbError());      psop->close(true);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/********************** * Interpreted update * **********************/static void do_interpreted_update(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                              myDict->getTable("api_recattr_vs_record");  const NdbDictionary::Index *myPIndex=                       myDict->getIndex("PRIMARY", "api_recattr_vs_record");  std::cout << "Running do_interpreted_update\n";  if (myTable == NULL)    APIERROR(myDict->getNdbError());  if (myPIndex == NULL)    APIERROR(myDict->getNdbError());  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbRecAttr *recAttrAttr1;  NdbRecAttr *recAttrAttr2;  NdbRecAttr *recAttrAttr3;  NdbRecAttr *recAttrAttr11;  NdbRecAttr *recAttrAttr12;  NdbRecAttr *recAttrAttr13;  RowData rowData;  RowData rowData2;  /* Register aliases */  const Uint32 R1=1, R2=2, R3=3, R4=4, R5=5, R6=6;  switch (accessType)  {    case api_attr :    {      NdbOperation *pop;      pop=myTransaction->getNdbOperation(myTable);      if (pop == NULL) APIERROR(myTransaction->getNdbError());      if (pop->interpretedUpdateTuple())        APIERROR (pop->getNdbError());      /* Interpreted update on row where ATTR1 == 4 */      if (pop->equal("ATTR1", 4) != 0)        APIERROR (pop->getNdbError());      /* First, read the values of all attributes in the normal way */      recAttrAttr1=pop->getValue("ATTR1");      recAttrAttr2=pop->getValue("ATTR2");      recAttrAttr3=pop->getValue("ATTR3");      /* Now define interpreted program which will run after the       * values have been read       * This program is rather tortuous and doesn't achieve much other       * than demonstrating control flow, register and some column       * operations       */      // R5= 3      if (pop->load_const_u32(R5, 3) != 0)        APIERROR (pop->getNdbError());      // R1= *ATTR1; R2= *ATTR2; R3= *ATTR3      if (pop->read_attr("ATTR1", R1) != 0)        APIERROR (pop->getNdbError());      if (pop->read_attr("ATTR2", R2) != 0)        APIERROR (pop->getNdbError());      if (pop->read_attr("ATTR3", R3) != 0)        APIERROR (pop->getNdbError());      // R3= R3-R5      if (pop->sub_reg(R3, R5, R3) != 0)        APIERROR (pop->getNdbError());      // R2= R1+R2      if (pop->add_reg(R1, R2, R2) != 0)        APIERROR (pop->getNdbError());      // *ATTR2= R2      if (pop->write_attr("ATTR2", R2) != 0)        APIERROR (pop->getNdbError());      // *ATTR3= R3      if (pop->write_attr("ATTR3", R3) != 0)        APIERROR (pop->getNdbError());      // *ATTR3 = *ATTR3 - 30      if (pop->subValue("ATTR3", (Uint32)30) != 0)        APIERROR (pop->getNdbError());      Uint32 comparisonValue= 10;      // if *ATTR3 > comparisonValue, goto Label 0      if (pop->branch_col_lt(pattr3Col->getColumnNo(),                             &comparisonValue,                             sizeof(Uint32),                             false,                             0) != 0)        APIERROR (pop->getNdbError());      // assert(false)      // Fail the operation with error 627 if we get here.      if (pop->interpret_exit_nok(627) != 0)        APIERROR (pop->getNdbError());      // Label 0      if (pop->def_label(0) != 0)        APIERROR (pop->getNdbError());      Uint32 comparisonValue2= 344;      // if *ATTR2 == comparisonValue, goto Label 1      if (pop->branch_col_eq(pattr2Col->getColumnNo(),                             &comparisonValue2,                             sizeof(Uint32),                             false,                             1) != 0)        APIERROR (pop->getNdbError());      // assert(false)      // Fail the operation with error 628 if we get here      if (pop->interpret_exit_nok(628) != 0)        APIERROR (pop->getNdbError());      // Label 1      if (pop->def_label(1) != 1)        APIERROR (pop->getNdbError());      // Optional infinite loop      //if (pop->branch_label(0) != 0)      //  APIERROR (pop->getNdbError());      // R1 = 10      if (pop->load_const_u32(R1, 10) != 0)        APIERROR (pop->getNdbError());      // R3 = 2      if (pop->load_const_u32(R3, 2) != 0)        APIERROR (pop->getNdbError());      // Now call subroutine 0      if (pop->call_sub(0) != 0)        APIERROR (pop->getNdbError());      // *ATTR2= R2      if (pop->write_attr("ATTR2", R2) != 0)        APIERROR (pop->getNdbError());      // Return ok, we'll move onto an update.      if (pop->interpret_exit_ok() != 0)        APIERROR (pop->getNdbError());      /* Define a final read of the columns after the update */      recAttrAttr11= pop->getValue("ATTR1");      recAttrAttr12= pop->getValue("ATTR2");      recAttrAttr13= pop->getValue("ATTR3");      // Define any subroutines called by the 'main' program      // Subroutine 0      if (pop->def_subroutine(0) != 0)        APIERROR (pop->getNdbError());      // R4= 1      if (pop->load_const_u32(R4, 1) != 0)        APIERROR (pop->getNdbError());      // Label 2      if (pop->def_label(2) != 2)        APIERROR (pop->getNdbError());      // R3= R3-R4      if (pop->sub_reg(R3, R4, R3) != 0)        APIERROR (pop->getNdbError());      // R2= R2 + R1      if (pop->add_reg(R2, R1, R2) != 0)        APIERROR (pop->getNdbError());      // Optional infinite loop      // if (pop->branch_label(2) != 0)      //  APIERROR (pop->getNdbError());      // Loop, subtracting 1 from R4 until R4 < 1      if (pop->branch_ge(R4, R3, 2) != 0)        APIERROR (pop->getNdbError());      // Jump to label 3      if (pop->branch_label(3) != 0)        APIERROR (pop->getNdbError());      // assert(false)      // Fail operation with error 629      if (pop->interpret_exit_nok(629) != 0)        APIERROR (pop->getNdbError());      // Label 3      if (pop->def_label(3) != 3)        APIERROR (pop->getNdbError());      // Nested subroutine call to sub 2      if (pop->call_sub(2) != 0)        APIERROR (pop->getNdbError());      // Return from subroutine 0      if (pop->ret_sub() !=0)        APIERROR (pop->getNdbError());      // Subroutine 1      if (pop->def_subroutine(1) != 1)        APIERROR (pop->getNdbError());      // R6= R1+R2      if (pop->add_reg(R1, R2, R6) != 0)        APIERROR (pop->getNdbError());      // Return from subrouine 1      if (pop->ret_sub() !=0)        APIERROR (pop->getNdbError());      // Subroutine 2      if (pop->def_subroutine(2) != 2)        APIERROR (pop->getNdbError());      // Call backward to subroutine 1      if (pop->call_sub(1) != 0)        APIERROR (pop->getNdbError());      // Return from subroutine 2      if (pop->ret_sub() !=0)        APIERROR (pop->getNdbError());      break;    }    case api_record :    {      const NdbOperation *pop;      rowData.attr1= 4;      /* NdbRecord does not support an updateTuple pre-read or post-read, so       * we use separate operations for these.       * Note that this assumes that a operations are executed in       * the order they are defined by NDBAPI, which is not guaranteed.  To       * ensure execution order, the application should perform a NoCommit       * execute between operations.       */      const NdbOperation *op0= myTransaction->readTuple(pkeyColumnRecord,                                                        (char*) &rowData,                                                        pallColsRecord,                                                        (char*) &rowData);      if (op0 == NULL)        APIERROR (myTransaction->getNdbError());      /* Allocate some space to define an Interpreted program */      const Uint32 numWords= 64;      Uint32 space[numWords];      NdbInterpretedCode stackCode(myTable,                                   &space[0],                                   numWords);      NdbInterpretedCode *code= &stackCode;      /* Similar program as above, with tortuous control flow and little       * purpose.  Note that for NdbInterpretedCode, some instruction       * arguments are in different orders       */      // R5= 3      if (code->load_const_u32(R5, 3) != 0)        APIERROR(code->getNdbError());      // R1= *ATTR1; R2= *ATTR2; R3= *ATTR3      if (code->read_attr(R1, pattr1Col) != 0)        APIERROR (code->getNdbError());      if (code->read_attr(R2, pattr2Col) != 0)        APIERROR (code->getNdbError());      if (code->read_attr(R3, pattr3Col) != 0)        APIERROR (code->getNdbError());      // R3= R3-R5      if (code->sub_reg(R3, R3, R5) != 0)        APIERROR (code->getNdbError());      // R2= R1+R2      if (code->add_reg(R2, R1, R2) != 0)        APIERROR (code->getNdbError());      // *ATTR2= R2      if (code->write_attr(pattr2Col, R2) != 0)        APIERROR (code->getNdbError());      // *ATTR3= R3      if (code->write_attr(pattr3Col, R3) != 0)        APIERROR (code->getNdbError());      // *ATTR3 = *ATTR3 - 30      if (code->sub_val(pattr3Col->getColumnNo(), (Uint32)30) != 0)        APIERROR (code->getNdbError());      Uint32 comparisonValue= 10;      // if comparisonValue < *ATTR3, goto Label 0      if (code->branch_col_lt(&comparisonValue,                              sizeof(Uint32),                              pattr3Col->getColumnNo(),                              0) != 0)        APIERROR (code->getNdbError());      // assert(false)      // Fail operation with error 627      if (code->interpret_exit_nok(627) != 0)        APIERROR (code->getNdbError());      // Label 0      if (code->def_label(0) != 0)        APIERROR (code->getNdbError());      Uint32 comparisonValue2= 344;      // if *ATTR2 == comparisonValue, goto Label 1      if (code->branch_col_eq(&comparisonValue2,                              sizeof(Uint32),                              pattr2Col->getColumnNo(),                              1) != 0)        APIERROR (code->getNdbError());      // assert(false)      // Fail operation with error 628      if (code->interpret_exit_nok(628) != 0)        APIERROR (code->getNdbError());      // Label 1      if (code->def_label(1) != 0)        APIERROR (code->getNdbError());      // R1= 10      if (code->load_const_u32(R1, 10) != 0)        APIERROR (code->getNdbError());      // R3= 2      if (code->load_const_u32(R3, 2) != 0)        APIERROR (code->getNdbError());      // Call subroutine 0 to effect      // R2 = R2 + (R1*R3)      if (code->call_sub(0) != 0)        APIERROR (code->getNdbError());      // *ATTR2= R2      if (code->write_attr(pattr2Col, R2) != 0)        APIERROR (code->getNdbError());      // Return ok      if (code->interpret_exit_ok() != 0)        APIERROR (code->getNdbError());      // Subroutine 0      if (code->def_sub(0) != 0)        APIERROR (code->getNdbError());      // R4= 1      if (code->load_const_u32(R4, 1) != 0)        APIERROR (code->getNdbError());      // Label 2      if (code->def_label(2) != 0)        APIERROR (code->getNdbError());      // R3= R3-R4      if (code->sub_reg(R3, R3, R4) != 0)        APIERROR (code->getNdbError());      // R2= R2+R1      if (code->add_reg(R2, R2, R1) != 0)        APIERROR (code->getNdbError());      // Loop, subtracting 1 from R4 until R4>1      if (code->branch_ge(R3, R4, 2) != 0)        APIERROR (code->getNdbError());      // Jump to label 3      if (code->branch_label(3) != 0)        APIERROR (code->getNdbError());      // Fail operation with error 629      if (code->interpret_exit_nok(629) != 0)        APIERROR (code->getNdbError());      // Label 3      if (code->def_label(3) != 0)        APIERROR (code->getNdbError());      // Call sub 2      if (code->call_sub(2) != 0)        APIERROR (code->getNdbError());      // Return from sub 0      if (code->ret_sub() != 0)        APIERROR (code->getNdbError());      // Subroutine 1      if (code->def_sub(1) != 0)        APIERROR (code->getNdbError());      // R6= R1+R2      if (code->add_reg(R6, R1, R2) != 0)        APIERROR (code->getNdbError());      // Return from subroutine 1      if (code->ret_sub() !=0)        APIERROR (code->getNdbError());      // Subroutine 2      if (code->def_sub(2) != 0)        APIERROR (code->getNdbError());      // Call backward to subroutine 1      if (code->call_sub(1) != 0)        APIERROR (code->getNdbError());      // Return from subroutine 2      if (code->ret_sub() !=0)        APIERROR (code->getNdbError());      /* Finalise code object       * This step is essential for NdbInterpretedCode objects       * and must be done before they can be used.       */      if (code->finalise() !=0)        APIERROR (code->getNdbError());      /* Time to define the update operation to use the       * InterpretedCode object.  The same finalised object       * could be used with multiple operations or even       * multiple threads       */      NdbOperation::OperationOptions oo;      oo.optionsPresent=        NdbOperation::OperationOptions::OO_INTERPRETED;      oo.interpretedCode= code;      unsigned char mask= 0;      pop= myTransaction->updateTuple(pkeyColumnRecord,                                      (char*) &rowData,                                      pallColsRecord,                                      (char*) &rowData,                                      (const unsigned char *) &mask,                                                // mask - update nothing                                      &oo,                                      sizeof(NdbOperation::OperationOptions));      if (pop == NULL)        APIERROR (myTransaction->getNdbError());      // NoCommit execute so we can read the 'after' data.      if (myTransaction->execute( NdbTransaction::NoCommit ) != 0)        APIERROR(myTransaction->getNdbError());      /* Second read op as we can't currently do a 'read after       * 'interpreted code' read as part of NdbRecord.       * We are assuming that the order of op definition == order       * of execution on a single row, which is not guaranteed.       */      const NdbOperation *pop2=        myTransaction->readTuple(pkeyColumnRecord,                                 (char*) &rowData,                                 pallColsRecord,                                 (char*) &rowData2);      if (pop2 == NULL)        APIERROR (myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  // Check return code  if (myTransaction->getNdbError().status != NdbError::Success)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      printf(" %2d    %2d    %2d  Before\n"             " %2d    %2d    %2d  After\n",             recAttrAttr1->u_32_value(),             recAttrAttr2->u_32_value(),             recAttrAttr3->u_32_value(),             recAttrAttr11->u_32_value(),             recAttrAttr12->u_32_value(),             recAttrAttr13->u_32_value());      break;    }    case api_record :    {      printf(" %2d    %2d    %2d  Before\n"             " %2d    %2d    %2d  After\n",             rowData.attr1,             rowData.attr2,             rowData.attr3,             rowData2.attr1,             rowData2.attr2,             rowData2.attr3);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/****************************************************** * Read and print selected rows with interpreted code * ******************************************************/static void do_interpreted_scan(Ndb &myNdb, ApiType accessType){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                              myDict->getTable("api_recattr_vs_record");  std::cout << "Running do_interpreted_scan\n";  if (myTable == NULL)    APIERROR(myDict->getNdbError());  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  NdbTransaction *myTransaction=myNdb.startTransaction();  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());  NdbScanOperation *psop;  NdbRecAttr *recAttrAttr1;  NdbRecAttr *recAttrAttr2;  NdbRecAttr *recAttrAttr3;  /* Create some space on the stack for the program */  const Uint32 numWords= 64;  Uint32 space[numWords];  NdbInterpretedCode stackCode(myTable,                               &space[0],                               numWords);  NdbInterpretedCode *code= &stackCode;  /* RecAttr and NdbRecord scans both use NdbInterpretedCode   * Let's define a small scan filter of sorts   */  Uint32 comparisonValue= 10;  // Return rows where 10 > ATTR3 (ATTR3 <10)  if (code->branch_col_gt(&comparisonValue,                          sizeof(Uint32),                          pattr3Col->getColumnNo(),                          0) != 0)    APIERROR (myTransaction->getNdbError());  /* If we get here then we don't return this row */  if (code->interpret_exit_nok() != 0)    APIERROR (myTransaction->getNdbError());  /* Label 0 */  if (code->def_label(0) != 0)    APIERROR (myTransaction->getNdbError());  /* Return this row */  if (code->interpret_exit_ok() != 0)    APIERROR (myTransaction->getNdbError());  /* Finalise the Interpreted Program */  if (code->finalise() != 0)    APIERROR (myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      psop=myTransaction->getNdbScanOperation(myTable);      if (psop == NULL)        APIERROR(myTransaction->getNdbError());      if (psop->readTuples(NdbOperation::LM_Read) != 0)        APIERROR (myTransaction->getNdbError());      if (psop->setInterpretedCode(code) != 0)        APIERROR (myTransaction->getNdbError());      recAttrAttr1=psop->getValue("ATTR1");      recAttrAttr2=psop->getValue("ATTR2");      recAttrAttr3=psop->getValue("ATTR3");      break;    }    case api_record :    {      NdbScanOperation::ScanOptions so;      so.optionsPresent = NdbScanOperation::ScanOptions::SO_INTERPRETED;      so.interpretedCode= code;      psop=myTransaction->scanTable(pallColsRecord,                                    NdbOperation::LM_Read,                                    NULL, // mask                                    &so,                                    sizeof(NdbScanOperation::ScanOptions));      if (psop == NULL) APIERROR(myTransaction->getNdbError());      break;    }  default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::NoCommit ) != 0)    APIERROR(myTransaction->getNdbError());  switch (accessType)  {    case api_attr :    {      while (psop->nextResult(true) == 0)      {        printf(" %2d    %2d    %2d\n",               recAttrAttr1->u_32_value(),               recAttrAttr2->u_32_value(),               recAttrAttr3->u_32_value());      }      psop->close();      break;    }    case api_record :    {      RowData *prowData; // Ptr to point to our data      int rc=0;      while ((rc = psop->nextResult((const char**) &prowData,                                    true,                                    false)) == GOT_ROW)      {        printf(" %2d    %2d    %2d\n",               prowData->attr1,               prowData->attr2,               prowData->attr3);      }      if (rc != NO_MORE_ROWS)  APIERROR(myTransaction->getNdbError());      psop->close(true);      break;    }    default :    {      std::cout << "Bad branch : " << accessType << "\n";      exit(-1);    }  }  if(myTransaction->execute( NdbTransaction::Commit ) !=0)    APIERROR(myTransaction->getNdbError());  myNdb.closeTransaction(myTransaction);  std::cout << "-------\n";}/****************************************************** * Read some data using the default NdbRecord objects * ******************************************************/static void do_read_using_default(Ndb &myNdb){  NdbDictionary::Dictionary* myDict= myNdb.getDictionary();  const NdbDictionary::Table *myTable=                              myDict->getTable("api_recattr_vs_record");  const NdbRecord* tableRec= myTable->getDefaultRecord();  if (myTable == NULL)    APIERROR(myDict->getNdbError());  std::cout << "Running do_read_using_default_record (NdbRecord only)\n";  std::cout << "ATTR1 ATTR2 ATTR3" << std::endl;  /* Allocate some space for the rows to be read into */  char* buffer= (char*)malloc(NdbDictionary::getRecordRowLength(tableRec));  if (buffer== NULL)  {    printf("Allocation failed\n");    exit(-1);  }  for (int i = 0; i < 10; i++) {    NdbTransaction *myTransaction= myNdb.startTransaction();    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());    char* attr1= NdbDictionary::getValuePtr(tableRec,                                            buffer,                                            attr1ColNum);    *((unsigned int*)attr1)= i;    const NdbOperation *pop=      myTransaction->readTuple(tableRec,                               buffer,                               tableRec, // Read everything                               buffer);    if (pop==NULL) APIERROR(myTransaction->getNdbError());    if(myTransaction->execute( NdbTransaction::Commit ) == -1)      APIERROR(myTransaction->getNdbError());    NdbError err= myTransaction->getNdbError();    if (err.code != 0)    {      if (err.classification == NdbError::NoDataFound)        std::cout << "Tuple " << i                  << " does not exist." << std::endl;      else        APIERROR(myTransaction->getNdbError());    }    else    {      printf(" %2d    %2d    %2d\n",             i,             *((unsigned int*) NdbDictionary::getValuePtr(tableRec,                                                          buffer,                                                          attr2ColNum)),             *((unsigned int*) NdbDictionary::getValuePtr(tableRec,                                                          buffer,                                                          attr3ColNum)));    }    myNdb.closeTransaction(myTransaction);  }  free(buffer);  std::cout << "-------\n";}