List:Commits« Previous MessageNext Message »
From:jon Date:April 28 2006 7:47am
Subject:svn commit - mysqldoc@docsrva: r1960 - trunk/ndbapi
View as plain text  
Author: jstephens
Date: 2006-04-28 09:47:31 +0200 (Fri, 28 Apr 2006)
New Revision: 1960

Log:

Added existing NDBAPI examples from v 1.x doc.



Modified:
   trunk/ndbapi/examples.xml

Modified: trunk/ndbapi/examples.xml
===================================================================
--- trunk/ndbapi/examples.xml	2006-04-27 16:23:34 UTC (rev 1959)
+++ trunk/ndbapi/examples.xml	2006-04-28 07:47:31 UTC (rev 1960)
@@ -27,60 +27,2018 @@
 
     <title>Using Synchronous Transactions</title>
 
-<!-- <abstract> -->
+    <abstract>
 
-    <para></para>
+      <para>
+        This example illustrates the use of synchronous transactions in
+        the NDB API.
+      </para>
 
-<!-- </abstract> -->
+      <para>
+        The source code for this example can be found in
+        <filename>storage/ndb/ndbapi-examples/ndbapi_simple_index/ndbapi_simple_index.cpp</filename>
+        in the MySQL 5.1 tree.
+      </para>
 
+    </abstract>
+
+    <para>
+      The correct output from this program is as follows:
+    </para>
+
+<programlisting>
+ ATTR1  ATTR2
+ 0      10
+ 1       1
+ 2      12
+ Detected that deleted tuple doesn't exist!
+ 4      14
+ 5       5
+ 6      16
+ 7       7
+ 8      18
+ 9       9
+</programlisting>
+
+<programlisting>
+/* 
+ *  ndbapi_simple.cpp: Using synchronous transactions in the NDB API.
+ */
+
+#include &lt;mysql.h&gt;
+#include &lt;NdbApi.hpp&gt;
+
+// Used for cout
+#include &lt;stdio.h&gt;
+#include &lt;iostream&gt;
+
+static void run_application(MYSQL &amp;, Ndb_cluster_connection &amp;);
+
+#define PRINT_ERROR(code,msg) \
+  std::cout &lt;&lt; "Error in " &lt;&lt; __FILE__ &lt;&lt; ", line: " &lt;&lt; __LINE__ \
+            &lt;&lt; ", code: " &lt;&lt; code \
+            &lt;&lt; ", msg: " &lt;&lt; msg &lt;&lt; "." &lt;&lt; std::endl
+#define MYSQLERROR(mysql) { \
+  PRINT_ERROR(mysql_errno(&amp;mysql),mysql_error(&amp;mysql)); \
+  exit(-1); }
+#define APIERROR(error) { \
+  PRINT_ERROR(error.code,error.message); \
+  exit(-1); }
+
+int main()
+{
+  // ndb_init must be called first.
+  ndb_init();
+
+  //  Connect to the MySQL server and the cluster, 
+  //  and run the application.
+  {
+    // Object representing the cluster.
+    Ndb_cluster_connection cluster_connection;
+
+    // Connect to the cluster management server (ndb_mgmd)
+    if (cluster_connection.connect(
+            4 /* retries               */,
+            5 /* delay between retries */,
+            1 /* verbose               */))
+    {
+      std::cout &lt;&lt; "Cluster management server was not ready within 30 seconds.\n";
+      exit(-1);
+    }
+
+    //  (Optional:) Connect and wait 
+    //  for the storage nodes (ndbd processes)
+    if (cluster_connection.wait_until_ready(30,0) &lt; 0)
+    {
+      std::cout &lt;&lt; "Cluster was not ready within 30 secs.\n";
+      exit(-1);
+    }
+
+    // Connect to the MySQL server.
+    MYSQL mysql;
+    if ( !mysql_init(&amp;mysql) ) {
+      std::cout &lt;&lt; "mysql_init failed\n";
+      exit(-1);
+    }
+    if ( !mysql_real_connect(&amp;mysql, "localhost", "root", "", "",
+           3306, "/tmp/mysql.sock", 0) )
+      MYSQLERROR(mysql);
+    
+    // Run the application code.
+    run_application(mysql, cluster_connection);
+  }
+
+  ndb_end(0);
+
+  std::cout &lt;&lt; "\nTo drop created table use:\n"
+      &lt;&lt; "echo \"drop table MYTABLENAME\" | mysql TEST_DB_1 -u root\n";
+
+  return 0;
+}
+
+static void create_table(MYSQL &amp;);
+static void do_insert(Ndb &amp;);
+static void do_update(Ndb &amp;);
+static void do_delete(Ndb &amp;);
+static void do_read(Ndb &amp;);
+
+static void run_application(MYSQL &amp;mysql,
+          Ndb_cluster_connection &amp;cluster_connection)
+{
+  /********************************************
+   * Connect to database via MySQL C API.     *
+   ********************************************/
+  mysql_query(&amp;mysql, "CREATE DATABASE TEST_DB_1");
+  if (mysql_query(&amp;mysql, "USE TEST_DB_1") != 0) MYSQLERROR(mysql);
+  create_table(mysql);
+
+  /********************************************
+   * Connect to database via NDB API          *
+   ********************************************/
+  // Object representing the database.
+  Ndb myNdb( &amp;cluster_connection, "TEST_DB_1" );
+  if (myNdb.init()) APIERROR(myNdb.getNdbError());
+
+  /*
+   * Perform various operations on the database.
+   */
+  do_insert(myNdb);
+  do_update(myNdb);
+  do_delete(myNdb);
+  do_read(myNdb);
+}
+
+/*********************************************************
+ * Create a table named MYTABLENAME if it does not exist.*
+ *********************************************************/
+static void create_table(MYSQL &amp;mysql)
+{
+  if (mysql_query(&amp;mysql, 
+      "CREATE TABLE"
+      "  MYTABLENAME"
+      "    (ATTR1 INT UNSIGNED NOT NULL PRIMARY KEY,"
+      "     ATTR2 INT UNSIGNED NOT NULL)"
+      "  ENGINE=NDB"))
+    MYSQLERROR(mysql);
+}
+
+/***********************************************
+ * Using 5 transactions, insert 10 tuples into *
+ * the table: (0,0),(1,1),...,(9,9)            *
+ ***********************************************/
+ 
+static void do_insert(Ndb &amp;myNdb)
+{
+  const NdbDictionary::Dictionary* myDict= myNdb.getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("MYTABLENAME");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  for (int i = 0; i &lt; 5; i++) {
+    NdbTransaction *myTransaction= myNdb.startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());
+    
+    NdbOperation *myOperation= myTransaction-&gt;getNdbOperation(myTable);
+    if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+    myOperation-&gt;insertTuple();
+    myOperation-&gt;equal("ATTR1", i);
+    myOperation-&gt;setValue("ATTR2", i);
+
+    myOperation= myTransaction-&gt;getNdbOperation(myTable);
+    if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+
+    myOperation-&gt;insertTuple();
+    myOperation-&gt;equal("ATTR1", i+5);
+    myOperation-&gt;setValue("ATTR2", i+5);
+    
+    if (myTransaction-&gt;execute( NdbTransaction::Commit ) == -1)
+      APIERROR(myTransaction-&gt;getNdbError());
+    
+    myNdb.closeTransaction(myTransaction);
+  }
+}
+ 
+/*****************************************************************
+ * Update the second attribute in half of the tuples (adding 10).*
+ *****************************************************************/
+ 
+static void do_update(Ndb &amp;myNdb)
+{
+  const NdbDictionary::Dictionary* myDict= myNdb.getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("MYTABLENAME");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  for (int i = 0; i &lt; 10; i+=2) {
+    NdbTransaction *myTransaction= myNdb.startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());
+    
+    NdbOperation *myOperation= myTransaction-&gt;getNdbOperation(myTable);
+    if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+    myOperation-&gt;updateTuple();
+    myOperation-&gt;equal( "ATTR1", i );
+    myOperation-&gt;setValue( "ATTR2", i+10);
+    
+    if( myTransaction-&gt;execute( NdbTransaction::Commit ) == -1 ) 
+      APIERROR(myTransaction-&gt;getNdbError());
+    
+    myNdb.closeTransaction(myTransaction);
+  }
+}
+  
+/**********************************************************
+ * Delete one tuple (the one whose primary key equals 3). *
+ **********************************************************/
+ 
+static void do_delete(Ndb &amp;myNdb)
+{
+  const NdbDictionary::Dictionary* myDict= myNdb.getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("MYTABLENAME");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  NdbTransaction *myTransaction= myNdb.startTransaction();
+  if (myTransaction == NULL) APIERROR(myNdb.getNdbError());
+  
+  NdbOperation *myOperation= myTransaction-&gt;getNdbOperation(myTable);
+  if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+  
+  myOperation-&gt;deleteTuple();
+  myOperation-&gt;equal( "ATTR1", 3 );
+  
+  if (myTransaction-&gt;execute(NdbTransaction::Commit) == -1) 
+    APIERROR(myTransaction-&gt;getNdbError());
+  
+  myNdb.closeTransaction(myTransaction);
+}
+
+/******************************
+ * Read and print all tuples. *
+ ******************************/
+static void do_read(Ndb &amp;myNdb)
+{
+  const NdbDictionary::Dictionary* myDict= myNdb.getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("MYTABLENAME");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  std::cout &lt;&lt; "ATTR1 ATTR2" &lt;&lt; std::endl;
+  
+  for (int i = 0; i &lt; 10; i++) {
+    NdbTransaction *myTransaction= myNdb.startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());
+    
+    NdbOperation *myOperation= myTransaction-&gt;getNdbOperation(myTable);
+    if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+    myOperation-&gt;readTuple(NdbOperation::LM_Read);
+    myOperation-&gt;equal("ATTR1", i);
+
+    NdbRecAttr *myRecAttr= myOperation-&gt;getValue("ATTR2", NULL);
+    if (myRecAttr == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+    if(myTransaction-&gt;execute( NdbTransaction::Commit ) == -1)
+      if (i == 3) {
+  std::cout &lt;&lt; "Detected that deleted tuple doesn't exist!" &lt;&lt; std::endl;
+      } else {
+  APIERROR(myTransaction-&gt;getNdbError());
+      }
+    
+    if (i != 3) {
+      printf(" %2d    %2d\n", i, myRecAttr-&gt;u_32_value());
+    }
+    myNdb.closeTransaction(myTransaction);
+  }
+}
+</programlisting>
+
   </section>
 
   <section id="example-handling-errors">
 
     <title>Handling Errors and Retrying Transactions</title>
 
-<!-- <abstract> -->
+    <abstract>
 
-    <para></para>
+      <para>
+        This program demonstrates handling errors and retrying failed
+        transactions using the NDB API.
+      </para>
 
-<!-- </abstract> -->
+      <para>
+        The source code for this example can be found in
+        <filename>storage/ndb/ndbapi-examples/ndbapi_retries/ndbapi_retries.cpp</filename>
+        in the MySQL 5.1 tree.
+      </para>
 
+    </abstract>
+
+    <para>
+      There are many ways to program using the NDB API. In this example,
+      we perform two inserts in the same transaction using
+      <literal>NdbConnection::execute(NoCommit)</literal>.
+    </para>
+
+    <para>
+      In NDB API applications, there are two types of failures to be
+      taken into account:
+    </para>
+
+    <orderedlist>
+
+      <listitem>
+        <para>
+          <emphasis role="bold">Transaction failures</emphasis>: If
+          non-permanent, these can be handled handled by re-executing
+          the transaction.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          <emphasis role="bold">Application errors</emphasis>: These are
+          indicated by <literal>APIERROR</literal>; they must be handled
+          by the application programmer.
+        </para>
+      </listitem>
+
+    </orderedlist>
+
+    <important>
+
+      <para>
+        Before executing this program, you should first run
+        <command>ndbapi_simple</command> (see
+        <xref linkend="example-synchronous-transactions"/>), in order to
+        create the table <literal>MYTABLENAME</literal>.
+      </para>
+
+    </important>
+
+<programlisting>
+/* 
+ *  ndbapi_retries.cpp: Error handling and retrying transactions
+ */
+
+#include &lt;NdbApi.hpp&gt;
+
+// Used for cout.
+#include &lt;iostream&gt;  
+
+// Used for sleep (use your own version of sleep).
+#include &lt;unistd.h&gt;
+#define TIME_TO_SLEEP_BETWEEN_TRANSACTION_RETRIES 1
+
+//
+//  APIERROR prints an NdbError object.
+//
+
+#define APIERROR(error) \
+  { std::cout &lt;&lt; "API ERROR: " &lt;&lt; error.code &lt;&lt; " " &lt;&lt; error.message \
+              &lt;&lt; std::endl \
+              &lt;&lt; "           " &lt;&lt; "Status: " &lt;&lt; error.status \
+              &lt;&lt; ", Classification: " &lt;&lt; error.classification &lt;&lt; std::endl\
+              &lt;&lt; "           " &lt;&lt; "File: " &lt;&lt; __FILE__ \
+              &lt;&lt; " (Line: " &lt;&lt; __LINE__ &lt;&lt; ")" &lt;&lt; std::endl \
+              ; \
+  }
+
+//
+//  TRANSERROR prints all error info regarding an NdbTransaction.
+//
+
+#define TRANSERROR(ndbTransaction) \
+  { NdbError error = ndbTransaction-&gt;getNdbError(); \
+    std::cout &lt;&lt; "TRANS ERROR: " &lt;&lt; error.code &lt;&lt; " " &lt;&lt; error.message \
+              &lt;&lt; std::endl \
+              &lt;&lt; "           " &lt;&lt; "Status: " &lt;&lt; error.status \
+              &lt;&lt; ", Classification: " &lt;&lt; error.classification &lt;&lt; std::endl \
+              &lt;&lt; "           " &lt;&lt; "File: " &lt;&lt; __FILE__ \
+              &lt;&lt; " (Line: " &lt;&lt; __LINE__ &lt;&lt; ")" &lt;&lt; std::endl \
+              ; \
+    printTransactionError(ndbTransaction); \
+  }
+
+void printTransactionError(NdbTransaction *ndbTransaction) {
+  const NdbOperation *ndbOp = NULL;
+  int i=0;
+
+  /****************************************************************
+   * Print NdbError object for each operation in the transaction. *
+   ****************************************************************/
+  while ((ndbOp = ndbTransaction-&gt;getNextCompletedOperation(ndbOp)) != NULL) {
+    NdbError error = ndbOp-&gt;getNdbError();
+    std::cout &lt;&lt; "           OPERATION " &lt;&lt; i+1 &lt;&lt; ": " 
+        &lt;&lt; error.code &lt;&lt; " " &lt;&lt; error.message &lt;&lt; std::endl
+        &lt;&lt; "           Status: " &lt;&lt; error.status 
+        &lt;&lt; ", Classification: " &lt;&lt; error.classification &lt;&lt; std::endl;
+    i++;
+  }
+}
+
+//  Example insert:
+//  @param myNdb          Ndb object representing NDB Cluster
+//  @param myTransaction  NdbTransaction used for transaction
+//  @param myTable        Table to insert into
+//  @param error          NdbError object returned in case of errors
+//  @return -1 on failure, otherwise 0.
+//
+int insert(int transactionId, NdbTransaction* myTransaction,
+     const NdbDictionary::Table *myTable) {
+  NdbOperation   *myOperation;          // For other operations.
+
+  myOperation = myTransaction-&gt;getNdbOperation(myTable);
+  if (myOperation == NULL) return -1;
+  
+  if (myOperation-&gt;insertTuple() ||  
+      myOperation-&gt;equal("ATTR1", transactionId) ||
+      myOperation-&gt;setValue("ATTR2", transactionId)) {
+    APIERROR(myOperation-&gt;getNdbError());
+    exit(-1);
+  }
+
+  return myTransaction-&gt;execute(NdbTransaction::NoCommit);
+}
+
+//  Execute function which re-executes the transaction (tries 10 times) 
+//  if there are temporary errors (for example, if the NDB Cluster is 
+//  overloaded).
+//  @return -1 on failure, 1 on success
+//
+int executeInsertTransaction(int transactionId, Ndb* myNdb,
+           const NdbDictionary::Table *myTable) {
+  int result = 0;                       // No result yet.
+  int noOfRetriesLeft = 10;
+  NdbTransaction   *myTransaction;      // For other transactions.
+  NdbError ndberror;
+  
+  while (noOfRetriesLeft &gt; 0 &amp;&amp; !result) {
+    
+    /*************************************
+     * Start and execute the transaction.*
+     *************************************/
+    myTransaction = myNdb-&gt;startTransaction();
+    if (myTransaction == NULL) {
+      APIERROR(myNdb-&gt;getNdbError());
+      ndberror = myNdb-&gt;getNdbError();
+      result = -1;  // Failure
+    } else if (insert(transactionId, myTransaction, myTable) || 
+         insert(10000+transactionId, myTransaction, myTable) ||
+         myTransaction-&gt;execute(NdbTransaction::Commit)) {
+      TRANSERROR(myTransaction);
+      ndberror = myTransaction-&gt;getNdbError();
+      result = -1;  // Failure
+    } else {
+      result = 1;   // Success
+    }
+
+    /**********************************
+     * On failure: analyze the error. *
+     **********************************/
+    if (result == -1) {                 
+      switch (ndberror.status) {
+      case NdbError::Success:
+  break;
+      case NdbError::TemporaryError:
+  std::cout &lt;&lt; "Retrying transaction..." &lt;&lt; std::endl;
+  sleep(TIME_TO_SLEEP_BETWEEN_TRANSACTION_RETRIES);
+  --noOfRetriesLeft;
+  result = 0;   // No completed transaction yet
+  break;
+  
+      case NdbError::UnknownResult:
+      case NdbError::PermanentError:
+  std::cout &lt;&lt; "No retry of transaction..." &lt;&lt; std::endl;
+  result = -1;  // Permanent failure
+  break;
+      }
+    }
+
+    /**************************
+     * Close the transaction. *
+     **************************/
+    if (myTransaction != NULL) {
+      myNdb-&gt;closeTransaction(myTransaction);
+    }
+  }
+
+  if (result != 1) exit(-1);
+  return result;
+}
+
+
+int main()
+{
+  ndb_init();
+
+  Ndb_cluster_connection *cluster_connection=
+    new Ndb_cluster_connection(); // Object representing the cluster.
+
+  int r= cluster_connection-&gt;connect(
+                      5 /* retries               */,
+                      3 /* delay between retries */,
+                      1 /* verbose               */);
+  if (r &gt; 0)
+  {
+    std::cout
+      &lt;&lt; "Cluster connection failed, possibly resolved with more retries.\n";
+    exit(-1);
+  }
+  else if (r &lt; 0)
+  {
+    std::cout
+      &lt;&lt; "Cluster connection failed.\n";
+    exit(-1);
+  }
+             
+  if (cluster_connection-&gt;wait_until_ready(30,30))
+  {
+    std::cout &lt;&lt; "Cluster was not ready within 30 seconds." &lt;&lt; std::endl;
+    exit(-1);
+  }
+
+  Ndb* myNdb= new Ndb( cluster_connection,
+           "TEST_DB_1" );  // Object representing the database.
+  
+  if (myNdb-&gt;init() == -1) {
+    APIERROR(myNdb-&gt;getNdbError());
+    exit(-1);
+  }
+
+  const NdbDictionary::Dictionary* myDict= myNdb-&gt;getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("MYTABLENAME");
+  if (myTable == NULL)
+  {
+    APIERROR(myDict-&gt;getNdbError());
+    return -1;
+  }
+  /*************************************
+   * Execute some insert transactions. *
+   *************************************/
+  for (int i = 10000; i &lt; 20000; i++) {
+    executeInsertTransaction(i, myNdb, myTable);
+  }
+  
+  delete myNdb;
+  delete cluster_connection;
+
+  ndb_end(0);
+  return 0;
+}
+</programlisting>
+
   </section>
 
   <section id="example-basic-scanning">
 
     <title>Basic Scanning Example</title>
 
-<!-- <abstract> -->
+    <abstract>
 
-    <para></para>
+      <para>
+        This example illustrates how to use the NDB scanning API. It
+        shows how to perform a scan, how to scan for an update, and how
+        to scan for a delete, making use of the
+        <literal>NdbScanFilter</literal> and
+        <literal>NdbScanOperation</literal> classes.
+      </para>
 
-<!-- </abstract> -->
+      <para>
+        (See <xref linkend="ndb-class-ndbscanfilter"/>, and
+        <xref linkend="ndb-class-ndbscanoperation"/>.)
+      </para>
 
+      <para>
+        The source code for this example may found in MySQL 5.1 tree, in
+        the file
+        <filename>storage/ndb/ndbapi-examples/ndbapi_scan/ndbapi_scan.cpp</filename>.
+      </para>
+
+    </abstract>
+
+    <para>
+      This example makes use of the following classes and methods:
+    </para>
+
+    <itemizedlist>
+
+      <listitem>
+        <para>
+          <literal>Ndb_cluster_connection</literal>:
+        </para>
+
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              <literal>connect()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>wait_until_ready()</literal>
+            </para>
+          </listitem>
+
+        </itemizedlist>
+
+        <para>
+          See <xref linkend="ndb-class-ndb-cluster-connection"/>.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          <literal>Ndb</literal>:
+        </para>
+
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              <literal>init()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>getDictionary()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>startTransaction()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>closeTransaction()</literal>
+            </para>
+          </listitem>
+
+        </itemizedlist>
+
+        <para>
+          See <xref linkend="ndb-class-ndb"/>.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          <literal>NdbTransaction</literal>:
+        </para>
+
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              <literal>getNdbScanOperation()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>execute()</literal>
+            </para>
+          </listitem>
+
+        </itemizedlist>
+
+        <para>
+          See <xref linkend="ndb-class-ndbtransaction"/>.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          <literal>NdbOperation</literal>:
+        </para>
+
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              <literal>insertTuple()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>equal()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>setValue()</literal>
+            </para>
+          </listitem>
+
+        </itemizedlist>
+
+        <para>
+          See <xref linkend="ndb-class-ndboperation"/>.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          <literal>NdbScanOperation</literal>:
+        </para>
+
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              <literal>getValue()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>readTuples()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>nextResult()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>deleteCurrentTuple()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>updateCurrentTuple()</literal>
+            </para>
+          </listitem>
+
+        </itemizedlist>
+
+        <para>
+          See <xref linkend="ndb-class-ndbscanoperation"/>.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          <literal>NdbDictionary</literal>:
+        </para>
+
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              <literal>Dictionary::getTable()</literal>
+            </para>
+
+            <para>
+              See <xref linkend="ndb-class-dictionary"/>.
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>Table::getColumn()</literal>
+            </para>
+
+            <para>
+              See <xref linkend="ndb-class-table"/>.
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>Column::getLength()</literal>
+            </para>
+
+            <para>
+              See <xref linkend="ndb-class-column"/>.
+            </para>
+          </listitem>
+
+        </itemizedlist>
+      </listitem>
+
+      <listitem>
+        <para>
+          <literal>NdbScanFilter</literal>:
+        </para>
+
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              <literal>begin()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>eq()</literal>
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              <literal>end()</literal>
+            </para>
+          </listitem>
+
+        </itemizedlist>
+
+        <para>
+          See <xref linkend="ndb-class-ndbscanfilter"/>.
+        </para>
+      </listitem>
+
+    </itemizedlist>
+
+<programlisting>
+/*
+ * ndbapi_scan.cpp: Use of the NDB scanning API
+ */
+
+
+#include &lt;mysql.h&gt;
+#include &lt;mysqld_error.h&gt;
+#include &lt;NdbApi.hpp&gt;
+// Used for cout
+#include &lt;iostream&gt;
+#include &lt;stdio.h&gt;
+
+static void
+milliSleep(int milliseconds){
+  struct timeval sleeptime;
+  sleeptime.tv_sec = milliseconds / 1000;
+  sleeptime.tv_usec = (milliseconds - (sleeptime.tv_sec * 1000)) * 1000000;
+  select(0, 0, 0, 0, &amp;sleeptime);
+}
+
+
+#define PRINT_ERROR(code,msg) \
+  std::cout &lt;&lt; "Error in " &lt;&lt; __FILE__ &lt;&lt; ", line: " &lt;&lt; __LINE__ \
+            &lt;&lt; ", code: " &lt;&lt; code \
+            &lt;&lt; ", msg: " &lt;&lt; msg &lt;&lt; "." &lt;&lt; std::endl
+#define MYSQLERROR(mysql) { \
+  PRINT_ERROR(mysql_errno(&amp;mysql),mysql_error(&amp;mysql)); \
+  exit(-1); }
+#define APIERROR(error) { \
+  PRINT_ERROR(error.code,error.message); \
+  exit(-1); }
+
+struct Car 
+{
+  Car() { memset(this, 0, sizeof(* this)); }
+  
+  unsigned int reg_no;
+  char brand[20];
+  char color[20];
+};
+
+int create_table(MYSQL &amp;mysql) 
+{
+  while (mysql_query(&amp;mysql, 
+      "CREATE TABLE"
+      "  GARAGE"
+      "    (REG_NO INT UNSIGNED NOT NULL,"
+      "     BRAND CHAR(20) NOT NULL,"
+      "     COLOR CHAR(20) NOT NULL,"
+      "     PRIMARY KEY USING HASH (REG_NO))"
+      "  ENGINE=NDB"))
+  {
+    if (mysql_errno(&amp;mysql) != ER_TABLE_EXISTS_ERROR)
+      MYSQLERROR(mysql);
+    std::cout &lt;&lt; "MySQL Cluster already has the example table GARAGE. "
+        &lt;&lt; "Now dropping it..." &lt;&lt; std::endl; 
+    /***************
+     * Drop table. *
+     ***************/
+    if (mysql_query(&amp;mysql, "DROP TABLE GARAGE"))
+      MYSQLERROR(mysql);
+  }
+  return 1;
+}
+
+
+int populate(Ndb * myNdb)
+{
+  int i;
+  Car cars[15];
+
+  const NdbDictionary::Dictionary* myDict= myNdb-&gt;getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("GARAGE");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  for (i = 0; i &lt; 5; i++)
+  {
+    cars[i].reg_no = i;
+    sprintf(cars[i].brand, "Mercedes");
+    sprintf(cars[i].color, "Blue");
+  }
+
+  for (i = 5; i &lt; 10; i++)
+  {
+    cars[i].reg_no = i;
+    sprintf(cars[i].brand, "BMW");
+    sprintf(cars[i].color, "Black");
+  }
+
+  for (i = 10; i &lt; 15; i++)
+  {
+    cars[i].reg_no = i;
+    sprintf(cars[i].brand, "Toyota");
+    sprintf(cars[i].color, "Pink");
+  }
+  
+  NdbTransaction* myTrans = myNdb-&gt;startTransaction();
+  if (myTrans == NULL)
+    APIERROR(myNdb-&gt;getNdbError());
+
+  for (i = 0; i &lt; 15; i++) 
+  {
+    NdbOperation* myNdbOperation = myTrans-&gt;getNdbOperation(myTable);
+    if (myNdbOperation == NULL) 
+      APIERROR(myTrans-&gt;getNdbError());
+    myNdbOperation-&gt;insertTuple();
+    myNdbOperation-&gt;equal("REG_NO", cars[i].reg_no);
+    myNdbOperation-&gt;setValue("BRAND", cars[i].brand);
+    myNdbOperation-&gt;setValue("COLOR", cars[i].color);
+  }
+
+  int check = myTrans-&gt;execute(NdbTransaction::Commit);
+
+  myTrans-&gt;close();
+
+  return check != -1;
+}
+
+int scan_delete(Ndb* myNdb, 
+    int column,
+    const char * color)
+  
+{
+  
+  // Perform an exclusive scan on all records, 
+  // then delete them one by one.
+  int                  retryAttempt = 0;
+  const int            retryMax = 10;
+  int deletedRows = 0;
+  int check;
+  NdbError              err;
+  NdbTransaction  *myTrans;
+  NdbScanOperation  *myScanOp;
+
+  const NdbDictionary::Dictionary* myDict= myNdb-&gt;getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("GARAGE");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  while (true)
+  {
+    if (retryAttempt &gt;= retryMax)
+    {
+      std::cout &lt;&lt; "ERROR: has retried this operation " &lt;&lt; retryAttempt 
+    &lt;&lt; " times, failing!" &lt;&lt; std::endl;
+      return -1;
+    }
+
+    myTrans = myNdb-&gt;startTransaction();
+    if (myTrans == NULL) 
+    {
+      const NdbError err = myNdb-&gt;getNdbError();
+
+      if (err.status == NdbError::TemporaryError)
+      {
+  milliSleep(50);
+  retryAttempt++;
+  continue;
+      }
+      std::cout &lt;&lt;  err.message &lt;&lt; std::endl;
+      return -1;
+    }
+
+    myScanOp = myTrans-&gt;getNdbScanOperation(myTable); 
+    if (myScanOp == NULL) 
+    {
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }
+
+    if(myScanOp-&gt;readTuples(NdbOperation::LM_Exclusive) != 0)
+    {
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    } 
+    
+    NdbScanFilter filter(myScanOp) ;   
+    if(filter.begin(NdbScanFilter::AND) &lt; 0  || 
+       filter.cmp(NdbScanFilter::COND_EQ, column, color) &lt; 0 ||
+       filter.end() &lt; 0)
+    {
+      std::cout &lt;&lt;  myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }    
+    
+    if(myTrans-&gt;execute(NdbTransaction::NoCommit) != 0){      
+      err = myTrans-&gt;getNdbError();    
+      if(err.status == NdbError::TemporaryError){
+  std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+  myNdb-&gt;closeTransaction(myTrans);
+  milliSleep(50);
+  continue;
+      }
+      std::cout &lt;&lt; err.code &lt;&lt; std::endl;
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().code &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }
+
+
+    while((check = myScanOp-&gt;nextResult(true)) == 0){
+      do 
+      {
+  if (myScanOp-&gt;deleteCurrentTuple() != 0)
+  {
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+    return -1;
+  }
+  deletedRows++;
+  
+      } while((check = myScanOp-&gt;nextResult(false)) == 0);
+      
+      if(check != -1)
+      {
+  check = myTrans-&gt;execute(NdbTransaction::Commit);   
+      }
+
+      if(check == -1)
+      {
+  check = myTrans-&gt;restart();
+      }
+
+      err = myTrans-&gt;getNdbError();    
+      if(check == -1)
+      {
+  if(err.status == NdbError::TemporaryError)
+  {
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+    milliSleep(50);
+    continue;
+  } 
+      }
+    }
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+    return 0;
+  }
+  
+  if(myTrans!=0) 
+  {
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+  }
+  return -1;
+}
+
+
+int scan_update(Ndb* myNdb, 
+    int update_column,
+    const char * before_color,
+    const char * after_color)
+    
+{
+  
+  //  Perform an exclusve scan on all records, 
+  //  then update them one by one.
+  int                  retryAttempt = 0;
+  const int            retryMax = 10;
+  int updatedRows = 0;
+  int check;
+  NdbError              err;
+  NdbTransaction  *myTrans;
+  NdbScanOperation  *myScanOp;
+
+  const NdbDictionary::Dictionary* myDict= myNdb-&gt;getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("GARAGE");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  while (true)
+  {
+
+    if (retryAttempt &gt;= retryMax)
+    {
+      std::cout &lt;&lt; "ERROR: has retried this operation " &lt;&lt; retryAttempt 
+    &lt;&lt; " times, failing!" &lt;&lt; std::endl;
+      return -1;
+    }
+
+    myTrans = myNdb-&gt;startTransaction();
+    if (myTrans == NULL) 
+    {
+      const NdbError err = myNdb-&gt;getNdbError();
+
+      if (err.status == NdbError::TemporaryError)
+      {
+  milliSleep(50);
+  retryAttempt++;
+  continue;
+      }
+      std::cout &lt;&lt;  err.message &lt;&lt; std::endl;
+      return -1;
+    }
+
+    myScanOp = myTrans-&gt;getNdbScanOperation(myTable); 
+    if (myScanOp == NULL) 
+    {
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }
+
+    if( myScanOp-&gt;readTuples(NdbOperation::LM_Exclusive) ) 
+    {
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    } 
+
+    NdbScanFilter filter(myScanOp) ;   
+    if(filter.begin(NdbScanFilter::AND) &lt; 0  || 
+       filter.cmp(NdbScanFilter::COND_EQ, update_column, before_color) &lt;0||
+       filter.end() &lt;0)
+    {
+      std::cout &lt;&lt;  myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }    
+    
+    if(myTrans-&gt;execute(NdbTransaction::NoCommit) != 0)
+    {      
+      err = myTrans-&gt;getNdbError();    
+      if(err.status == NdbError::TemporaryError){
+  std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+  myNdb-&gt;closeTransaction(myTrans);
+  milliSleep(50);
+  continue;
+      }
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().code &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }
+
+    while((check = myScanOp-&gt;nextResult(true)) == 0){
+      do {
+  NdbOperation * myUpdateOp = myScanOp-&gt;updateCurrentTuple();
+  if (myUpdateOp == 0)
+  {
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+    return -1;
+  }
+  updatedRows++;
+
+  myUpdateOp-&gt;setValue(update_column, after_color);
+  
+      } while((check = myScanOp-&gt;nextResult(false)) == 0);
+      
+      if(check != -1)
+      {
+  check = myTrans-&gt;execute(NdbTransaction::NoCommit);   
+      }
+
+      err = myTrans-&gt;getNdbError();    
+      if(check == -1)
+      {
+  if(err.status == NdbError::TemporaryError){
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+    milliSleep(50);
+    continue;
+  } 
+      }
+    }
+
+    if(myTrans-&gt;execute(NdbTransaction::Commit) == -1)
+    {
+      if(err.status == NdbError::TemporaryError){
+  std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+  myNdb-&gt;closeTransaction(myTrans);
+  milliSleep(50);
+  continue;
+      } 
+    }
+
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+    return 0;    
+  }
+
+
+  if(myTrans!=0) 
+  {
+    std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+    myNdb-&gt;closeTransaction(myTrans);
+  }
+  return -1;
+}
+
+
+
+int scan_print(Ndb * myNdb)
+{
+  //  Perform an exclusve scan on all records, 
+  //  then update them one by one.
+  int                  retryAttempt = 0;
+  const int            retryMax = 10;
+  int fetchedRows = 0;
+  int check;
+  NdbError              err;
+  NdbTransaction  *myTrans;
+  NdbScanOperation  *myScanOp;
+  /* The result from reading the attribute value 
+     is made up of three columns:
+     
+     REG_NO, BRAND, and COLOR.
+   */
+  NdbRecAttr *      myRecAttr[3];   
+
+  const NdbDictionary::Dictionary* myDict= myNdb-&gt;getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("GARAGE");
+
+  if (myTable == NULL) 
+    APIERROR(myDict-&gt;getNdbError());
+
+  while (true)
+  {
+
+    if (retryAttempt &gt;= retryMax)
+    {
+      std::cout &lt;&lt; "ERROR: has retried this operation " &lt;&lt; retryAttempt 
+    &lt;&lt; " times, failing!" &lt;&lt; std::endl;
+      return -1;
+    }
+
+    myTrans = myNdb-&gt;startTransaction();
+    if (myTrans == NULL) 
+    {
+      const NdbError err = myNdb-&gt;getNdbError();
+
+      if (err.status == NdbError::TemporaryError)
+      {
+  milliSleep(50);
+  retryAttempt++;
+  continue;
+      }
+     std::cout &lt;&lt; err.message &lt;&lt; std::endl;
+      return -1;
+    }
+    /*
+     * Define a scan operation. 
+     * (NDBAPI.)
+     */
+    myScanOp = myTrans-&gt;getNdbScanOperation(myTable); 
+    if (myScanOp == NULL) 
+    {
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }
+
+    if( myScanOp-&gt;readTuples(NdbOperation::LM_CommittedRead) == -1)
+    {
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    } 
+
+    myRecAttr[0] = myScanOp-&gt;getValue("REG_NO");
+    myRecAttr[1] = myScanOp-&gt;getValue("BRAND");
+    myRecAttr[2] = myScanOp-&gt;getValue("COLOR");
+    if(myRecAttr[0] ==NULL || myRecAttr[1] == NULL || myRecAttr[2]==NULL) 
+    {
+  std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+  myNdb-&gt;closeTransaction(myTrans);
+  return -1;
+    }
+    
+    if(myTrans-&gt;execute(NdbTransaction::NoCommit) != 0){      
+      err = myTrans-&gt;getNdbError();    
+      if(err.status == NdbError::TemporaryError){
+  std::cout &lt;&lt; myTrans-&gt;getNdbError().message &lt;&lt; std::endl;
+  myNdb-&gt;closeTransaction(myTrans);
+  milliSleep(50);
+  continue;
+      }
+      std::cout &lt;&lt; err.code &lt;&lt; std::endl;
+      std::cout &lt;&lt; myTrans-&gt;getNdbError().code &lt;&lt; std::endl;
+      myNdb-&gt;closeTransaction(myTrans);
+      return -1;
+    }
+    
+    while((check = myScanOp-&gt;nextResult(true)) == 0){
+      do {
+  
+  fetchedRows++;
+  std::cout &lt;&lt; myRecAttr[0]-&gt;u_32_value() &lt;&lt; "\t";
+
+  std::cout &lt;&lt; myRecAttr[1]-&gt;aRef() &lt;&lt; "\t";
+
+  std::cout &lt;&lt; myRecAttr[2]-&gt;aRef() &lt;&lt; std::endl;
+
+      } while((check = myScanOp-&gt;nextResult(false)) == 0);
+
+    }    
+    myNdb-&gt;closeTransaction(myTrans);
+    return 1;
+  }
+  return -1;
+
+}
+
+
+int main()
+{
+  ndb_init();
+  MYSQL mysql;
+
+  /**************************************************************
+   * Connect to the MySQL server, and create the table.         *
+   **************************************************************/
+  {
+    if ( !mysql_init(&amp;mysql) ) {
+      std::cout &lt;&lt; "mysql_init failed\n";
+      exit(-1);
+    }
+    if ( !mysql_real_connect(&amp;mysql, "localhost", "root", "", "",
+           3306, "/tmp/mysql.sock", 0) )
+      MYSQLERROR(mysql);
+
+    mysql_query(&amp;mysql, "CREATE DATABASE TEST_DB");
+    if (mysql_query(&amp;mysql, "USE TEST_DB") != 0) MYSQLERROR(mysql);
+
+    create_table(mysql);
+  }
+
+  /**************************************************************
+   * Connect to NDB Cluster.                                    *
+   **************************************************************/
+
+  Ndb_cluster_connection cluster_connection;
+  if (cluster_connection.connect(4, 5, 1))
+  {
+    std::cout &lt;&lt; "Unable to connect to cluster within 30 secs." &lt;&lt; std::endl;
+    exit(-1);
+  }
+  // (Optional:) Connect and wait 
+  // for the storage nodes (ndbd processess).
+  if (cluster_connection.wait_until_ready(30,0) &lt; 0)
+  {
+    std::cout &lt;&lt; "Cluster was not ready within 30 secs.\n";
+    exit(-1);
+  }
+
+  Ndb myNdb(&amp;cluster_connection,"TEST_DB");
+  if (myNdb.init(1024) == -1) {      // Set max 1024  parallel transactions
+    APIERROR(myNdb.getNdbError());
+    exit(-1);
+  }
+
+  /*******************************************
+   * Check table definition.                 *
+   *******************************************/
+  int column_color;
+  {
+    const NdbDictionary::Dictionary* myDict= myNdb.getDictionary();
+    const NdbDictionary::Table *t= myDict-&gt;getTable("GARAGE");
+
+    Car car;
+    if (t-&gt;getColumn("COLOR")-&gt;getLength() != sizeof(car.color) ||
+  t-&gt;getColumn("BRAND")-&gt;getLength() != sizeof(car.brand))
+    {
+      std::cout &lt;&lt; "Wrong table definition" &lt;&lt; std::endl;
+      exit(-1);
+    }
+    column_color= t-&gt;getColumn("COLOR")-&gt;getColumnNo();
+  }
+
+  if(populate(&amp;myNdb) &gt; 0)
+    std::cout &lt;&lt; "populate: Success!" &lt;&lt; std::endl;
+  
+  if(scan_print(&amp;myNdb) &gt; 0)
+    std::cout &lt;&lt; "scan_print: Success!" &lt;&lt; std::endl  &lt;&lt; std::endl;
+  
+  std::cout &lt;&lt; "Going to delete all pink cars!" &lt;&lt; std::endl;
+  
+  {
+    Car tmp;
+    sprintf(tmp.color, "Pink");
+    if(scan_delete(&amp;myNdb, column_color, tmp.color) &gt; 0)
+      std::cout &lt;&lt; "scan_delete: Success!" &lt;&lt; std::endl  &lt;&lt; std::endl;
+  }
+
+  if(scan_print(&amp;myNdb) &gt; 0)
+    std::cout &lt;&lt; "scan_print: Success!" &lt;&lt; std::endl  &lt;&lt; std::endl;
+  
+  {
+    Car tmp1, tmp2;
+    sprintf(tmp1.color, "Blue");
+    sprintf(tmp2.color, "Black");
+    std::cout &lt;&lt; "Going to update all " &lt;&lt; tmp1.color 
+        &lt;&lt; " cars to " &lt;&lt; tmp2.color &lt;&lt; " cars!" &lt;&lt; std::endl;
+    if(scan_update(&amp;myNdb, column_color, tmp1.color, tmp2.color) &gt; 0) 
+      std::cout &lt;&lt; "scan_update: Success!" &lt;&lt; std::endl  &lt;&lt; std::endl;
+  }
+  if(scan_print(&amp;myNdb) &gt; 0)
+    std::cout &lt;&lt; "scan_print: Success!" &lt;&lt; std::endl  &lt;&lt; std::endl;
+
+  return 0;
+}
+</programlisting>
+
   </section>
 
   <section id="example-secondary-indexes">
 
     <title>Using Secondary Indexes in Scans</title>
 
-<!-- <abstract> -->
+    <abstract>
 
-    <para></para>
+      <para>
+        This program illustrates how to use secondary indexes in the NDB
+        API.
+      </para>
 
-<!-- </abstract> -->
+      <para>
+        The source code for this example may be found in the MySQL 5.1
+        source tree, in
+        <filename>storage/ndb/ndbapi-examples/ndbapi_simple_index/ndbapi_simple_index.cpp</filename>.
+      </para>
 
+    </abstract>
+
+    <para>
+      The correct output from this program is shown here:
+    </para>
+
+<programlisting>
+ATTR1 ATTR2
+0      10
+1       1
+2      12
+Detected that deleted tuple doesn't exist!
+4      14
+5       5
+6      16
+7       7
+8      18
+9       9
+</programlisting>
+
+<programlisting>
+
+/*
+ *  ndbapi_simple_index.cpp: Using secondary indexes in the NDB API.
+ */
+
+#include &lt;mysql.h&gt;
+#include &lt;NdbApi.hpp&gt;
+
+// Used for cout
+#include &lt;stdio.h&gt;
+#include &lt;iostream&gt;
+
+#define PRINT_ERROR(code,msg) \
+  std::cout &lt;&lt; "Error in " &lt;&lt; __FILE__ &lt;&lt; ", line: " &lt;&lt; __LINE__ \
+            &lt;&lt; ", code: " &lt;&lt; code \
+            &lt;&lt; ", msg: " &lt;&lt; msg &lt;&lt; "." &lt;&lt; std::endl
+#define MYSQLERROR(mysql) { \
+  PRINT_ERROR(mysql_errno(&amp;mysql),mysql_error(&amp;mysql)); \
+  exit(-1); }
+#define APIERROR(error) { \
+  PRINT_ERROR(error.code,error.message); \
+  exit(-1); }
+
+int main()
+{
+  ndb_init();
+  MYSQL mysql;
+
+  /**************************************************************
+   * Connect to the MySQL server and create the table           *
+   **************************************************************/
+  {
+    if ( !mysql_init(&amp;mysql) ) {
+      std::cout &lt;&lt; "mysql_init failed\n";
+      exit(-1);
+    }
+    if ( !mysql_real_connect(&amp;mysql, "localhost", "root", "", "",
+           3306, "/tmp/mysql.sock", 0) )
+      MYSQLERROR(mysql);
+
+    mysql_query(&amp;mysql, "CREATE DATABASE TEST_DB_1");
+    if (mysql_query(&amp;mysql, "USE TEST_DB_1") != 0) MYSQLERROR(mysql);
+
+    if (mysql_query(&amp;mysql, 
+        "CREATE TABLE"
+        "  MYTABLENAME"
+        "    (ATTR1 INT UNSIGNED,"
+        "     ATTR2 INT UNSIGNED NOT NULL,"
+        "     PRIMARY KEY USING HASH (ATTR1),"
+        "     UNIQUE MYINDEXNAME USING HASH (ATTR2))"
+        "  ENGINE=NDB"))
+      MYSQLERROR(mysql);
+  }
+
+  /**************************************************************
+   * Connect to the NDB cluster.                                *
+   **************************************************************/
+
+  Ndb_cluster_connection *cluster_connection=
+    new Ndb_cluster_connection(); // Object representing the cluster
+
+  if (cluster_connection-&gt;connect(5,3,1))
+  {
+    std::cout &lt;&lt; "Connect to cluster management server failed.\n";
+    exit(-1);
+  }
+
+  if (cluster_connection-&gt;wait_until_ready(30,30))
+  {
+    std::cout &lt;&lt; "Cluster was not ready within 30 seconds.\n";
+    exit(-1);
+  }
+
+  Ndb* myNdb = new Ndb( cluster_connection,
+      "TEST_DB_1" );  // Object representing the database.
+  if (myNdb-&gt;init() == -1) { 
+    APIERROR(myNdb-&gt;getNdbError());
+    exit(-1);
+  }
+
+  const NdbDictionary::Dictionary* myDict= myNdb-&gt;getDictionary();
+  const NdbDictionary::Table *myTable= myDict-&gt;getTable("MYTABLENAME");
+  if (myTable == NULL)
+    APIERROR(myDict-&gt;getNdbError());
+  const NdbDictionary::Index *myIndex= myDict-&gt;getIndex("MYINDEXNAME","MYTABLENAME");
+  if (myIndex == NULL)
+    APIERROR(myDict-&gt;getNdbError());
+
+/***********************************************
+ * Using 5 transactions, insert 10 tuples into *
+ * the table: (0,0),(1,1),...,(9,9)            *
+ ***********************************************/
+ 
+  for (int i = 0; i &lt; 5; i++) {
+    NdbTransaction *myTransaction= myNdb-&gt;startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb-&gt;getNdbError());
+    
+    NdbOperation *myOperation= myTransaction-&gt;getNdbOperation(myTable);
+    if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+    myOperation-&gt;insertTuple();
+    myOperation-&gt;equal("ATTR1", i);
+    myOperation-&gt;setValue("ATTR2", i);
+
+    myOperation = myTransaction-&gt;getNdbOperation(myTable);  
+    if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+
+    myOperation-&gt;insertTuple();
+    myOperation-&gt;equal("ATTR1", i+5);
+    myOperation-&gt;setValue("ATTR2", i+5);
+    
+    if (myTransaction-&gt;execute( NdbTransaction::Commit ) == -1)
+      APIERROR(myTransaction-&gt;getNdbError());
+    
+    myNdb-&gt;closeTransaction(myTransaction);
+  }
+  
+  /**********************************************
+   * Read and print all tuples using the index. *
+   **********************************************/
+  std::cout &lt;&lt; "ATTR1 ATTR2" &lt;&lt; std::endl;
+  
+  for (int i = 0; i &lt; 10; i++) {
+    NdbTransaction *myTransaction= myNdb-&gt;startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb-&gt;getNdbError());
+    
+    NdbIndexOperation *myIndexOperation=
+      myTransaction-&gt;getNdbIndexOperation(myIndex);
+    if (myIndexOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+    myIndexOperation-&gt;readTuple(NdbOperation::LM_Read);
+    myIndexOperation-&gt;equal("ATTR2", i);
+    
+    NdbRecAttr *myRecAttr= myIndexOperation-&gt;getValue("ATTR1", NULL);
+    if (myRecAttr == NULL) APIERROR(myTransaction-&gt;getNdbError());
+
+    if(myTransaction-&gt;execute( NdbTransaction::Commit ) != -1)
+      printf(" %2d    %2d\n", myRecAttr-&gt;u_32_value(), i);
+
+    myNdb-&gt;closeTransaction(myTransaction);
+  }
+
+  /******************************************************************
+   * Update the second attribute in half of the tuples (adding 10). *
+   ******************************************************************/
+   
+  for (int i = 0; i &lt; 10; i+=2) {
+    NdbTransaction *myTransaction= myNdb-&gt;startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb-&gt;getNdbError());
+    
+    NdbIndexOperation *myIndexOperation=
+      myTransaction-&gt;getNdbIndexOperation(myIndex);
+    if (myIndexOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+    myIndexOperation-&gt;updateTuple();
+    myIndexOperation-&gt;equal( "ATTR2", i );
+    myIndexOperation-&gt;setValue( "ATTR2", i+10);
+    
+    if( myTransaction-&gt;execute( NdbTransaction::Commit ) == -1 ) 
+      APIERROR(myTransaction-&gt;getNdbError());
+    
+    myNdb-&gt;closeTransaction(myTransaction);
+  }
+    
+/**********************************************************
+ * Delete one tuple (the one whose primary key equals 3). *
+ **********************************************************/
+ 
+  {
+    NdbTransaction *myTransaction= myNdb-&gt;startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb-&gt;getNdbError());
+  
+    NdbIndexOperation *myIndexOperation=
+      myTransaction-&gt;getNdbIndexOperation(myIndex);
+    if (myIndexOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+  
+    myIndexOperation-&gt;deleteTuple();
+    myIndexOperation-&gt;equal( "ATTR2", 3 );
+  
+    if (myTransaction-&gt;execute(NdbTransaction::Commit) == -1) 
+      APIERROR(myTransaction-&gt;getNdbError());
+  
+    myNdb-&gt;closeTransaction(myTransaction);
+  }
+
+  /**********************************
+   * Read and print out all tuples. *
+   **********************************/
+  {
+    std::cout &lt;&lt; "ATTR1 ATTR2" &lt;&lt; std::endl;
+  
+    for (int i = 0; i &lt; 10; i++) {
+      NdbTransaction *myTransaction= myNdb-&gt;startTransaction();
+      if (myTransaction == NULL) APIERROR(myNdb-&gt;getNdbError());
+      
+      NdbOperation *myOperation= myTransaction-&gt;getNdbOperation(myTable);
+      if (myOperation == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+      myOperation-&gt;readTuple(NdbOperation::LM_Read);
+      myOperation-&gt;equal("ATTR1", i);
+    
+      NdbRecAttr *myRecAttr= myOperation-&gt;getValue("ATTR2", NULL);
+      if (myRecAttr == NULL) APIERROR(myTransaction-&gt;getNdbError());
+    
+      if(myTransaction-&gt;execute( NdbTransaction::Commit ) == -1)
+  if (i == 3) {
+    std::cout &lt;&lt; "Detected that deleted tuple doesn't exist!\n";
+  } else {
+    APIERROR(myTransaction-&gt;getNdbError());
+  }
+    
+      if (i != 3) {
+  printf(" %2d    %2d\n", i, myRecAttr-&gt;u_32_value());
+      }
+      myNdb-&gt;closeTransaction(myTransaction);
+    }
+  }
+
+  /*******************
+   * Drop the table. *
+   *******************/
+   
+  if (mysql_query(&amp;mysql, "DROP TABLE MYTABLENAME"))
+    MYSQLERROR(mysql);
+
+  delete myNdb;
+  delete cluster_connection;
+
+  ndb_end(0);
+  return 0;
+}
+</programlisting>
+
   </section>
 
   <section id="example-event-handling">
 
     <title>NDB API Event Handling Example</title>
 
-<!-- <abstract> -->
+    <abstract>
 
-    <para></para>
+      <para>
+        This example demonstrates NDB API event handling.
+      </para>
 
-<!-- </abstract> -->
+      <para>
+        The source code for this program may be found in the MySQL 5.1
+        source tree, in the file
+        <filename>storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event.cpp</filename>.
+      </para>
 
+    </abstract>
+
+<programlisting>
+/*
+ *  ndbapi_event.cpp: Illustrates event handling in the NDB API.
+ */
+#include &lt;NdbApi.hpp&gt;
+
+// Used for cout.
+#include &lt;stdio.h&gt;
+#include &lt;iostream&gt;
+#include &lt;unistd.h&gt;
+#ifdef VM_TRACE
+#include &lt;my_global.h&gt;
+#endif
+#ifndef assert
+#include &lt;assert.h&gt;
+#endif
+
+
+#define APIERROR(error) \
+  { std::cout &lt;&lt; "Error in " &lt;&lt; __FILE__ &lt;&lt; ", line:" &lt;&lt; __LINE__ &lt;&lt; ", code:" \
+              &lt;&lt; error.code &lt;&lt; ", msg: " &lt;&lt; error.message &lt;&lt; "." &lt;&lt; std::endl; \
+    exit(-1); }
+
+int myCreateEvent(Ndb* myNdb,
+      const char *eventName,
+      const char *eventTableName,
+      const char **eventColumnName,
+      const int noEventColumnName,
+                  bool merge_events);
+
+int main(int argc, char** argv)
+{
+  ndb_init();
+  bool merge_events = argc &gt; 1 &amp;&amp; strchr(argv[1], 'm') != 0;
+#ifdef VM_TRACE
+  bool dbug = argc &gt; 1 &amp;&amp; strchr(argv[1], 'd') != 0;
+  if (dbug) DBUG_PUSH("d:t:");
+  if (dbug) putenv("API_SIGNAL_LOG=-");
+#endif
+
+  Ndb_cluster_connection *cluster_connection=
+    new Ndb_cluster_connection(); // Object representing the cluster.
+
+  int r= cluster_connection-&gt;connect(
+             5 /* retries               */,
+             3 /* delay between retries */,
+             1 /* verbose               */);
+  if (r &gt; 0)
+  {
+    std::cout
+      &lt;&lt; "Cluster connect failed, possibly resolved with more retries.\n";
+    exit(-1);
+  }
+  else if (r &lt; 0)
+  {
+    std::cout
+      &lt;&lt; "Cluster connect failed.\n";
+    exit(-1);
+  }
+             
+  if (cluster_connection-&gt;wait_until_ready(30,30))
+  {
+    std::cout &lt;&lt; "Cluster was not ready within 30 seconds." &lt;&lt; std::endl;
+    exit(-1);
+  }
+
+  Ndb* myNdb= new Ndb(cluster_connection,
+          "TEST_DB");  // Object representing the database.
+
+  if (myNdb-&gt;init() == -1) APIERROR(myNdb-&gt;getNdbError());
+
+  const char *eventName= "CHNG_IN_t0";
+  const char *eventTableName= "t0";
+  const int noEventColumnName= 5;
+  const char *eventColumnName[noEventColumnName]=
+    {"c0",
+     "c1",
+     "c2",
+     "c3",
+     "c4"
+    };
+  
+  // Create events.
+  myCreateEvent(myNdb,
+    eventName,
+    eventTableName,
+    eventColumnName,
+    noEventColumnName,
+                merge_events);
+
+  // Normal values and blobs are unfortunately handled differently..
+  typedef union { NdbRecAttr* ra; NdbBlob* bh; } RA_BH;
+
+  int i, j, k, l;
+  j = 0;
+  while (j &lt; 99) {
+
+    // Start "transaction" for handling events.
+    NdbEventOperation* op;
+    printf("Create EventOperation...\n");
+    if ((op = myNdb-&gt;createEventOperation(eventName)) == NULL)
+      APIERROR(myNdb-&gt;getNdbError());
+    op-&gt;mergeEvents(merge_events);
+
+    printf("Get values...\n");
+    RA_BH recAttr[noEventColumnName];
+    RA_BH recAttrPre[noEventColumnName];
+    // Primary keys should always be a part of the result.
+    for (i = 0; i &lt; noEventColumnName; i++) {
+      if (i &lt; 4) {
+        recAttr[i].ra    = op-&gt;getValue(eventColumnName[i]);
+        recAttrPre[i].ra = op-&gt;getPreValue(eventColumnName[i]);
+      } else if (merge_events) {
+        recAttr[i].bh    = op-&gt;getBlobHandle(eventColumnName[i]);
+        recAttrPre[i].bh = op-&gt;getPreBlobHandle(eventColumnName[i]);
+      }
+    }
+
+    // Set up the callbacks.
+    printf("Execute...\n");
+    // This causes the changes to start "flowing".
+    if (op-&gt;execute())
+      APIERROR(op-&gt;getNdbError());
+
+    NdbEventOperation* the_op = op;
+
+    i= 0;
+    while (i &lt; 40) {
+      // printf("Now waiting for event...\n");
+      int r = myNdb-&gt;pollEvents(1000);  //  Wait for the event (maximum 
+                                        //  1000 ms).
+      if (r &gt; 0) {
+  // printf("Got data! %d\n", r);
+  while ((op= myNdb-&gt;nextEvent())) {
+          assert(the_op == op);
+    i++;
+    switch (op-&gt;getEventType()) {
+    case NdbDictionary::Event::TE_INSERT:
+      printf("%u INSERT", i);
+      break;
+    case NdbDictionary::Event::TE_DELETE:
+      printf("%u DELETE", i);
+      break;
+    case NdbDictionary::Event::TE_UPDATE:
+      printf("%u UPDATE", i);
+      break;
+    default:
+      abort(); // This should not happen.
+    }
+          printf(" gci=%d\n", (int)op-&gt;getGCI());
+          for (k = 0; k &lt;= 1; k++) {
+            printf(k == 0 ? "post: " : "pre : ");
+            for (l = 0; l &lt; noEventColumnName; l++) {
+              if (l &lt; 4) {
+                NdbRecAttr* ra = k == 0 ? recAttr[l].ra : recAttrPre[l].ra;
+                if (ra-&gt;isNULL() &gt;= 0) { // We have a value.
+                  if (ra-&gt;isNULL() == 0) { // We have a non-null value.
+                    if (l &lt; 2)
+                      printf("%-5u", ra-&gt;u_32_value());
+                    else
+                      printf("%-5.4s", ra-&gt;aRef());
+                  } else
+                    printf("%-5s", "NULL");
+                } else
+                  printf("%-5s", "-"); // no value
+              } else if (merge_events) {
+                int isNull;
+                NdbBlob* bh = k == 0 ? recAttr[l].bh : recAttrPre[l].bh;
+                bh-&gt;getDefined(isNull);
+                if (isNull &gt;= 0) { // We have a value.
+                  if (! isNull) { // We have a non-null value.
+                    Uint64 length = 0;
+                    bh-&gt;getLength(length);
+                    // Read into buffer.
+                    unsigned char* buf = new unsigned char [length];
+                    memset(buf, 'X', length);
+                    Uint32 n = length;
+                    bh-&gt;readData(buf, n); // n is in/out.
+                    assert(n == length);
+                    // pretty-print
+                    bool first = true;
+                    Uint32 i = 0;
+                    while (i &lt; n) {
+                      unsigned char c = buf[i++];
+                      Uint32 m = 1;
+                      while (i &lt; n &amp;&amp; buf[i] == c)
+                        i++, m++;
+                      if (! first)
+                        printf("+");
+                      printf("%u%c", m, c);
+                      first = false;
+                    }
+                    printf("[%u]", n);
+                    delete [] buf;
+                  } else
+                    printf("%-5s", "NULL");
+                } else
+                  printf("%-5s", "-"); // No value.
+              }
+            }
+            printf("\n");
+          }
+  }
+      } else
+  ;  //  printf("Timed out.\n");
+    }
+    // We don't want to listen to events anymore...
+    if (myNdb-&gt;dropEventOperation(the_op)) APIERROR(myNdb-&gt;getNdbError());
+    the_op = 0;
+
+    j++;
+  }
+
+  {
+    NdbDictionary::Dictionary *myDict = myNdb-&gt;getDictionary();
+    if (!myDict) APIERROR(myNdb-&gt;getNdbError());
+    // Remove the event from the database.
+    if (myDict-&gt;dropEvent(eventName)) APIERROR(myDict-&gt;getNdbError());
+  }
+
+  delete myNdb;
+  delete cluster_connection;
+  ndb_end(0);
+  return 0;
+}
+
+int myCreateEvent(Ndb* myNdb,
+      const char *eventName,
+      const char *eventTableName,
+      const char **eventColumnNames,
+      const int noEventColumnNames,
+                  bool merge_events)
+{
+  NdbDictionary::Dictionary *myDict= myNdb-&gt;getDictionary();
+  if (!myDict) APIERROR(myNdb-&gt;getNdbError());
+
+  const NdbDictionary::Table *table= myDict-&gt;getTable(eventTableName);
+  if (!table) APIERROR(myDict-&gt;getNdbError());
+
+  NdbDictionary::Event myEvent(eventName, *table);
+  myEvent.addTableEvent(NdbDictionary::Event::TE_ALL); 
+  //  myEvent.addTableEvent(NdbDictionary::Event::TE_INSERT); 
+  //  myEvent.addTableEvent(NdbDictionary::Event::TE_UPDATE); 
+  //  myEvent.addTableEvent(NdbDictionary::Event::TE_DELETE);
+
+  myEvent.addEventColumns(noEventColumnNames, eventColumnNames);
+  myEvent.mergeEvents(merge_events);
+
+  // Add an event to the database.
+  if (myDict-&gt;createEvent(myEvent) == 0)
+    myEvent.print();
+  else if (myDict-&gt;getNdbError().classification ==
+     NdbError::SchemaObjectExists) {
+    printf("Event creation failed; event already exists.\n");
+    printf("Dropping event...\n");
+    if (myDict-&gt;dropEvent(eventName)) APIERROR(myDict-&gt;getNdbError());
+    // Try again...
+    // Add the event to the database.
+    if ( myDict-&gt;createEvent(myEvent)) APIERROR(myDict-&gt;getNdbError());
+  } else
+    APIERROR(myDict-&gt;getNdbError());
+
+  return 0;
+}
+</programlisting>
+
   </section>
 
 </chapter>

Thread
svn commit - mysqldoc@docsrva: r1960 - trunk/ndbapijon28 Apr