diff --git a/ndb/include/ndbapi/Ndb.hpp b/ndb/include/ndbapi/Ndb.hpp index 0a58bfb3abc..39f6a2a02bd 100644 --- a/ndb/include/ndbapi/Ndb.hpp +++ b/ndb/include/ndbapi/Ndb.hpp @@ -52,15 +52,6 @@ The execution can be of two different types, Commit or NoCommit. -*/ -#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 -/** If the execution is of type NoCommit, then the application program executes part of a transaction, but without committing the transaction. @@ -94,28 +85,13 @@ To execute several parallel synchronous transactions, one can either use multiple Ndb objects in several threads, or start multiple applications programs. -*/ -#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL -/** - Another way to execute several parallel transactions is to use - asynchronous transactions. -*/ -#endif -/** + @section secNdbOperations Operations - Each NdbTransaction (that is, a transaction) + Each NdbTransaction consists of a list of operations which are represented by instances of Ndb*Operation. -*/ -#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL -/** - Operations are of two different kinds: - -# standard operations, and - -# interpreted program operations. -*/ -#endif -/** +

Single row operations

After the operation is created using NdbTransaction::getNdbOperation() (or NdbTransaction::getNdbIndexOperation()), it is defined in the following @@ -163,11 +139,8 @@ We will now discuss in somewhat greater detail each step involved in the creation and use of synchronous transactions. -*/ - // Edit stop point - JS, 20041228 0425+1000 -/**

Step 1: Define single row operation type

The following types of operations exist: -# NdbOperation::insertTuple : @@ -207,14 +180,13 @@ Normally the attribute is defined by its name but it is also possible to use the attribute identity to define the attribute. - The mapping from name to identity is performed by the Table object. - NdbIndexOperation::getValue returns an NdbRecAttr object + NdbOperation::getValue returns an NdbRecAttr object containing the read value. To get the value, there is actually two methods. The application can either - use its own memory (passed through a pointer aValue) to - NdbIndexOperation::getValue, or + NdbOperation::getValue, or - receive the attribute value in an NdbRecAttr object allocated by the NDB API. @@ -224,7 +196,466 @@ Ndb::closeTransaction have been called. The result of reading data from an NdbRecAttr object before calling NdbTransaction::execute is undefined. + + + @subsection secScan Scan Operations + + Scans are roughly the equivalent of SQL cursors. + + Scans can either be performed on a table (@ref NdbScanOperation) or + on an ordered index (@ref NdbIndexScanOperation). + + Scan operation are characteriesed by the following: + - They can only perform 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 parallell + + After the operation is created using NdbTransaction::getNdbScanOperation() + (or NdbTransaction::getNdbIndexScanOperation()), it is defined 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() + -# Iterating through the result set using NdbScanOperation::nextResult + + Here are two brief examples illustrating this process. For the sake of brevity, + we omit error-handling. + + This first example uses an NdbScanOperation: + @code + // 1. Create + MyOperation= MyTransaction->getNdbScanOperation("MYTABLENAME"); + + // 2. Define type of operation and lock mode + MyOperation->readTuples(NdbOperation::LM_Read); + + // 3. 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(); + + // 4. Attribute Actions + MyRecAttr= MyOperation->getValue("ATTR2", NULL); + @endcode + + The second example uses an NdbIndexScanOperation: + @code + // 1. Create + MyOperation= MyTransaction->getNdbIndexScanOperation("MYORDEREDINDEX", "MYTABLENAME"); + + // 2. Define type of operation and lock mode + MyOperation->readTuples(NdbOperation::LM_Read); + + // 3. 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); + + // 4. Attribute Actions + MyRecAttr = MyOperation->getValue("ATTR2", NULL); + @endcode + +

Step 1: Define scan operation operation type

+ Scan operations only support 1 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::getNdb*ScanOperation for each + operation. + +

Step 2: Specify Search Conditions

+ The search condition is used to select tuples. + If no search condition is specified, the scan will return all rows + in the table. + + Search condition can be @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 have both NdbScanFilter and bounds + + @note When NdbScanFilter is used each row is examined but maybe not + returned. But when using bounds, only rows within bounds will be examined. + +

Step 3: Specify Attribute Actions

+ + Now it is time to define which attributes should be read. + Normally the attribute is defined by its name but it is + also possible to use the attribute identity to define the + attribute. + + NdbOperation::getValue returns an NdbRecAttr object + containing the read value. + To get the value, there is actually two methods. + 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 can not reference this object after + Ndb::closeTransaction have been called. + The result of reading data from an NdbRecAttr object before + calling NdbTransaction::execute is undefined. + +

Using Scan to update/delete

+ Scanning can also be used to update/delete rows. + This is performed by + -# Scan using exclusive locks, NdbOperation::LM_Exclusive + -# When iterating through the result set, for each row optionally call + either NdbScanOperation::updateCurrentTuple or + NdbScanOperation::deleteCurrentTuple + -# If performing NdbScanOperation::updateCurrentTuple, + set new values on record using ordinary @ref NdbOperation::setValue. + NdbOperation::equal should _not_ be called as the primary key is + retreived from the scan. + + @note that the actual update/delete will not be performed until next + NdbTransaction::execute (as with single row operations), + NdbTransaction::execute needs to be called before locks are released, + see @ref secScanLocks + +

Index scans specific features

+ The following features are available when performing an index scan + - Scan subset of table using @ref NdbIndexScanOperation::setBound + - Ordering result set ascending or descending, @ref NdbIndexScanOperation::readTuples + - When using NdbIndexScanOperation::BoundEQ on distribution key + only fragment containing rows will be scanned. + + Rows are returned unordered unless sorted is set to true. + @note When performing sorted scan, parameter parallelism to readTuples will + be ignored and max parallelism will be used instead. + + @subsection secScanLocks Lock handling with scans + + When scanning a table or an index potentially + a lot of records will be returned. + + But Ndb will only lock a batch of rows per fragment at a time. + How many rows will be locked per fragment is controlled by the + batch parameter to @ref NdbScanOperation::readTuples. + + To let the application handle how locks are released + @ref NdbScanOperation::nextResult have a parameter fetch_allow. + If NdbScanOperation::nextResult is called with fetch_allow = false, no + locks may be released as result of the function call. Otherwise the locks + for the current batch may be released. + + This example shows scan delete, handling 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 full example of scan. + + @section secError Error Handling + + Errors can occur when + -# operations are being defined, or when the + -# transaction is being executed. + + One recommended way to handle a transaction failure + (i.e. an error is reported) is to: + -# Rollback transaction (NdbTransaction::execute with a special parameter) + -# Close transaction + -# Restart transaction (if the error was temporary) + + @note Transaction are not automatically closed when an error occur. + + Several errors can occur when a transaction holds multiple + operations which are simultaneously executed. + In this case the application has to go through the operation + objects and query for their NdbError objects to find out what really + happened. + + NdbTransaction::getNdbErrorOperation returns a reference to the + operation causing the latest error. + NdbTransaction::getNdbErrorLine delivers the method number of the + erroneous method in the operation. + + @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); //Here an error occurs + theOperation->setValue("ATTR_3", at1); + theOperation->setValue("ATTR_4", at1); + + if (theTransaction->execute(Commit) == -1) { + errorLine = theTransaction->getNdbErrorLine(); + errorOperation = theTransaction->getNdbErrorOperation(); + @endcode + + Here errorLine will be 3 as the error occurred in the third method + on the operation object. + Getting errorLine == 0 means that the error occurred when executing the + operations. + Here errorOperation will be a pointer to the theOperation object. + NdbTransaction::getNdbError will return the NdbError object + including holding information about the error. + + Since errors could have occurred even when a commit was reported, + there is also a special method, NdbTransaction::commitStatus, + to check the commit status of the transaction. + +*******************************************************************************/ + +/** + * @page ndbapi_example1.cpp ndbapi_example1.cpp + * @include ndbapi_example1.cpp + */ + +/** + * @page ndbapi_example2.cpp ndbapi_example2.cpp + * @include ndbapi_example2.cpp + */ + +/** + * @page ndbapi_example3.cpp ndbapi_example3.cpp + * @include ndbapi_example3.cpp + */ + +/** + * @page ndbapi_example4.cpp ndbapi_example4.cpp + * @include ndbapi_example4.cpp + */ + +/** + * @page ndbapi_scan.cpp ndbapi_scan.cpp + * @include ndbapi_scan.cpp + */ + + +/** + @page secAdapt Adaptive Send Algorithm + + At the time of "sending" the transaction + (using NdbTransaction::execute), the transactions + are in reality not 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 + be transfered to the NDB kernel. + + 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. + + + Since the NDB API is designed as a multi-threaded interface, + it is 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 transfering + information to the NDB kernel and the expected amount of threads to + interact with the NDB kernel. + Note that an Ndb object should be used in at most one thread. + Two different threads should not use the same Ndb object. + + There are four reasons leading to transfering of database + operations: + -# 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 + might change between NDB Cluster releases. + On TCP/IP the buffer size is usually around 64 kByte and + on OSE/Delta it is usually less than 2000 bytes. + In each Ndb object there is one buffer per DB node, + so this criteria of a full buffer is only + local to the connection to one DB node. + -# Statistical information on the transfered information + may force sending of buffers to all DB nodes. + -# Every 10 ms a special send-thread checks whether + any send activity has occurred. If not, then the thread will + force sending to all nodes. + This means that 20 ms is the maximum time database operations + are waiting before being sent off. The 10 millisecond limit + is likely to become a configuration parameter in + later releases of NDB Cluster. + However, to support faster than 10 ms checks, + there has to be support from the operating system. + -# When calling NdbTransaction::execute synchronously or calling any + of the poll-methods, there is a force parameter that overrides the + adaptive algorithm and forces the send to all nodes. + + @note The times mentioned above are examples. These might + change in later releases of NDB Cluster. */ + +/** + @page secConcepts NDB Cluster Concepts + + The NDB Kernel is the collection of database (DB) nodes + belonging to an NDB Cluster. + The application programmer can for most purposes view the + set of all DB nodes as one entity. + Each DB node has three main components: + - TC : The transaction coordinator + - ACC : The index storage + - TUP : The data storage + + When the application program executes a transaction, + it connects to one TC on one DB node. + Usually, the programmer does not need to specify which TC to use, + but some cases when performance is important, + transactions can be hinted to use a certain TC. + (If the node with the TC is down, then another TC will + automatically take over the work.) + + Every DB node has an ACC and a TUP which stores + the index and the data part of the database. + Even though one TC is responsible for the transaction, + several ACCs and TUPs on other DB nodes might be involved in the + execution of the transaction. + + + @section secNdbKernelConnection Selecting Transaction Coordinator + + The default method is to select the transaction coordinator (TC) as being + the "closest" DB node. There is a heuristics for closeness based on + the type of transporter connection. In order of closest first, we have + SCI, SHM, TCP/IP (localhost), and TCP/IP (remote host). If there are several + connections available with the same "closeness", they will each be + selected in a round robin fashion for every transaction. Optionally + one may set the methos for TC selection round robin over all available + connections, where each new set of transactions + is placed on the next DB node. + + The application programmer can however hint the NDB API which + transaction coordinator to use + by providing a distribution key (usually the primary key). + By using the primary key as distribution 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 and then the NDB API will choose a transaction + coordinator without using the hint. + For more information, see NdbDictionary::Column::setDistributionKey. + + + @section secRecordStruct Record Structure + NDB Cluster is a relational database with tables of records. + Table rows represent tuples of relational data stored as records. + When created, the attribute schema of the table is specified, + and thus each record of the table has the same schema. + + + @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 to main memory, + and are committed to disk after a global checkpoint, GCP. + 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 after 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 (parallel application programs, thread-based + applications) + sometimes deadlock when they try to access the same information. + Applications need to be programmed so that timeout errors + occurring due to deadlocks are handled. This generally + means that the transaction encountering timeout + should be rolled back and restarted. + + + @section secHint Hints and performance + + Placing the transaction coordinator close + to the actual data used in the transaction can in many cases + improve performance significantly. This is particularly true for + systems using TCP/IP. A system using Solaris and a 500 MHz processor + has a cost model for TCP/IP communication which is: + + 30 microseconds + (100 nanoseconds * no of Bytes) + + This means that if we can ensure that we use "popular" links we increase + buffering and thus drastically reduce the communication cost. + Systems using SCI has a different cost model which is: + + 5 microseconds + (10 nanoseconds * no of Bytes) + + Thus SCI systems are much less dependent on selection of + transaction coordinators. + Typically TCP/IP systems spend 30-60% of the time during communication, + whereas SCI systems typically spend 5-10% of the time during + communication. + Thus SCI means that less care from the NDB API programmer is + needed and great scalability can be achieved even for applications using + data from many 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 distribution 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 /**

Interpreted Program Operations

@@ -233,10 +664,6 @@ updates a tuple using an interpreted program -# NdbOperation::interpretedDeleteTuple : delete a tuple using an interpreted program - -# NdbOperation::openScanRead : - scans a table with read lock on each tuple - -# NdbOperation::openScanExclusive : - scans a table with exclusive update lock on each tuple The operations interpretedUpdateTuple and interpretedDeleteTuple both work using the unique tuple key. @@ -289,122 +716,6 @@ NdbOperation::def_subroutine and NdbOperation::ret_sub. */ #endif -/** - @subsection secScan Scanning - The most common use of interpreted programs is for scanning - tables. Scanning is a search of all tuples in a table. - Tuples which satisfy conditions (a search filter) - stated in the interpreted program - are sent to the application. - - Reasons for using scan transactions include - need to use a search key different from the primary key - and any secondary index. - Or that the query needs to access so many tuples so that - it is more efficient to scan the entire table. - - Scanning can also be used to update information. - The scanning transaction itself is however - not allowed to update any tuples. - To do updates via scanning transactions, the tuples - need to be handed over to another transaction which is - executing the actual update. - - Even though a scan operation is part of a transaction, - the scan transaction is not a normal transaction. - The locks are not kept throughout the entire - scan transaction, since this would imply non-optimal performance. - - A transaction containing a scan operation can only - contain that operation. - No other operations are allowed in the same transaction. - - - The NdbOperation::openScanRead operation - only sets a temporary read lock while - reading the tuple. - The tuple lock is released already when the - result of the read reaches the application. - The NdbOperation::openScanExclusive operation sets an - exclusive lock on the tuple - and sends the result to the application. - Thus when the application reads the data it is still - locked with the exclusive lock. - - If the application desires to update the tuple it may transfer - the tuple to another transaction which updates the tuple. - The updating transaction can consist of a combination of tuples - received from the scan and normal operations. - - For transferred operations it is not necessary to provide the - primary key. It is part of the transfer. - You only need to give the operation type and the - actions to perform on the tuple. - - The scan transaction starts like a usual transaction, - but is of the following form: - -# Start transaction - -# Get NdbOperation for the table to be scanned - -# Set the operation type using NdbOperation::openScanRead or - NdbOperation::openScanExclusive - -# Search conditions are defined by an interpreted program - (setValue and write_attr are not allowed, since scan transactions - are only allowed to read information). - The instruction interpret_exit_nok does in this case - not abort the transaction, it only skips the tuple and - proceeds with the next. - The skipped tuple will not be reported to the application. - -# Call NdbTransaction::executeScan to define (and start) the scan. - -# Call NdbTransaction::nextScanResult to proceed with next tuple. - When calling NdbTransaction::nextScanResult, the lock on any - previous tuples are released. -
- If the tuple should be updated then it must be transferred over - to another updating transaction. - This is performed by calling - NdbOperation::takeOverForUpdate or takeOverForDelete on - the scanning transactions NdbOperation object with the updating - transactions NdbTransaction object as parameter. -

- If NdbOperation::takeOverFor* returns NULL then the - operation was not successful, otherwise it returns a reference - to the NdbOperation which the updating transaction has received - -# Use Ndb::closeTransaction as usual to close the transaction. - This can be performed even if there are more tuples to scan. - - See also example program in section @ref select_all.cpp. - - However, a new scan api is under development, using NdbScanOperation - and NdbScanFilter. NdbScanFilter makes it easier to define a search - criteria and is recommended instead of using Interpreted Programs. - - The scan transaction starts like a usual transaction, - but is of the following form: - -# Start transaction - -# Get NdbScanOperation for the table to be scanned - -# NdbScanOperation::readTuples(NdbOperation::LM_Exclusive) returns a handle to a - NdbResultSet. - -# Search conditions are defined by NdbScanFilter - -# Call NdbTransaction::execute(NoCommit) to start the scan. - -# Call NdbResultSet::nextResult to proceed with next tuple. - When calling NdbResultSet::nextResult(false), the lock on any - previous tuples are released and the next tuple cached in the API - is fetched. -
- If the tuple should be updated then define a new update operation - (NdbOperation) using NdbResultSet::updateTuple(). - The new update operation can the be used to modify the tuple. - When nextResult(false) returns != 0, then no more tuples - are cached in the API. Updated tuples is now commit using - NdbTransaction::execute(Commit). - After the commit, more tuples are fetched from NDB using - nextResult(true). - -# Use Ndb::closeTransaction as usual to close the transaction. - This can be performed even if there are more tuples to scan. - - See the scan example program in @ref ndbapi_scan.cppn for example - usage of the new scan api. -*/ #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL /**

Interpreted Programs

@@ -584,234 +895,11 @@ */ #endif -/** - @section secError Error Handling - - Errors can occur when - -# operations are being defined, or when the - -# transaction is being executed. - - One recommended way to handle a transaction failure - (i.e. an error is reported) is to: - -# Rollback transaction (NdbTransaction::execute with a special parameter) - -# Close transaction - -# Restart transaction (if the error was temporary) - - @note Transaction are not automatically closed when an error occur. - - Several errors can occur when a transaction holds multiple - operations which are simultaneously executed. - In this case the application has to go through the operation - objects and query for their NdbError objects to find out what really - happened. - - NdbTransaction::getNdbErrorOperation returns a reference to the - operation causing the latest error. - NdbTransaction::getNdbErrorLine delivers the method number of the - erroneous method in the operation. - - @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); //Here an error occurs - theOperation->setValue("ATTR_3", at1); - theOperation->setValue("ATTR_4", at1); - - if (theTransaction->execute(Commit) == -1) { - errorLine = theTransaction->getNdbErrorLine(); - errorOperation = theTransaction->getNdbErrorOperation(); - @endcode - - Here errorLine will be 3 as the error occurred in the third method - on the operation object. - Getting errorLine == 0 means that the error occurred when executing the - operations. - Here errorOperation will be a pointer to the theOperation object. - NdbTransaction::getNdbError will return the NdbError object - including holding information about the error. - - Since errors could have occurred even when a commit was reported, - there is also a special method, NdbTransaction::commitStatus, - to check the commit status of the transaction. - -*******************************************************************************/ /** - * @page ndbapi_example1.cpp ndbapi_example1.cpp - * @include ndbapi_example1.cpp - */ - -/** - * @page ndbapi_example2.cpp ndbapi_example2.cpp - * @include ndbapi_example2.cpp - */ - -/** - * @page ndbapi_example3.cpp ndbapi_example3.cpp - * @include ndbapi_example3.cpp - */ - -/** - * @page ndbapi_example4.cpp ndbapi_example4.cpp - * @include ndbapi_example4.cpp - */ - -#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL -/** - * @page select_all.cpp select_all.cpp - * @include select_all.cpp - */ - -/** - * @page ndbapi_async.cpp ndbapi_async.cpp - * @include ndbapi_async.cpp - */ -#endif - -/** - * @page ndbapi_scan.cpp ndbapi_scan.cpp - * @include ndbapi_scan.cpp - */ - - -/** - @page secAdapt Adaptive Send Algorithm - - At the time of "sending" the transaction - (using NdbTransaction::execute), the transactions - are in reality not 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 - be transfered to the NDB kernel. - - 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. - - - Since the NDB API is designed as a multi-threaded interface, - it is 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 transfering - information to the NDB kernel and the expected amount of threads to - interact with the NDB kernel. - Note that an Ndb object should be used in at most one thread. - Two different threads should not use the same Ndb object. - - There are four reasons leading to transfering of database - operations: - -# 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 - might change between NDB Cluster releases. - On TCP/IP the buffer size is usually around 64 kByte and - on OSE/Delta it is usually less than 2000 bytes. - In each Ndb object there is one buffer per DB node, - so this criteria of a full buffer is only - local to the connection to one DB node. - -# Statistical information on the transfered information - may force sending of buffers to all DB nodes. - -# Every 10 ms a special send-thread checks whether - any send activity has occurred. If not, then the thread will - force sending to all nodes. - This means that 20 ms is the maximum time database operations - are waiting before being sent off. The 10 millisecond limit - is likely to become a configuration parameter in - later releases of NDB Cluster. - However, to support faster than 10 ms checks, - there has to be support from the operating system. - -# When calling NdbTransaction::execute synchronously or calling any - of the poll-methods, there is a force parameter that overrides the - adaptive algorithm and forces the send to all nodes. - - @note The times mentioned above are examples. These might - change in later releases of NDB Cluster. -*/ - -/** - @page secConcepts NDB Cluster Concepts - - The NDB Kernel is the collection of database (DB) nodes - belonging to an NDB Cluster. - The application programmer can for most purposes view the - set of all DB nodes as one entity. - Each DB node has three main components: - - TC : The transaction coordinator - - ACC : The index storage - - TUP : The data storage - - When the application program executes a transaction, - it connects to one TC on one DB node. - Usually, the programmer does not need to specify which TC to use, - but some cases when performance is important, - transactions can be hinted to use a certain TC. - (If the node with the TC is down, then another TC will - automatically take over the work.) - - Every DB node has an ACC and a TUP which stores - the index and the data part of the database. - Even though one TC is responsible for the transaction, - several ACCs and TUPs on other DB nodes might be involved in the - execution of the transaction. - - - @section secNdbKernelConnection Selecting Transaction Coordinator - - The default method is to select the transaction coordinator (TC) as being - the "closest" DB node. There is a heuristics for closeness based on - the type of transporter connection. In order of closest first, we have - SCI, SHM, TCP/IP (localhost), and TCP/IP (remote host). If there are several - connections available with the same "closeness", they will each be - selected in a round robin fashion for every transaction. Optionally - one may set the methos for TC selection round robin over all available - connections, where each new set of transactions - is placed on the next DB node. - The application programmer can however hint the NDB API which - transaction coordinator to use - by providing a distribution key (usually the primary key). - By using the primary key as distribution 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 and then the NDB API will choose a transaction - coordinator without using the hint. - For more information, see NdbDictionary::Column::setDistributionKey. - - - @section secRecordStruct Record Structure - NDB Cluster is a relational database with tables of records. - Table rows represent tuples of relational data stored as records. - When created, the attribute schema of the table is specified, - and thus each record of the table has the same schema. - - - @subsection secKeys Tuple Keys - Each record has from zero up to four attributes which belong - to the primary key of the table. - If no attribute belongs to the primary key, then - the NDB Cluster creates an attribute named NDB$TID - which stores a tuple identity. - The tuple key of a table is thus either - the primary key attributes or the special NDB$TID attribute. - + 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 array type. @@ -819,66 +907,7 @@ elements. The attribute size is the size of one element of the array (expressed in bits) and the array size is the number of elements of the array. - - @section secTrans Transactions - - Transactions are committed to main memory, - and are committed to disk after a global checkpoint, GCP. - 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 after 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 (parallel application programs, thread-based - applications) - sometimes deadlock when they try to access the same information. - Applications need to be programmed so that timeout errors - occurring due to deadlocks are handled. This generally - means that the transaction encountering timeout - should be rolled back and restarted. - - - @section secHint Hints and performance - - Placing the transaction coordinator close - to the actual data used in the transaction can in many cases - improve performance significantly. This is particularly true for - systems using TCP/IP. A system using Solaris and a 500 MHz processor - has a cost model for TCP/IP communication which is: - - 30 microseconds + (100 nanoseconds * no of Bytes) - - This means that if we can ensure that we use "popular" links we increase - buffering and thus drastically reduce the communication cost. - Systems using SCI has a different cost model which is: - - 5 microseconds + (10 nanoseconds * no of Bytes) - - Thus SCI systems are much less dependent on selection of - transaction coordinators. - Typically TCP/IP systems spend 30-60% of the time during communication, - whereas SCI systems typically spend 5-10% of the time during - communication. - Thus SCI means that less care from the NDB API programmer is - needed and great scalability can be achieved even for applications using - data from many 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 distribution key. - Then the keyData will be the address of the integer - of the primary key and keyLen will be 4. */ #ifndef Ndb_H diff --git a/ndb/include/ndbapi/NdbIndexScanOperation.hpp b/ndb/include/ndbapi/NdbIndexScanOperation.hpp index c94f48ea06b..6fe4dc3df84 100644 --- a/ndb/include/ndbapi/NdbIndexScanOperation.hpp +++ b/ndb/include/ndbapi/NdbIndexScanOperation.hpp @@ -34,16 +34,15 @@ class NdbIndexScanOperation : public NdbScanOperation { public: /** - * readTuples returns a NdbResultSet where tuples are stored. - * Tuples are not stored in NdbResultSet until execute(NoCommit) - * has been executed and nextResult has been called. + * readTuples using ordered index * - * @param parallel Scan parallelism + * @param lock_mode Lock mode * @param batch No of rows to fetch from each fragment at a time - * @param LockMode Scan lock handling + * @param parallel No of fragments to scan in parallel * @param order_by Order result set in index order - * @param order_desc Order descending, ignored unless order_by - * @returns NdbResultSet. + * @param order_desc Order descending, ignored unless order_by + * @param read_range_no Enable reading of range no using @ref get_range_no + * @returns 0 for success and -1 for failure * @see NdbScanOperation::readTuples */ int readTuples(LockMode = LM_Read, @@ -53,16 +52,6 @@ public: bool order_desc = false, bool read_range_no = false); -#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED - inline int readTuples(int parallell){ - return readTuples(LM_Read, 0, parallell, false); - } - - inline int readTuplesExclusive(int parallell = 0){ - return readTuples(LM_Exclusive, 0, parallell, false); - } -#endif - /** * Type of ordered index key bound. The values (0-4) will not change * and can be used explicitly (e.g. they could be computed). @@ -134,7 +123,14 @@ public: */ int get_range_no(); + /** + * Is current scan sorted + */ bool getSorted() const { return m_ordered; } + + /** + * Is current scan sorted descending + */ bool getDescending() const { return m_descending; } private: NdbIndexScanOperation(Ndb* aNdb); diff --git a/ndb/include/ndbapi/NdbOperation.hpp b/ndb/include/ndbapi/NdbOperation.hpp index 2e62b64492d..c9a961c519d 100644 --- a/ndb/include/ndbapi/NdbOperation.hpp +++ b/ndb/include/ndbapi/NdbOperation.hpp @@ -265,21 +265,6 @@ public: int equal(Uint32 anAttrId, Int64 aValue); int equal(Uint32 anAttrId, Uint64 aValue); - /** - * Generate a tuple id and set it as search argument. - * - * The Tuple id has NDB$TID as attribute name and 0 as attribute id. - * - * The generated tuple id is returned by the method. - * If zero is returned there is an error. - * - * This is mostly used for tables without any primary key - * attributes. - * - * @return Generated tuple id if successful, otherwise 0. - */ - Uint64 setTupleId(); - /** @} *********************************************************************/ /** * @name Specify Attribute Actions for Operations @@ -708,6 +693,7 @@ public: /** @} *********************************************************************/ +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL /** * Type of operation */ @@ -723,11 +709,16 @@ public: NotDefined2, ///< Internal for debugging NotDefined ///< Internal for debugging }; +#endif + /** + * Return lock mode for operation + */ LockMode getLockMode() const { return theLockMode; } - void setAbortOption(Int8 ao) { m_abortOption = ao; } #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + void setAbortOption(Int8 ao) { m_abortOption = ao; } + /** * Set/get distribution/partition key */ @@ -758,8 +749,10 @@ protected: void next(NdbOperation*); // Set next pointer NdbOperation* next(); // Get next pointer public: +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL const NdbOperation* next() const; const NdbRecAttr* getFirstRecAttr() const; +#endif protected: enum OperationStatus diff --git a/ndb/include/ndbapi/NdbRecAttr.hpp b/ndb/include/ndbapi/NdbRecAttr.hpp index 71c4d4e1247..32a75cee745 100644 --- a/ndb/include/ndbapi/NdbRecAttr.hpp +++ b/ndb/include/ndbapi/NdbRecAttr.hpp @@ -245,7 +245,9 @@ public: ~NdbRecAttr(); public: +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL const NdbRecAttr* next() const; +#endif private: NdbRecAttr(); diff --git a/ndb/include/ndbapi/NdbScanOperation.hpp b/ndb/include/ndbapi/NdbScanOperation.hpp index 983aa51fbee..58793c28750 100644 --- a/ndb/include/ndbapi/NdbScanOperation.hpp +++ b/ndb/include/ndbapi/NdbScanOperation.hpp @@ -37,16 +37,14 @@ class NdbScanOperation : public NdbOperation { public: /** - * readTuples returns a NdbResultSet where tuples are stored. - * Tuples are not stored in NdbResultSet until execute(NoCommit) - * has been executed and nextResult has been called. + * readTuples * - * @param parallel Scan parallelism + * @param lock_mode Lock mode * @param batch No of rows to fetch from each fragment at a time - * @param LockMode Scan lock handling + * @param parallel No of fragments to scan in parallell * @note specifying 0 for batch and parallall means max performance */ - int readTuples(LockMode = LM_Read, + int readTuples(LockMode lock_mode = LM_Read, Uint32 batch = 0, Uint32 parallel = 0); #ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED @@ -67,7 +65,7 @@ public: /** * Get the next tuple in a scan transaction. * - * After each call to NdbResult::nextResult + * After each call to nextResult * the buffers and NdbRecAttr objects defined in * NdbOperation::getValue are updated with values * from the scanned tuple. @@ -114,53 +112,31 @@ public: int nextResult(bool fetchAllowed = true, bool forceSend = false); /** - * Close result set (scan) + * Close scan */ void close(bool forceSend = false); /** - * Restart - */ - int restart(bool forceSend = false); - - /** - * Transfer scan operation to an updating transaction. Use this function - * when a scan has found a record that you want to update. - * 1. Start a new transaction. - * 2. Call the function takeOverForUpdate using your new transaction - * as parameter, all the properties of the found record will be copied - * to the new transaction. - * 3. When you execute the new transaction, the lock held by the scan will - * be transferred to the new transaction(it's taken over). + * Update current tuple * - * @note You must have started the scan with openScanExclusive - * to be able to update the found tuple. - * - * @param updateTrans the update transaction connection. * @return an NdbOperation or NULL. */ NdbOperation* updateCurrentTuple(); NdbOperation* updateCurrentTuple(NdbTransaction* updateTrans); /** - * Transfer scan operation to a deleting transaction. Use this function - * when a scan has found a record that you want to delete. - * 1. Start a new transaction. - * 2. Call the function takeOverForDelete using your new transaction - * as parameter, all the properties of the found record will be copied - * to the new transaction. - * 3. When you execute the new transaction, the lock held by the scan will - * be transferred to the new transaction(its taken over). - * - * @note You must have started the scan with openScanExclusive - * to be able to delete the found tuple. - * - * @param deleteTrans the delete transaction connection. - * @return an NdbOperation or NULL. + * Delete current tuple + * @return 0 on success or -1 on failure */ int deleteCurrentTuple(); int deleteCurrentTuple(NdbTransaction* takeOverTransaction); + /** + * Restart scan with exactly the same + * getValues and search conditions + */ + int restart(bool forceSend = false); + protected: NdbScanOperation(Ndb* aNdb); virtual ~NdbScanOperation(); diff --git a/ndb/include/ndbapi/NdbTransaction.hpp b/ndb/include/ndbapi/NdbTransaction.hpp index a2008b80988..10d641c022e 100644 --- a/ndb/include/ndbapi/NdbTransaction.hpp +++ b/ndb/include/ndbapi/NdbTransaction.hpp @@ -110,23 +110,6 @@ enum ExecType { * -# AbortOption::IgnoreError * Continue execution of transaction even if operation fails * - * NdbTransaction::execute can sometimes indicate an error - * (return with -1) while the error code on the NdbTransaction is 0. - * This is an indication that one of the operations found a record - * problem. The transaction is still ok and can continue as usual. - * The NdbTransaction::execute returns -1 together with error code - * on NdbTransaction object equal to 0 always means that an - * operation was not successful but that the total transaction was OK. - * By checking error codes on the individual operations it is possible - * to find out which operation was not successful. - * - * NdbTransaction::executeScan is used to setup a scan in the NDB kernel - * after it has been defined. - * NdbTransaction::nextScanResult is used to iterate through the - * scanned tuples. - * After each call to NdbTransaction::nextScanResult, the pointers - * of NdbRecAttr objects defined in the NdbOperation::getValue - * operations are updated with the values of the new the scanned tuple. */ /* FUTURE IMPLEMENTATION: @@ -376,6 +359,7 @@ public: #endif void close(); +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL /** * Restart transaction * @@ -385,6 +369,7 @@ public: * Note this method also releases completed operations */ int restart(); +#endif /** @} *********************************************************************/ @@ -494,7 +479,7 @@ public: */ const NdbOperation * getNextCompletedOperation(const NdbOperation * op)const; - +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL const NdbOperation* getFirstDefinedOperation()const{return theFirstOpInList;} const NdbOperation* getLastDefinedOperation()const{return theLastOpInList;} @@ -508,6 +493,7 @@ public: * ops are used (read, insert, update, delete). */ int executePendingBlobOps(Uint8 flags = 0xFF); +#endif private: /** diff --git a/ndb/src/ndbapi/NdbOperationSearch.cpp b/ndb/src/ndbapi/NdbOperationSearch.cpp index 65e2ab3e769..70850bcc66b 100644 --- a/ndb/src/ndbapi/NdbOperationSearch.cpp +++ b/ndb/src/ndbapi/NdbOperationSearch.cpp @@ -300,32 +300,6 @@ NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo, return -1; } -/****************************************************************************** - * Uint64 setTupleId( void ) - * - * Return Value: Return > 0: OK - * Return 0 : setTupleId failed - * Parameters: - * Remark: - *****************************************************************************/ -Uint64 -NdbOperation::setTupleId() -{ - if (theStatus != OperationDefined) - { - return 0; - } - Uint64 tTupleId = theNdb->getTupleIdFromNdb(m_currentTable->m_tableId); - if (tTupleId == ~(Uint64)0){ - setErrorCodeAbort(theNdb->theError.code); - return 0; - } - if (equal((Uint32)0, tTupleId) == -1) - return 0; - - return tTupleId; -} - /****************************************************************************** * int insertKEYINFO(const char* aValue, aStartPosition, * anAttrSizeInWords, Uint32 anAttrBitsInLastWord);