diff options
Diffstat (limited to 'storage/ndb/include/ndbapi/Ndb.hpp')
-rw-r--r-- | storage/ndb/include/ndbapi/Ndb.hpp | 1700 |
1 files changed, 1700 insertions, 0 deletions
diff --git a/storage/ndb/include/ndbapi/Ndb.hpp b/storage/ndb/include/ndbapi/Ndb.hpp new file mode 100644 index 00000000000..41085e6e06a --- /dev/null +++ b/storage/ndb/include/ndbapi/Ndb.hpp @@ -0,0 +1,1700 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @mainpage NDB API Programmers' Guide + + This guide assumes a basic familiarity with MySQL Cluster concepts found + on http://dev.mysql.com/doc/mysql/en/NDBCluster.html . + Some of the fundamental ones are also described in section @ref secConcepts. + + The NDB API is a MySQL Cluster application interface + that implements transactions. + The NDB API consists of the following fundamental classes: + - Ndb_cluster_connection, representing a connection to a cluster, + - Ndb is the main class, representing a connection to a database, + - NdbTransaction represents a transaction, + - NdbOperation represents an operation using a primary key, + - NdbScanOperation represents an operation performing a full table scan. + - NdbIndexOperation represents an operation using a unique hash index, + - NdbIndexScanOperation represents an operation performing a scan using + an ordered index, + - NdbRecAttr represents an attribute value + - NdbDictionary represents meta information about tables and attributes. + + In addition, the NDB API defines a structure NdbError, which contains the + specification for an error. + + It is also possible to receive "events" triggered when data in the database in changed. + This is done through the NdbEventOperation class. + + There are also some auxiliary classes, which are listed in the class hierarchy. + + The main structure of an application program is as follows: + -# Connect to a cluster using the Ndb_cluster_connection + object. + -# Initiate a database connection by constructing and initialising one or more Ndb objects. + -# Define and execute transactions using the NdbTransaction class. + -# Delete Ndb objects. + -# Terminate the connection to the cluster (terminate instance of Ndb_cluster_connection). + + The procedure for using transactions is as follows: + -# Start transaction (instantiate an NdbTransaction object) + -# Add and define operations associated with the transaction using instances of one or more of the + NdbOperation, NdbScanOperation, NdbIndexOperation, and NdbIndexScanOperation classes + -# Execute transaction (call NdbTransaction::execute()) + + The operation can be of two different types, + <var>Commit</var> or <var>NoCommit</var>. + If the operation is of type <var>NoCommit</var>, + then the application program executes the operation part of a transaction, + but without actually committing the transaction. + After executing a <var>NoCommit</var> operation, the program can continue + to add and define more operations to the transaction + for later execution. + + If the operation is of type <var>Commit</var>, then the transaction is + immediately committed. The transaction <em>must</em> be closed after it has been + commited (event if commit fails), and no further addition or definition of + operations for this transaction is allowed. + + @section secSync Synchronous Transactions + + Synchronous transactions are defined and executed as follows: + + -# Start (create) the transaction, which is + referenced by an NdbTransaction object + (typically created using Ndb::startTransaction()). + At this point, the transaction is only being defined, + and is not yet sent to the NDB kernel. + -# Define operations and add them to the transaction, using one or more of + - NdbTransaction::getNdbOperation() + - NdbTransaction::getNdbScanOperation() + - NdbTransaction::getNdbIndexOperation() + - NdbTransaction::getNdbIndexScanOperation() + along with the appropriate methods of the respective NdbOperation class + (or one possiblt one or more of its subclasses). + Note that the transaction has still not yet been sent to the NDB kernel. + -# Execute the transaction, using the NdbTransaction::execute() method. + -# Close the transaction (call Ndb::closeTransaction()). + + For an example of this process, see the program listing in + @ref ndbapi_simple.cpp. + + To execute several parallel synchronous transactions, one can either + use multiple Ndb objects in several threads, or start multiple + application programs. + + @section secNdbOperations Operations + + A NdbTransaction consists of a list of operations, each of which is represented + by an instance of NdbOperation, NdbScanOperation, NdbIndexOperation, or + NdbIndexScanOperation. + + <h3>Single row operations</h3> + After the operation is created using NdbTransaction::getNdbOperation() + (or NdbTransaction::getNdbIndexOperation()), it is defined in the following + three steps: + -# Define the standard operation type, using NdbOperation::readTuple() + -# Specify search conditions, using NdbOperation::equal() + -# Specify attribute actions, using NdbOperation::getValue() + + Here are two brief examples illustrating this process. For the sake of + brevity, we omit error handling. + + This first example uses an NdbOperation: + @code + // 1. Retrieve table object + myTable= myDict->getTable("MYTABLENAME"); + + // 2. Create + myOperation= myTransaction->getNdbOperation(myTable); + + // 3. Define type of operation and lock mode + myOperation->readTuple(NdbOperation::LM_Read); + + // 4. Specify Search Conditions + myOperation->equal("ATTR1", i); + + // 5. Attribute Actions + myRecAttr= myOperation->getValue("ATTR2", NULL); + @endcode + For additional examples of this sort, see @ref ndbapi_simple.cpp. + + The second example uses an NdbIndexOperation: + @code + // 1. Retrieve index object + myIndex= myDict->getIndex("MYINDEX", "MYTABLENAME"); + + // 2. Create + myOperation= myTransaction->getNdbIndexOperation(myIndex); + + // 3. Define type of operation and lock mode + myOperation->readTuple(NdbOperation::LM_Read); + + // 4. Specify Search Conditions + myOperation->equal("ATTR1", i); + + // 5. Attribute Actions + myRecAttr = myOperation->getValue("ATTR2", NULL); + @endcode + Another example of this second type can be found in + @ref ndbapi_simple_index.cpp. + + We will now discuss in somewhat greater detail each step involved in the + creation and use of synchronous transactions. + + <h4>Step 1: Define single row operation type</h4> + The following operation types are supported: + -# NdbOperation::insertTuple() : + inserts a non-existing tuple + -# NdbOperation::writeTuple() : + updates an existing tuple if is exists, + otherwise inserts a new tuple + -# NdbOperation::updateTuple() : + updates an existing tuple + -# NdbOperation::deleteTuple() : + deletes an existing tuple + -# NdbOperation::readTuple() : + reads an existing tuple with specified lock mode + + All of these operations operate on the unique tuple key. + (When NdbIndexOperation is used then all of these operations + operate on a defined unique hash index.) + + @note If you want to define multiple operations within the same transaction, + then you need to call NdbTransaction::getNdbOperation() or + NdbTransaction::getNdbIndexOperation() for each operation. + + <h4>Step 2: Specify Search Conditions</h4> + The search condition is used to select tuples. Search conditions are set using NdbOperation::equal(). + + <h4>Step 3: Specify Attribute Actions</h4> + Next, it is necessary to determine which attributes should be read or updated. + It is important to remember that: + - Deletes can neither read nor set values, but only delete them + - Reads can only read values + - Updates can only set values + Normally the attribute is identified by name, but it is + also possible to use the attribute's identity to determine the + attribute. + + NdbOperation::getValue() returns an NdbRecAttr object + containing the read value. + To obtain the actual value, one of two methods can be used; + the application can either + - use its own memory (passed through a pointer aValue) to + NdbOperation::getValue(), or + - receive the attribute value in an NdbRecAttr object allocated + by the NDB API. + + The NdbRecAttr object is released when Ndb::closeTransaction() + is called. + Thus, the application cannot reference this object following + any subsequent call to Ndb::closeTransaction(). + Attempting to read data from an NdbRecAttr object before + calling NdbTransaction::execute() yields an undefined result. + + + @subsection secScan Scan Operations + + Scans are roughly the equivalent of SQL cursors, providing a means to + preform high-speed row processing. A scan can be performed + on either a table (using @ref NdbScanOperation) or + an ordered index (by means of an @ref NdbIndexScanOperation). + + Scan operations are characterised by the following: + - They can perform only reads (shared, exclusive or dirty) + - They can potentially work with multiple rows + - They can be used to update or delete multiple rows + - They can operate on several nodes in parallel + + After the operation is created using NdbTransaction::getNdbScanOperation() + (or NdbTransaction::getNdbIndexScanOperation()), + it is carried out in the following three steps: + -# Define the standard operation type, using NdbScanOperation::readTuples() + -# Specify search conditions, using @ref NdbScanFilter and/or + @ref NdbIndexScanOperation::setBound() + -# Specify attribute actions, using NdbOperation::getValue() + -# Executing the transaction, using NdbTransaction::execute() + -# Traversing the result set by means of succssive calls to + NdbScanOperation::nextResult() + + Here are two brief examples illustrating this process. Once again, in order + to keep things relatively short and simple, we will forego any error handling. + + This first example performs a table scan, using an NdbScanOperation: + @code + // 1. Retrieve table object + myTable= myDict->getTable("MYTABLENAME"); + + // 2. Create + myOperation= myTransaction->getNdbScanOperation(myTable); + + // 3. Define type of operation and lock mode + myOperation->readTuples(NdbOperation::LM_Read); + + // 4. Specify Search Conditions + NdbScanFilter sf(myOperation); + sf.begin(NdbScanFilter::OR); + sf.eq(0, i); // Return rows with column 0 equal to i or + sf.eq(1, i+1); // column 1 equal to (i+1) + sf.end(); + + // 5. Attribute Actions + myRecAttr= myOperation->getValue("ATTR2", NULL); + @endcode + + Our second example uses an NdbIndexScanOperation to perform an index scan: + @code + // 1. Retrieve index object + myIndex= myDict->getIndex("MYORDEREDINDEX", "MYTABLENAME"); + + // 2. Create + myOperation= myTransaction->getNdbIndexScanOperation(myIndex); + + // 3. Define type of operation and lock mode + myOperation->readTuples(NdbOperation::LM_Read); + + // 4. Specify Search Conditions + // All rows with ATTR1 between i and (i+1) + myOperation->setBound("ATTR1", NdbIndexScanOperation::BoundGE, i); + myOperation->setBound("ATTR1", NdbIndexScanOperation::BoundLE, i+1); + + // 5. Attribute Actions + myRecAttr = MyOperation->getValue("ATTR2", NULL); + @endcode + + Some additional discussion of each step required to perform a scan follows: + + <h4>Step 1: Define Scan Operation Type</h4> + It is important to remember that only a single operation is supported for each scan operation + (@ref NdbScanOperation::readTuples() or @ref NdbIndexScanOperation::readTuples()). + + @note If you want to define multiple scan operations within the same + transaction, then you need to call + NdbTransaction::getNdbScanOperation() or + NdbTransaction::getNdbIndexScanOperation() separately for <b>each</b> operation. + + <h4>Step 2: Specify Search Conditions</h4> + The search condition is used to select tuples. + If no search condition is specified, the scan will return all rows + in the table. + + The search condition can be an @ref NdbScanFilter (which can be used on both + @ref NdbScanOperation and @ref NdbIndexScanOperation) or bounds which + can only be used on index scans (@ref NdbIndexScanOperation::setBound()). + An index scan can use both NdbScanFilter and bounds. + + @note When NdbScanFilter is used, each row is examined, whether or not it is + actually returned. However, when using bounds, only rows within the bounds will be examined. + + <h4>Step 3: Specify Attribute Actions</h4> + + Next, it is necessary to define which attributes should be read. + As with transaction attributes, scan attributes are defined by name but it is + also possible to use the attributes' identities to define attributes. + + As previously discussed (see @ref secSync), the value read is returned as + an NdbRecAttr object by the NdbOperation::getValue() method. + + <h3>Using Scan to Update/Delete</h3> + Scanning can also be used to update or delete rows. + This is performed by + -# Scanning using exclusive locks (using NdbOperation::LM_Exclusive) + -# When iterating through the result set, for each row optionally calling + either NdbScanOperation::updateCurrentTuple() or + NdbScanOperation::deleteCurrentTuple() + -# (If performing NdbScanOperation::updateCurrentTuple():) + Setting new values for records simply by using @ref NdbOperation::setValue(). + NdbOperation::equal() should <em>not</em> be called in such cases, as the primary + key is retrieved from the scan. + + @note The actual update or delete will not be performed until the next + call to NdbTransaction::execute(), just as with single row operations. + NdbTransaction::execute() also must be called before any locks are released; + see @ref secScanLocks for more information. + + <h4>Features Specific to Index Scans</h4> + + When performing an index scan, it is possible to + scan only a subset of a table using @ref NdbIndexScanOperation::setBound(). + In addition, result sets can be sorted in either ascending or descending order, using + @ref NdbIndexScanOperation::readTuples(). Note that rows are returned unordered + by default, that is, unless <var>sorted</var> is set to <b>true</b>. + It is also important to note that, when using NdbIndexScanOperation::BoundEQ + on a partition key, only fragments containing rows will actually be scanned. + + @note When performing a sorted scan, any value passed as the + NdbIndexScanOperation::readTuples() method's <code>parallel</code> argument + will be ignored and maximum parallelism will be used instead. In other words, all + fragments which it is possible to scan will be scanned simultaneously and in parallel + in such cases. + + @subsection secScanLocks Lock handling with scans + + Performing scans on either a tables or an index has the potential + return a great many records; however, Ndb will lock only a predetermined + number of rows per fragment at a time. + How many rows will be locked per fragment is controlled by the + <var>batch</var> parameter passed to NdbScanOperation::readTuples(). + + In order to allow the application to handle how locks are released, + NdbScanOperation::nextResult() has a Boolean parameter <var>fetch_allow</var>. + If NdbScanOperation::nextResult() is called with <var>fetch_allow</var> equal to + <b>false</b>, then no locks may be released as result of the function call. + Otherwise the locks for the current batch may be released. + + This next example shows a scan delete that handle locks in an efficient manner. + For the sake of brevity, we omit error-handling. + @code + int check; + + // Outer loop for each batch of rows + while((check = MyScanOperation->nextResult(true)) == 0) + { + do + { + // Inner loop for each row within batch + MyScanOperation->deleteCurrentTuple(); + } while((check = MyScanOperation->nextResult(false)) == 0); + + // When no more rows in batch, exeute all defined deletes + MyTransaction->execute(NoCommit); + } + @endcode + + See @ref ndbapi_scan.cpp for a more complete example of a scan. + + @section secError Error Handling + + Errors can occur either when operations making up a transaction are being + defined, or when the transaction is actually being executed. Catching and + handling either sort of error requires testing the value returned by + NdbTransaction::execute(), and then, if an error is indicated (that is, + if this value is equal to -1), using the following two methods in order to + identify the error's type and location: + + - NdbTransaction::getNdbErrorOperation() returns a reference to the + operation causing the most recent error. + - NdbTransaction::getNdbErrorLine() yields the method number of the + erroneous method in the operation. + + This short example illustrates how to detect an error and to use these + two methods to identify it: + + @code + theTransaction = theNdb->startTransaction(); + theOperation = theTransaction->getNdbOperation("TEST_TABLE"); + if (theOperation == NULL) goto error; + theOperation->readTuple(NdbOperation::LM_Read); + theOperation->setValue("ATTR_1", at1); + theOperation->setValue("ATTR_2", at1); // Error occurs here + theOperation->setValue("ATTR_3", at1); + theOperation->setValue("ATTR_4", at1); + + if (theTransaction->execute(Commit) == -1) { + errorLine = theTransaction->getNdbErrorLine(); + errorOperation = theTransaction->getNdbErrorOperation(); + } + @endcode + + Here <code>errorLine</code> will be 3, as the error occurred in the + third method called on the NdbOperation object (in this case, + <code>theOperation</code>); if the result of + NdbTransaction::getNdbErrorLine() is 0, this means that the error + occurred when the operations were executed. In this example, + <code>errorOperation</code> will be a pointer to the <code>theOperation</code> + object. The NdbTransaction::getNdbError() method returns an NdbError + object providing information about the error. + + @note Transactions are <b>not</b> automatically closed when an error occurs. Call + Ndb::closeTransaction() to close the transaction. + + One recommended way to handle a transaction failure + (i.e. an error is reported) is to: + -# Rollback transaction (call NdbTransaction::execute() with a special parameter) + -# Close transaction (call NdbTransaction::closeTransaction()) + -# If the error was temporary, attempt to restart the transaction + + Several errors can occur when a transaction contains multiple + operations which are simultaneously executed. + In this case the application has to go through all operations + and query their NdbError objects to find out what really happened. + + It is also important to note that errors can occur even when a commit is + reported as successful. In order to handle such situations, the NDB API + provides an additional NdbTransaction::commitStatus() method to check the + transactions's commit status. + +******************************************************************************/ + +/** + * @page ndbapi_simple.cpp ndbapi_simple.cpp + * @include ndbapi_simple.cpp + */ + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + * @page ndbapi_async.cpp ndbapi_async.cpp + * @include ndbapi_async.cpp + */ +/** + * @page ndbapi_async1.cpp ndbapi_async1.cpp + * @include ndbapi_async1.cpp + */ +#endif + +/** + * @page ndbapi_retries.cpp ndbapi_retries.cpp + * @include ndbapi_retries.cpp + */ + +/** + * @page ndbapi_simple_index.cpp ndbapi_simple_index.cpp + * @include ndbapi_simple_index.cpp + */ + +/** + * @page ndbapi_scan.cpp ndbapi_scan.cpp + * @include ndbapi_scan.cpp + */ + +/** + * @page ndbapi_event.cpp ndbapi_event.cpp + * @include ndbapi_event.cpp + */ + + +/** + @page secAdapt Adaptive Send Algorithm + + At the time of "sending" a transaction + (using NdbTransaction::execute()), the transactions + are in reality <em>not</em> immediately transfered to the NDB Kernel. + Instead, the "sent" transactions are only kept in a + special send list (buffer) in the Ndb object to which they belong. + The adaptive send algorithm decides when transactions should + actually be transferred to the NDB kernel. + + The NDB API is designed as a multi-threaded interface and so + it is often desirable to transfer database operations from more than + one thread at a time. + The NDB API keeps track of which Ndb objects are active in transferring + information to the NDB kernel and the expected amount of threads to + interact with the NDB kernel. + Note that a given instance of Ndb should be used in at most one thread; + different threads should <em>not</em> use the same Ndb object. + + There are four conditions leading to the transfer of database + operations from Ndb object buffers to the NDB kernel: + -# The NDB Transporter (TCP/IP, OSE, SCI or shared memory) + decides that a buffer is full and sends it off. + The buffer size is implementation-dependent and + may change between MySQL Cluster releases. + On TCP/IP the buffer size is usually around 64 KB; + on OSE/Delta it is usually less than 2000 bytes. + Since each Ndb object provides a single buffer per storage node, + the notion of a "full" buffer is local to this storage node. + -# The accumulation of statistical data on transferred information + may force sending of buffers to all storage nodes. + -# Every 10 ms, a special transmission thread checks whether or not + any send activity has occurred. If not, then the thread will + force transmission to all nodes. + This means that 20 ms is the maximum time database operations + are kept waiting before being sent off. The 10-millisecond limit + is likely to become a configuration parameter in + future releases of MySQL Cluster; however, for checks that + are more frequent than each 10 ms, + additional support from the operating system is required. + -# For methods that are affected by the adaptive send alorithm + (such as NdbTransaction::execute()), there is a <var>force</var> + parameter + that overrides its default behaviour in this regard and forces + immediate transmission to all nodes. See the inidvidual NDB API class + listings for more information. + + @note The conditions listed above are subject to change in future releases + of MySQL Cluster. +*/ + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + + For each of these "sent" transactions, there are three + possible states: + -# Waiting to be transferred to NDB Kernel. + -# Has been transferred to the NDB Kernel and is currently + being processed. + -# Has been transferred to the NDB Kernel and has + finished processing. + Now it is waiting for a call to a poll method. + (When the poll method is invoked, + then the transaction callback method will be executed.) + + The poll method invoked (either Ndb::pollNdb() or Ndb::sendPollNdb()) + will return when: + -# at least 'minNoOfEventsToWakeup' of the transactions + in the send list have transitioned to state 3 as described above, and + -# all of these transactions have executed their callback methods. +*/ +#endif + +/** + @page secConcepts MySQL Cluster Concepts + + The <em>NDB Kernel</em> is the collection of storage nodes + belonging to a MySQL Cluster. + The application programmer can for most purposes view the + set of all storage nodes as a single entity. + Each storage node is made up of three main components: + - TC : The transaction co-ordinator + - ACC : Index storage component + - TUP : Data storage component + + When an application program executes a transaction, + it connects to one transaction co-ordinator on one storage node. + Usually, the programmer does not need to specify which TC should be used, + but in some cases when performance is important, the programmer can + provide "hints" to use a certain TC. + (If the node with the desired transaction co-ordinator is down, then another TC will + automatically take over the work.) + + Every storage node has an ACC and a TUP which store + the indexes and data portions of the database table fragment. + Even though one TC is responsible for the transaction, + several ACCs and TUPs on other storage nodes might be involved in the + execution of the transaction. + + + @section secNdbKernelConnection Selecting a Transaction Co-ordinator + + The default method is to select the transaction co-ordinator (TC) determined to be + the "closest" storage node, using a heuristic for proximity based on + the type of transporter connection. In order of closest to most distant, these are + - SCI + - SHM + - TCP/IP (localhost) + - TCP/IP (remote host) + If there are several connections available with the same proximity, they will each be + selected in a round robin fashion for every transaction. Optionally + one may set the method for TC selection to round-robin mode, where each new set of + transactions is placed on the next DB node. The pool of connections from which this + selection is made consists of all available connections. + + As noted previously, the application programmer can provide hints to the NDB API as to + which transaction co-ordinator it should use. This is done by + providing a <em>table</em> and <em>partition key</em> + (usually the primary key). + By using the primary key as the partition key, + the transaction will be placed on the node where the primary replica + of that record resides. + Note that this is only a hint; the system can be + reconfigured at any time, in which case the NDB API will choose a transaction + co-ordinator without using the hint. + For more information, see NdbDictionary::Column::getPartitionKey() and + Ndb::startTransaction(). The application programmer can specify + the partition key from SQL by using the construct, + <code>CREATE TABLE ... ENGINE=NDB PARTITION BY KEY (<var>attribute-list</var>);</code>. + + + @section secRecordStruct NDB Record Structure + The NDB Cluster engine used by MySQL Cluster is a relational database engine + storing records in tables just as with any other RDBMS. + Table rows represent records as tuples of relational data. + When a new table is created, its attribute schema is specified for the table as a whole, + and thus each record of the table has the same structure. Again, this is typical + of relational databases, and NDB is no different in this regard. + + + @subsection secKeys Primary Keys + Each record has from 1 up to 32 attributes which belong + to the primary key of the table. + + @section secTrans Transactions + + Transactions are committed first to main memory, + and then to disk after a global checkpoint (GCP) is issued. + Since all data is (in most NDB Cluster configurations) + synchronously replicated and stored on multiple NDB nodes, + the system can still handle processor failures without loss + of data. + However, in the case of a system failure (e.g. the whole system goes down), + then all (committed or not) transactions occurring since the latest GCP are lost. + + + @subsection secConcur Concurrency Control + NDB Cluster uses pessimistic concurrency control based on locking. + If a requested lock (implicit and depending on database operation) + cannot be attained within a specified time, + then a timeout error occurs. + + Concurrent transactions as requested by parallel application programs and + thread-based applications can sometimes deadlock when they try to access + the same information simultaneously. + Thus, applications need to be written in a manner so that timeout errors + occurring due to such deadlocks are handled gracefully. This generally + means that the transaction encountering a timeout should be rolled back + and restarted. + + + @section secHint Hints and Performance + + Placing the transaction co-ordinator in close proximity + to the actual data used in the transaction can in many cases + improve performance significantly. This is particularly true for + systems using TCP/IP. For example, a Solaris system using a single 500 MHz processor + has a cost model for TCP/IP communication which can be represented by the formula + + <code>[30 microseconds] + ([100 nanoseconds] * [<var>number of bytes</var>])</code> + + This means that if we can ensure that we use "popular" links we increase + buffering and thus drastically reduce the communication cost. + The same system using SCI has a different cost model: + + <code>[5 microseconds] + ([10 nanoseconds] * [<var>number of bytes</var>])</code> + + Thus, the efficiency of an SCI system is much less dependent on selection of + transaction co-ordinators. + Typically, TCP/IP systems spend 30-60% of their working time on communication, + whereas for SCI systems this figure is closer to 5-10%. + Thus, employing SCI for data transport means that less care from the NDB API + programmer is required and greater scalability can be achieved, even for + applications using data from many different parts of the database. + + A simple example is an application that uses many simple updates where + a transaction needs to update one record. + This record has a 32 bit primary key, + which is also the partition key. + Then the keyData will be the address of the integer + of the primary key and keyLen will be 4. +*/ + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + (A transaction's execution can also be divided into three + steps: prepare, send, and poll. This allows us to perform asynchronous + transactions. More about this later.) +*/ +#endif +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + Another way to execute several parallel transactions is to use + asynchronous transactions. +*/ +#endif +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + Operations are of two different kinds: + -# standard operations, and + -# interpreted program operations. +*/ +#endif +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + <h3>Interpreted Program Operations</h3> + The following types of interpreted program operations exist: + -# NdbOperation::interpretedUpdateTuple : + updates a tuple using an interpreted program + -# NdbOperation::interpretedDeleteTuple : + delete a tuple using an interpreted program + + The operations interpretedUpdateTuple and interpretedDeleteTuple both + work using the unique tuple key. + + These <em>interpreted programs</em> + make it possible to perform computations + inside the NDB Cluster Kernel instead of in the application + program. + This is sometimes very effective, since no intermediate results + are sent to the application, only the final result. + + + <h3>Interpreted Update and Delete</h3> + + Operations for interpreted updates and deletes must follow a + certain order when defining operations on a tuple. + As for read and write operations, + one must first define the operation type and then the search key. + -# The first step is to define the initial readings. + In this phase it is only allowed to use the + NdbOperation::getValue method. + This part might be empty. + -# The second step is to define the interpreted part. + The methods supported are the methods listed below except + NdbOperation::def_subroutine and NdbOperation::ret_sub + which can only be used in a subroutine. + NdbOperation::incValue and NdbOperation::subValue + increment and decrement attributes + (currently only unsigned integers supported). + This part can also be empty since interpreted updates + can be used for reading and updating the same tuple. + <p> + Even though getValue and setValue are not really interpreted + program instructions, it is still allowed to use them as + the last instruction of the program. + (If a getValue or setValue is found when an interpret_exit_ok + could have been issued then the interpreted_exit_ok + will be inserted. + A interpret_exit_ok should be viewed as a jump to the first + instruction after the interpreted instructions.) + -# The third step is to define all updates without any + interpreted program instructions. + Here a set of NdbOperation::setValue methods are called. + There might be zero such calls. + -# The fourth step is the final readings. + The initial readings reads the initial value of attributes + and the final readings reads them after their updates. + There might be zero NdbOperation::getValue calls. + -# The fifth step is possible subroutine definitions using + NdbOperation::def_subroutine and NdbOperation::ret_sub. +*/ +#endif +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + <h3>Interpreted Programs</h3> + Interpretation programs are executed in a + register-based virtual machine. + The virtual machine has eight 64 bit registers numbered 0-7. + Each register contains type information which is used both + for type conversion and for type checking. + + @note Arrays are currently <b>not</b> supported in the virtual machine. + Currently only unsigned integers are supported and of size + maximum 64 bits. + + All errors in the interpretation program will cause a + transaction abort, but will not affect any other transactions. + + The following are legal interpreted program instructions: + -# incValue : Add to an attribute + -# subValue : Subtract from an attribute + -# def_label : Define a label in the interpreted program + -# add_reg : Add two registers + -# sub_reg : Subtract one register from another + -# load_const_u32 : Load an unsigned 32 bit value into a register + -# load_const_u64 : Load an unsigned 64 bit value into a register + -# load_const_null : Load a NULL value into a register + -# read_attr : Read attribute value into a register + -# write_attr : Write a register value into an attribute + -# branch_ge : Compares registers and possibly jumps to specified label + -# branch_gt : Compares registers and possibly jumps to specified label + -# branch_le : Compares registers and possibly jumps to specified label + -# branch_lt : Compares registers and possibly jumps to specified label + -# branch_eq : Compares registers and possibly jumps to specified label + -# branch_ne : Compares registers and possibly jumps to specified label + -# branch_ne_null : Jumps if register does not contain NULL value + -# branch_eq_null : Jumps if register contains NULL value + -# branch_label : Unconditional jump to label + -# interpret_exit_ok : Exit interpreted program + (approving tuple if used in scan) + -# interpret_exit_nok : Exit interpreted program + (disqualifying tuple if used in scan) + + There are also three instructions for subroutines, which + are described in the next section. + + @subsection subsubSub Interpreted Programs: Subroutines + + The following are legal interpreted program instructions for + subroutines: + -# NdbOperation::def_subroutine : + Defines start of subroutine in interpreted program code + -# NdbOperation::call_sub : + Calls a subroutine + -# NdbOperation::ret_sub : + Return from subroutine + + The virtual machine executes subroutines using a stack for + its operation. + The stack allows for up to 24 subroutine calls in succession. + Deeper subroutine nesting will cause an abort of the transaction. + + All subroutines starts with the instruction + NdbOperation::def_subroutine and ends with the instruction + NdbOperation::ret_sub. + If it is necessary to return earlier in the subroutine + it has to be done using a branch_label instruction + to a label defined right before the + NdbOperation::ret_sub instruction. + + @note The subroutines are automatically numbered starting with 0. + The parameter used by NdbOperation::def_subroutine + should match the automatic numbering to make it easier to + debug the interpreted program. +*/ +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +/** + @section secAsync Asynchronous Transactions + The asynchronous interface is used to increase the speed of + transaction executing by better utilizing the connection + between the application and the NDB Kernel. + The interface is used to send many transactions + at the same time to the NDB kernel. + This is often much more efficient than using synchronous transactions. + The main reason for using this method is to ensure that + Sending many transactions at the same time ensures that bigger + chunks of data are sent when actually sending and thus decreasing + the operating system overhead. + + The synchronous call to NdbTransaction::execute + normally performs three main steps:<br> + -# <b>Prepare</b> + Check transaction status + - if problems, abort the transaction + - if ok, proceed + -# <b>Send</b> + Send the defined operations since last execute + or since start of transaction. + -# <b>Poll</b> + Wait for response from NDB kernel. + + The asynchronous method NdbTransaction::executeAsynchPrepare + only perform step 1. + (The abort part in step 1 is only prepared for. The actual + aborting of the transaction is performed in a later step.) + + Asynchronous transactions are defined and executed + in the following way. + -# Start (create) transactions (same way as for the + synchronous transactions) + -# Add and define operations (also as in the synchronous case) + -# <b>Prepare</b> transactions + (using NdbTransaction::executeAsynchPrepare or + NdbTransaction::executeAsynch) + -# <b>Send</b> transactions to NDB Kernel + (using Ndb::sendPreparedTransactions, + NdbTransaction::executeAsynch, or Ndb::sendPollNdb) + -# <b>Poll</b> NDB kernel to find completed transactions + (using Ndb::pollNdb or Ndb::sendPollNdb) + -# Close transactions (same way as for the synchronous transactions) + + See example program in section @ref ndbapi_example2.cpp. + + This prepare-send-poll protocol actually exists in four variants: + - (Prepare-Send-Poll). This is the one-step variant provided + by synchronous transactions. + - (Prepare-Send)-Poll. This is the two-step variant using + NdbTransaction::executeAsynch and Ndb::pollNdb. + - Prepare-(Send-Poll). This is the two-step variant using + NdbTransaction::executeAsynchPrepare and Ndb::sendPollNdb. + - Prepare-Send-Poll. This is the three-step variant using + NdbTransaction::executeAsynchPrepare, Ndb::sendPreparedTransactions, and + Ndb::pollNdb. + + Transactions first has to be prepared by using method + NdbTransaction::executeAsynchPrepare or NdbTransaction::executeAsynch. + The difference between these is that + NdbTransaction::executeAsynch also sends the transaction to + the NDB kernel. + One of the arguments to these methods is a callback method. + The callback method is executed during polling (item 5 above). + + Note that NdbTransaction::executeAsynchPrepare does not + send the transaction to the NDB kernel. When using + NdbTransaction::executeAsynchPrepare, you either have to call + Ndb::sendPreparedTransactions or Ndb::sendPollNdb to send the + database operations. + (Ndb::sendPollNdb also polls Ndb for completed transactions.) + + The methods Ndb::pollNdb and Ndb::sendPollNdb checks if any + sent transactions are completed. The method Ndb::sendPollNdb + also send all prepared transactions before polling NDB. + Transactions still in the definition phase (i.e. items 1-3 above, + transactions which has not yet been sent to the NDB kernel) are not + affected by poll-calls. + The poll method invoked (either Ndb::pollNdb or Ndb::sendPollNdb) + will return when: + -# at least 'minNoOfEventsToWakeup' of the transactions + are finished processing, and + -# all of these transactions have executed their + callback methods. + + The poll method returns the number of transactions that + have finished processing and executed their callback methods. + + @note When an asynchronous transaction has been started and sent to + the NDB kernel, it is not allowed to execute any methods on + objects belonging to this transaction until the transaction + callback method have been executed. + (The transaction is stated and sent by either + NdbTransaction::executeAsynch or through the combination of + NdbTransaction::executeAsynchPrepare and either + Ndb::sendPreparedTransactions or Ndb::sendPollNdb). + + More about how transactions are sent the NDB Kernel is + available in section @ref secAdapt. +*/ +#endif + + +/** + + Put this back when real array ops are supported + i.e. get/setValue("kalle[3]"); + + @subsection secArrays Array Attributes + A table attribute in NDB Cluster can be of type <var>Array</var>, + meaning that the attribute consists of an ordered sequence of + elements. In such cases, <var>attribute size</var> is the size + (expressed in bits) of any one element making up the array; the + <var>array size</var> is the number of elements in the array. + +*/ + +#ifndef Ndb_H +#define Ndb_H + +#include <ndb_types.h> +#include <ndbapi_limits.h> +#include <ndb_cluster_connection.hpp> +#include <NdbError.hpp> +#include <NdbDictionary.hpp> + +class NdbObjectIdMap; +class NdbOperation; +class NdbEventOperationImpl; +class NdbScanOperation; +class NdbIndexScanOperation; +class NdbIndexOperation; +class NdbTransaction; +class NdbApiSignal; +class NdbRecAttr; +class NdbLabel; +class NdbBranch; +class NdbSubroutine; +class NdbCall; +class Table; +class BaseString; +class NdbEventOperation; +class NdbBlob; +class NdbReceiver; + +typedef void (* NdbEventCallback)(NdbEventOperation*, Ndb*, void*); + + +#if defined NDB_OSE +/** + * Default time to wait for response after request has been sent to + * NDB Cluster (Set to 10 seconds usually, but to 100 s in + * the OSE operating system) + */ +#define WAITFOR_RESPONSE_TIMEOUT 100000 // Milliseconds +#else +#define WAITFOR_RESPONSE_TIMEOUT 120000 // Milliseconds +#endif + +#define NDB_MAX_INTERNAL_TABLE_LENGTH NDB_MAX_DATABASE_NAME_SIZE + \ + NDB_MAX_SCHEMA_NAME_SIZE + \ + NDB_MAX_TAB_NAME_SIZE*2 + +/** + * @class Ndb + * @brief Represents the NDB kernel and is the main class of the NDB API. + * + * Always start your application program by creating an Ndb object. + * By using several Ndb objects it is possible to design + * a multi-threaded application, but note that Ndb objects + * cannot be shared by several threads. + * Different threads should use different Ndb objects. + * A thread might however use multiple Ndb objects. + * Currently there is a limit of maximum 128 Ndb objects + * per application process. + * + * @note It is not allowed to call methods in the NDB API + * on the same Ndb object in different threads + * simultaneously (without special handling of the + * Ndb object). + * + * @note The Ndb object is multi-thread safe in the following manner. + * Each Ndb object can ONLY be handled in one thread. + * If an Ndb object is handed over to another thread then the + * application must ensure that a memory barrier is used to + * ensure that the new thread see all updates performed by + * the previous thread. + * Semaphores, mutexes and so forth are easy ways of issuing memory + * barriers without having to bother about the memory barrier concept. + * + */ + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL +// to be documented later +/* + * If one Ndb object is used to handle parallel transactions through the + * asynchronous programming interface, please read the notes regarding + * asynchronous transactions (Section @ref secAsync). + * The asynchronous interface provides much higher performance + * in some situations, but is more complicated for the application designer. + * + * @note Each Ndb object should either use the methods for + * asynchronous transaction or the methods for + * synchronous transactions but not both. + */ +#endif + +class Ndb +{ +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + friend class NdbReceiver; + friend class NdbOperation; + friend class NdbEventOperationImpl; + friend class NdbTransaction; + friend class Table; + friend class NdbApiSignal; + friend class NdbIndexOperation; + friend class NdbScanOperation; + friend class NdbIndexScanOperation; + friend class NdbDictionaryImpl; + friend class NdbDictInterface; + friend class NdbBlob; +#endif + +public: + /** + * @name General + * @{ + */ + /** + * The Ndb object represents a connection to a database. + * + * @note The init() method must be called before the Ndb object may actually be used. + * + * @param ndb_cluster_connection is a connection to the cluster containing + * the database to be used + * @param aCatalogName is the name of the catalog to be used. + * @note The catalog name provides a namespace for the tables and + * indexes created in any connection from the Ndb object. + * @param aSchemaName is the name of the schema you + * want to use. + * @note The schema name provides an additional namespace + * for the tables and indexes created in a given catalog. + */ + Ndb(Ndb_cluster_connection *ndb_cluster_connection, + const char* aCatalogName = "", const char* aSchemaName = "def"); + + ~Ndb(); + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + /** + * The current catalog name can be fetched by getCatalogName. + * + * @return the current catalog name + */ + const char * getCatalogName() const; + + /** + * The current catalog name can be set by setCatalogName. + * + * @param aCatalogName is the new name of the current catalog + */ + void setCatalogName(const char * aCatalogName); + + /** + * The current schema name can be fetched by getSchemaName. + * + * @return the current schema name + */ + const char * getSchemaName() const; + + /** + * The current schema name can be set by setSchemaName. + * + * @param aSchemaName is the new name of the current schema + */ + void setSchemaName(const char * aSchemaName); +#endif + + /** + * The current database name can be fetched by getDatabaseName. + * + * @return the current database name + */ + const char * getDatabaseName() const; + + /** + * The current database name can be set by setDatabaseName. + * + * @param aDatabaseName is the new name of the current database + */ + void setDatabaseName(const char * aDatabaseName); + + /** + * The current database schema name can be fetched by getDatabaseSchemaName. + * + * @return the current database schema name + */ + const char * getDatabaseSchemaName() const; + + /** + * The current database schema name can be set by setDatabaseSchemaName. + * + * @param aDatabaseSchemaName is the new name of the current database schema + */ + void setDatabaseSchemaName(const char * aDatabaseSchemaName); + + /** + * Initializes the Ndb object + * + * @param maxNoOfTransactions + * Maximum number of parallel + * NdbTransaction objects that can be handled by the Ndb object. + * Maximum value is 1024. + * + * @note each scan or index scan operation uses one extra + * NdbTransaction object + * + * @return 0 if successful, -1 otherwise. + */ + int init(int maxNoOfTransactions = 4); + +#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED + /** + * Wait for Ndb object to successfully set-up connections to + * the NDB kernel. + * Starting to use the Ndb object without using this method + * gives unspecified behavior. + * + * @param timeout The maximum time we will wait for + * the initiation process to finish. + * Timeout is expressed in seconds. + * @return 0: Ndb is ready and timeout has not occurred.<br> + * -1: Timeout has expired + */ + int waitUntilReady(int timeout = 60); +#endif + + /** @} *********************************************************************/ + + /** + * @name Meta Information + * @{ + */ + + /** + * Get an object for retrieving or manipulating database schema information + * + * @note this object operates outside any transaction + * + * @return Object containing meta information about all tables + * in NDB Cluster. + */ + class NdbDictionary::Dictionary* getDictionary() const; + + + /** @} *********************************************************************/ + + /** + * @name Event subscriptions + * @{ + */ + + /** + * Create a subcription to an event defined in the database + * + * @param eventName + * unique identifier of the event + * @param bufferLength + * circular buffer size for storing event data + * + * @return Object representing an event, NULL on failure + */ + NdbEventOperation* createEventOperation(const char* eventName, + const int bufferLength); + /** + * Drop a subscription to an event + * + * @param eventOp + * Event operation + * + * @return 0 on success + */ + int dropEventOperation(NdbEventOperation* eventOp); + + /** + * Wait for an event to occur. Will return as soon as an event + * is detected on any of the created events. + * + * @param aMillisecondNumber + * maximum time to wait + * + * @return the number of events that has occured, -1 on failure + */ + int pollEvents(int aMillisecondNumber); + + /** @} *********************************************************************/ + + /** + * @name Starting and Closing Transactions + * @{ + */ + + /** + * Start a transaction + * + * @note When the transaction is completed it must be closed using + * Ndb::closeTransaction or NdbTransaction::close. + * The transaction must be closed independent of its outcome, i.e. + * even if there is an error. + * + * @param table Pointer to table object used for deciding + * which node to run the Transaction Coordinator on + * @param keyData Pointer to partition key corresponding to + * <var>table</var> + * @param keyLen Length of partition key expressed in bytes + * + * @return NdbTransaction object, or NULL on failure. + */ + NdbTransaction* startTransaction(const NdbDictionary::Table *table= 0, + const char *keyData = 0, + Uint32 keyLen = 0); + + /** + * Close a transaction. + * + * @note should be called after the transaction has completed, irrespective + * of success or failure + */ +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + /** + * @note It is not allowed to call Ndb::closeTransaction after sending the + * transaction asynchronously with either + * Ndb::sendPreparedTransactions or + * Ndb::sendPollNdb before the callback method has been called. + * (The application should keep track of the number of + * outstanding transactions and wait until all of them + * has completed before calling Ndb::closeTransaction). + * If the transaction is not committed it will be aborted. + */ +#endif + void closeTransaction(NdbTransaction*); + + /** @} *********************************************************************/ + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + // to be documented later + /** + * @name Asynchronous Transactions + * @{ + */ + + /** + * Wait for prepared transactions. + * Will return as soon as at least 'minNoOfEventsToWakeUp' + * of them have completed, or the maximum time given as timeout has passed. + * + * @param aMillisecondNumber + * Maximum time to wait for transactions to complete. Polling + * without wait is achieved by setting the timer to zero. + * Time is expressed in milliseconds. + * @param minNoOfEventsToWakeup Minimum number of transactions + * which has to wake up before the poll-call will return. + * If minNoOfEventsToWakeup is + * set to a value larger than 1 then this is the minimum + * number of transactions that need to complete before the + * poll will return. + * Setting it to zero means that one should wait for all + * outstanding transactions to return before waking up. + * @return Number of transactions polled. + */ + int pollNdb(int aMillisecondNumber = WAITFOR_RESPONSE_TIMEOUT, + int minNoOfEventsToWakeup = 1); + + /** + * This send method will send all prepared database operations. + * The default method is to do it non-force and instead + * use the adaptive algorithm. (See Section @ref secAdapt.) + * The second option is to force the sending and + * finally there is the third alternative which is + * also non-force but also making sure that the + * adaptive algorithm do not notice the send. + * In this case the sending will be performed on a + * cyclical 10 millisecond event. + * + * @param forceSend When operations should be sent to NDB Kernel. + * (See @ref secAdapt.) + * - 0: non-force, adaptive algorithm notices it (default); + * - 1: force send, adaptive algorithm notices it; + * - 2: non-force, adaptive algorithm do not notice the send. + */ + void sendPreparedTransactions(int forceSend = 0); + + /** + * This is a send-poll variant that first calls + * Ndb::sendPreparedTransactions and then Ndb::pollNdb. + * It is however somewhat faster than calling the methods + * separately, since some mutex-operations are avoided. + * See documentation of Ndb::pollNdb and Ndb::sendPreparedTransactions + * for more details. + * + * @param aMillisecondNumber Timeout specifier + * Polling without wait is achieved by setting the + * millisecond timer to zero. + * @param minNoOfEventsToWakeup Minimum number of transactions + * which has to wake up before the poll-call will return. + * If minNoOfEventsToWakeup is + * set to a value larger than 1 then this is the minimum + * number of transactions that need to complete before the + * poll-call will return. + * Setting it to zero means that one should wait for all + * outstanding transactions to return before waking up. + * @param forceSend When operations should be sent to NDB Kernel. + * (See @ref secAdapt.) + * - 0: non-force, adaptive algorithm notices it (default); + * - 1: force send, adaptive algorithm notices it; + * - 2: non-force, adaptive algorithm does not notice the send. + * @return Number of transactions polled. + */ + int sendPollNdb(int aMillisecondNumber = WAITFOR_RESPONSE_TIMEOUT, + int minNoOfEventsToWakeup = 1, + int forceSend = 0); + /** @} *********************************************************************/ +#endif + + /** + * @name Error Handling + * @{ + */ + + /** + * Get the NdbError object + * + * @note The NdbError object is valid until a new NDB API method is called. + */ + const NdbError & getNdbError() const; + + /** + * Get a NdbError object for a specific error code + * + * The NdbError object is valid until you call a new NDB API method. + */ + const NdbError & getNdbError(int errorCode); + + + /** @} *********************************************************************/ + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + /** + * Get the application node identity. + * + * @return Node id of this application. + */ + int getNodeId(); + + bool usingFullyQualifiedNames(); + + /** + * Different types of tampering with the NDB Cluster. + * <b>Only for debugging purposes only.</b> + */ + enum TamperType { + LockGlbChp = 1, ///< Lock GCP + UnlockGlbChp, ///< Unlock GCP + CrashNode, ///< Crash an NDB node + ReadRestartGCI, ///< Request the restart GCI id from NDB Cluster + InsertError ///< Execute an error in NDB Cluster + ///< (may crash system) + }; + + /** + * For testing purposes it is possible to tamper with the NDB Cluster + * (i.e. send a special signal to DBDIH, the NDB distribution handler). + * <b>This feature should only used for debugging purposes.</b> + * In a release versions of NDB Cluster, + * this call always return -1 and does nothing. + * + * @param aAction Action to be taken according to TamperType above + * + * @param aNode Which node the action will be taken + * -1: Master DIH. + * 0-16: Nodnumber. + * @return -1 indicates error, other values have meaning dependent + * on type of tampering. + */ + int NdbTamper(TamperType aAction, int aNode); + + /** + * Return a unique tuple id for a table. The id sequence is + * ascending but may contain gaps. + * + * @param aTableName table name + * + * @param cacheSize number of values to cache in this Ndb object + * + * @return tuple id or 0 on error + */ + Uint64 getAutoIncrementValue(const char* aTableName, + Uint32 cacheSize = 1); + Uint64 getAutoIncrementValue(const NdbDictionary::Table * aTable, + Uint32 cacheSize = 1); + Uint64 readAutoIncrementValue(const char* aTableName); + Uint64 readAutoIncrementValue(const NdbDictionary::Table * aTable); + bool setAutoIncrementValue(const char* aTableName, Uint64 val, + bool increase = false); + bool setAutoIncrementValue(const NdbDictionary::Table * aTable, Uint64 val, + bool increase = false); + Uint64 getTupleIdFromNdb(const char* aTableName, + Uint32 cacheSize = 1000); + Uint64 getTupleIdFromNdb(Uint32 aTableId, + Uint32 cacheSize = 1000); + Uint64 readTupleIdFromNdb(Uint32 aTableId); + bool setTupleIdInNdb(const char* aTableName, Uint64 val, + bool increase); + bool setTupleIdInNdb(Uint32 aTableId, Uint64 val, bool increase); + Uint64 opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op); + + /** + */ + NdbTransaction* hupp( NdbTransaction* ); + Uint32 getReference() const { return theMyRef;} +#endif + +/***************************************************************************** + * These are service routines used by the other classes in the NDBAPI. + ****************************************************************************/ +private: + + void setup(Ndb_cluster_connection *ndb_cluster_connection, + const char* aCatalogName, const char* aSchemaName); + + void connected(Uint32 block_reference); + + + NdbTransaction* startTransactionLocal(Uint32 aPrio, Uint32 aFragmentId); + +// Connect the connection object to the Database. + int NDB_connect(Uint32 tNode); + NdbTransaction* doConnect(Uint32 nodeId); + void doDisconnect(); + + NdbReceiver* getNdbScanRec();// Get a NdbScanReceiver from idle list + NdbLabel* getNdbLabel(); // Get a NdbLabel from idle list + NdbBranch* getNdbBranch(); // Get a NdbBranch from idle list + NdbSubroutine* getNdbSubroutine();// Get a NdbSubroutine from idle + NdbCall* getNdbCall(); // Get a NdbCall from idle list + NdbApiSignal* getSignal(); // Get an operation from idle list + NdbRecAttr* getRecAttr(); // Get a receeive attribute object from + // idle list of the Ndb object. + NdbOperation* getOperation(); // Get an operation from idle list + NdbIndexScanOperation* getScanOperation(); // Get a scan operation from idle + NdbIndexOperation* getIndexOperation();// Get an index operation from idle + + class NdbGlobalEventBufferHandle* getGlobalEventBufferHandle(); + NdbBlob* getNdbBlob();// Get a blob handle etc + + void releaseSignal(NdbApiSignal* anApiSignal); + void releaseSignalsInList(NdbApiSignal** pList); + void releaseNdbScanRec(NdbReceiver* aNdbScanRec); + void releaseNdbLabel(NdbLabel* anNdbLabel); + void releaseNdbBranch(NdbBranch* anNdbBranch); + void releaseNdbSubroutine(NdbSubroutine* anNdbSubroutine); + void releaseNdbCall(NdbCall* anNdbCall); + void releaseRecAttr (NdbRecAttr* aRecAttr); + void releaseOperation(NdbOperation* anOperation); + void releaseScanOperation(NdbIndexScanOperation*); + void releaseNdbBlob(NdbBlob* aBlob); + + void check_send_timeout(); + void remove_sent_list(Uint32); + Uint32 insert_completed_list(NdbTransaction*); + Uint32 insert_sent_list(NdbTransaction*); + + // Handle a received signal. Used by both + // synchronous and asynchronous interface + void handleReceivedSignal(NdbApiSignal* anApiSignal, struct LinearSectionPtr ptr[3]); + + // Receive response signals + int receiveResponse(int waitTime = WAITFOR_RESPONSE_TIMEOUT); + + int sendRecSignal(Uint16 aNodeId, + Uint32 aWaitState, + NdbApiSignal* aSignal, + Uint32 nodeSequence); + + // Sets Restart GCI in Ndb object + void RestartGCI(int aRestartGCI); + + // Get block number of this NDBAPI object + int getBlockNumber(); + + /**************************************************************************** + * These are local service routines used by this class. + ***************************************************************************/ + + int createConIdleList(int aNrOfCon); + int createOpIdleList( int nrOfOp ); + + void freeOperation(); // Free the first idle operation. + void freeScanOperation(); // Free the first idle scan operation. + void freeIndexOperation(); // Free the first idle index operation. + void freeNdbCon(); // Free the first idle connection. + void freeSignal(); // Free the first idle signal + void freeRecAttr(); // Free the first idle receive attr obj + void freeNdbLabel(); // Free the first idle NdbLabel obj + void freeNdbBranch();// Free the first idle NdbBranch obj + void freeNdbSubroutine();// Free the first idle NdbSubroutine obj + void freeNdbCall(); // Free the first idle NdbCall obj + void freeNdbScanRec(); // Free the first idle NdbScanRec obj + void freeNdbBlob(); // Free the first etc + + NdbTransaction* getNdbCon(); // Get a connection from idle list + + /** + * Get a connected NdbTransaction to nodeId + * Returns NULL if none found + */ + NdbTransaction* getConnectedNdbTransaction(Uint32 nodeId); + + // Release and disconnect from DBTC a connection + // and seize it to theConIdleList + void releaseConnectToNdb (NdbTransaction*); + + // Release a connection to idle list + void releaseNdbCon (NdbTransaction*); + + int checkInitState(); // Check that we are initialized + void report_node_failure(Uint32 node_id); // Report Failed node + void report_node_failure_completed(Uint32 node_id); // Report Failed node(NF comp.) + + void checkFailedNode(); // Check for failed nodes + + int NDB_connect(); // Perform connect towards NDB Kernel + + // Release arrays of NdbTransaction pointers + void releaseTransactionArrays(); + + Uint32 pollCompleted(NdbTransaction** aCopyArray); + void sendPrepTrans(int forceSend); + void reportCallback(NdbTransaction** aCopyArray, Uint32 aNoOfComplTrans); + void waitCompletedTransactions(int milliSecs, int noOfEventsToWaitFor); + void completedTransaction(NdbTransaction* aTransaction); + void completedScanTransaction(NdbTransaction* aTransaction); + + void abortTransactionsAfterNodeFailure(Uint16 aNodeId); + + static + const char * externalizeTableName(const char * internalTableName, bool fullyQualifiedNames); + const char * externalizeTableName(const char * internalTableName); + const char * internalizeTableName(const char * externalTableName); + + static + const char * externalizeIndexName(const char * internalIndexName, bool fullyQualifiedNames); + const char * externalizeIndexName(const char * internalIndexName); + const char * internalizeIndexName(const NdbTableImpl * table, + const char * externalIndexName); + + static + const BaseString getDatabaseFromInternalName(const char * internalName); + static + const BaseString getSchemaFromInternalName(const char * internalName); + + void* int2void (Uint32 val); + NdbReceiver* void2rec (void* val); + NdbTransaction* void2con (void* val); + NdbOperation* void2rec_op (void* val); + NdbIndexOperation* void2rec_iop (void* val); + +/****************************************************************************** + * These are the private variables in this class. + *****************************************************************************/ + NdbTransaction** thePreparedTransactionsArray; + NdbTransaction** theSentTransactionsArray; + NdbTransaction** theCompletedTransactionsArray; + + Uint32 theNoOfPreparedTransactions; + Uint32 theNoOfSentTransactions; + Uint32 theNoOfCompletedTransactions; + Uint32 theNoOfAllocatedTransactions; + Uint32 theMaxNoOfTransactions; + Uint32 theMinNoOfEventsToWakeUp; + + Uint32 theNextConnectNode; + + bool fullyQualifiedNames; + + // Ndb database name. + char theDataBase[NDB_MAX_DATABASE_NAME_SIZE]; + // Ndb database schema name. + char theDataBaseSchema[NDB_MAX_SCHEMA_NAME_SIZE]; + char prefixName[NDB_MAX_INTERNAL_TABLE_LENGTH]; + char * prefixEnd; + + class NdbImpl * theImpl; + class NdbDictionaryImpl* theDictionary; + class NdbGlobalEventBufferHandle* theGlobalEventBufferHandle; + + NdbTransaction* theConIdleList; // First connection in idle list. + + NdbOperation* theOpIdleList; // First operation in the idle list. + + NdbIndexScanOperation* theScanOpIdleList; // First scan operation in the idle list. + NdbIndexOperation* theIndexOpIdleList; // First index operation in the idle list. + NdbTransaction* theTransactionList; + NdbTransaction** theConnectionArray; + NdbRecAttr* theRecAttrIdleList; + NdbApiSignal* theSignalIdleList; // First signal in idlelist. + NdbLabel* theLabelList; // First label descriptor in list + NdbBranch* theBranchList; // First branch descriptor in list + NdbSubroutine* theSubroutineList; // First subroutine descriptor in + NdbCall* theCallList; // First call descriptor in list + NdbReceiver* theScanList; + NdbBlob* theNdbBlobIdleList; + + Uint32 theMyRef; // My block reference + Uint32 theNode; // The node number of our node + + Uint64 the_last_check_time; + Uint64 theFirstTransId; + + // The tupleId is retreived from DB the + // tupleId is unique for each tableid. + Uint64 theFirstTupleId[2048]; + Uint64 theLastTupleId[2048]; + + Uint32 theRestartGCI; // the Restart GCI used by DIHNDBTAMPER + + NdbError theError; + + Int32 theNdbBlockNumber; + + enum InitType { + NotConstructed, + NotInitialised, + StartingInit, + Initialised, + InitConfigError + } theInitState; + + NdbApiSignal* theCommitAckSignal; + + +#ifdef POORMANSPURIFY + int cfreeSignals; + int cnewSignals; + int cgetSignals; + int creleaseSignals; +#endif + + static void executeMessage(void*, NdbApiSignal *, + struct LinearSectionPtr ptr[3]); + static void statusMessage(void*, Uint32, bool, bool); +#ifdef VM_TRACE + void printState(const char* fmt, ...); +#endif +}; + +#endif |