MySQL NDB Cluster API Developer Guide / ... / The NDB API / NDB API Examples / 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";}