1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include <ndb_global.h>
26 #include <NdbTick.h>
27 #include <NdbOut.hpp>
28 #include "API.hpp"
29 
30 #include <AttributeHeader.hpp>
31 #include <signaldata/TcKeyConf.hpp>
32 #include <signaldata/TcIndx.hpp>
33 #include <signaldata/TcCommit.hpp>
34 #include <signaldata/TcKeyFailConf.hpp>
35 #include <signaldata/TcHbRep.hpp>
36 #include <signaldata/TcRollbackRep.hpp>
37 
38 static const Uint64 InvalidTransactionId = ~Uint64(0);
39 
40 /*****************************************************************************
41 NdbTransaction( Ndb* aNdb );
42 
43 Return Value:  None
44 Parameters:    aNdb: Pointers to the Ndb object
45 Remark:        Creates a connection object.
46 *****************************************************************************/
NdbTransaction(Ndb * aNdb)47 NdbTransaction::NdbTransaction( Ndb* aNdb ) :
48   theSendStatus(NotInit),
49   theCallbackFunction(NULL),
50   theCallbackObject(NULL),
51   theTransArrayIndex(0),
52   theStartTransTime(0),
53   theErrorLine(0),
54   theErrorOperation(NULL),
55   theNdb(aNdb),
56   theNext(NULL),
57   theFirstOpInList(NULL),
58   theLastOpInList(NULL),
59   theFirstExecOpInList(NULL),
60   theLastExecOpInList(NULL),
61   theCompletedFirstOp(NULL),
62   theCompletedLastOp(NULL),
63   theNoOfOpSent(0),
64   theNoOfOpCompleted(0),
65   theMyRef(0),
66   theTCConPtr(0),
67   theTransactionId(0),
68   theGlobalCheckpointId(0),
69   p_latest_trans_gci(0),
70   theStatus(NotConnected),
71   theCompletionStatus(NotCompleted),
72   theCommitStatus(NotStarted),
73   theMagicNumber(0xFE11DC),
74   theTransactionIsStarted(false),
75   theDBnode(0),
76   theReleaseOnClose(false),
77   // Scan operations
78   m_waitForReply(true),
79   m_theFirstScanOperation(NULL),
80   m_theLastScanOperation(NULL),
81   m_firstExecutedScanOp(NULL),
82   // Scan operations
83   theScanningOp(NULL),
84   theBuddyConPtr(0xFFFFFFFF),
85   theBlobFlag(false),
86   thePendingBlobOps(0),
87   maxPendingBlobReadBytes(~Uint32(0)),
88   maxPendingBlobWriteBytes(~Uint32(0)),
89   pendingBlobReadBytes(0),
90   pendingBlobWriteBytes(0),
91   // Lock handle
92   m_theFirstLockHandle(NULL),
93   m_theLastLockHandle(NULL),
94   // Composite query operations
95   m_firstQuery(NULL),
96   m_firstExecQuery(NULL),
97   m_firstActiveQuery(NULL),
98   m_scanningQuery(NULL),
99   //
100   m_tcRef(numberToRef(DBTC, 0)),
101   m_enable_schema_obj_owner_check(false)
102 {
103   theListState = NotInList;
104   theError.code = 0;
105   //theId = NdbObjectIdMap::InvalidId;
106   theId = theNdb->theImpl->theNdbObjectIdMap.map(this);
107 
108 #define CHECK_SZ(mask, sz) assert((sizeof(mask)/sizeof(mask[0])) == sz)
109 
110   CHECK_SZ(m_db_nodes, NdbNodeBitmask::Size);
111   CHECK_SZ(m_failed_db_nodes, NdbNodeBitmask::Size);
112 }//NdbTransaction::NdbTransaction()
113 
114 /*****************************************************************************
115 ~NdbTransaction();
116 
117 Remark:        Deletes the connection object.
118 *****************************************************************************/
~NdbTransaction()119 NdbTransaction::~NdbTransaction()
120 {
121   DBUG_ENTER("NdbTransaction::~NdbTransaction");
122   theNdb->theImpl->theNdbObjectIdMap.unmap(theId, this);
123   DBUG_VOID_RETURN;
124 }//NdbTransaction::~NdbTransaction()
125 
126 /*****************************************************************************
127 void init();
128 
129 Remark:         Initialise connection object for new transaction.
130 *****************************************************************************/
131 int
init()132 NdbTransaction::init()
133 {
134   theListState            = NotInList;
135   theInUseState           = true;
136   theTransactionIsStarted = false;
137   theNext		  = NULL;
138 
139   theFirstOpInList	  = NULL;
140   theLastOpInList	  = NULL;
141 
142   theScanningOp           = NULL;
143   m_scanningQuery         = NULL;
144 
145   theFirstExecOpInList	  = NULL;
146   theLastExecOpInList	  = NULL;
147 
148   theCompletedFirstOp	  = NULL;
149   theCompletedLastOp	  = NULL;
150 
151   theGlobalCheckpointId   = 0;
152   p_latest_trans_gci      =
153     theNdb->theImpl->m_ndb_cluster_connection.get_latest_trans_gci();
154   theCommitStatus         = Started;
155   theCompletionStatus     = NotCompleted;
156 
157   theError.code		  = 0;
158   theErrorLine		  = 0;
159   theErrorOperation	  = NULL;
160 
161   theReleaseOnClose       = false;
162   theSimpleState          = true;
163   theSendStatus           = InitState;
164   theMagicNumber          = getMagicNumber();
165 
166   // Query operations
167   m_firstQuery            = NULL;
168   m_firstExecQuery        = NULL;
169   m_firstActiveQuery      = NULL;
170 
171   // Scan operations
172   m_waitForReply          = true;
173   m_theFirstScanOperation = NULL;
174   m_theLastScanOperation  = NULL;
175   m_firstExecutedScanOp   = 0;
176   theBuddyConPtr          = 0xFFFFFFFF;
177   //
178   theBlobFlag = false;
179   thePendingBlobOps = 0;
180   m_theFirstLockHandle    = NULL;
181   m_theLastLockHandle     = NULL;
182   pendingBlobReadBytes = 0;
183   pendingBlobWriteBytes = 0;
184   if (theId == NdbObjectIdMap::InvalidId)
185   {
186     theId = theNdb->theImpl->theNdbObjectIdMap.map(this);
187     if (theId == NdbObjectIdMap::InvalidId)
188     {
189       theError.code = 4000;
190       return -1;
191     }
192   }
193   return 0;
194 
195 }//NdbTransaction::init()
196 
197 /*****************************************************************************
198 setOperationErrorCode(int error);
199 
200 Remark:        Sets an error code on the connection object from an
201                operation object.
202 *****************************************************************************/
203 void
setOperationErrorCode(int error)204 NdbTransaction::setOperationErrorCode(int error)
205 {
206   DBUG_ENTER("NdbTransaction::setOperationErrorCode");
207   setErrorCode(error);
208   DBUG_VOID_RETURN;
209 }
210 
211 /*****************************************************************************
212 setOperationErrorCodeAbort(int error);
213 
214 Remark:        Sets an error code on the connection object from an
215                operation object.
216 *****************************************************************************/
217 void
setOperationErrorCodeAbort(int error,int abortOption)218 NdbTransaction::setOperationErrorCodeAbort(int error, int abortOption)
219 {
220   DBUG_ENTER("NdbTransaction::setOperationErrorCodeAbort");
221   if (theTransactionIsStarted == false) {
222     theCommitStatus = Aborted;
223   } else if ((theCommitStatus != Committed) &&
224              (theCommitStatus != Aborted)) {
225     theCommitStatus = NeedAbort;
226   }//if
227   setErrorCode(error);
228   DBUG_VOID_RETURN;
229 }
230 
231 /*****************************************************************************
232 setErrorCode(int anErrorCode);
233 
234 Remark:        Sets an error indication on the connection object.
235 *****************************************************************************/
236 void
setErrorCode(int error)237 NdbTransaction::setErrorCode(int error)
238 {
239   DBUG_ENTER("NdbTransaction::setErrorCode");
240   DBUG_PRINT("enter", ("error: %d, theError.code: %d", error, theError.code));
241 
242   if (theError.code == 0)
243     theError.code = error;
244 
245   DBUG_VOID_RETURN;
246 }//NdbTransaction::setErrorCode()
247 
248 int
restart()249 NdbTransaction::restart(){
250   DBUG_ENTER("NdbTransaction::restart");
251   if(theCompletionStatus == CompletedSuccess){
252     releaseCompletedOperations();
253     releaseCompletedQueries();
254 
255     theTransactionId = theNdb->allocate_transaction_id();
256 
257     theCommitStatus = Started;
258     theCompletionStatus = NotCompleted;
259     theTransactionIsStarted = false;
260     DBUG_RETURN(0);
261   }
262   DBUG_PRINT("error",("theCompletionStatus != CompletedSuccess"));
263   DBUG_RETURN(-1);
264 }
265 
266 /*****************************************************************************
267 void handleExecuteCompletion(void);
268 
269 Remark:        Handle time-out on a transaction object.
270 *****************************************************************************/
271 void
handleExecuteCompletion()272 NdbTransaction::handleExecuteCompletion()
273 {
274   /***************************************************************************
275    *	  Move the NdbOperation objects from the list of executing
276    *      operations to list of completed
277    **************************************************************************/
278   NdbOperation* tFirstExecOp = theFirstExecOpInList;
279   NdbOperation* tLastExecOp = theLastExecOpInList;
280   if (tLastExecOp != NULL) {
281     tLastExecOp->next(theCompletedFirstOp);
282     theCompletedFirstOp = tFirstExecOp;
283     if (theCompletedLastOp == NULL)
284       theCompletedLastOp = tLastExecOp;
285     theFirstExecOpInList = NULL;
286     theLastExecOpInList = NULL;
287   }//if
288 
289   theSendStatus = InitState;
290   return;
291 }//NdbTransaction::handleExecuteCompletion()
292 
293 /*****************************************************************************
294 int execute(ExecType aTypeOfExec, CommitType aTypeOfCommit, int forceSend);
295 
296 Return Value:  Return 0 : execute was successful.
297                Return -1: In all other case.
298 Parameters :   aTypeOfExec: Type of execute.
299 Remark:        Initialise connection object for new transaction.
300 *****************************************************************************/
301 int
execute(ExecType aTypeOfExec,NdbOperation::AbortOption abortOption,int forceSend)302 NdbTransaction::execute(ExecType aTypeOfExec,
303 			NdbOperation::AbortOption abortOption,
304 			int forceSend)
305 {
306   NdbError existingTransError = theError;
307   NdbError firstTransError;
308   DBUG_ENTER("NdbTransaction::execute");
309   DBUG_PRINT("enter", ("aTypeOfExec: %d, abortOption: %d",
310 		       aTypeOfExec, abortOption));
311 
312   if (! theBlobFlag)
313     DBUG_RETURN(executeNoBlobs(aTypeOfExec, abortOption, forceSend));
314 
315   /*
316    * execute prepared ops in batches, as requested by blobs
317    * - blob error does not terminate execution
318    * - blob error sets error on operation
319    * - if error on operation skip blob calls
320    *
321    * In the call to preExecute(), each operation involving blobs can
322    * add (and execute) extra operations before (reads) and after
323    * (writes) the operation on the main row.
324    * In the call to postExecute(), each blob can add extra read and
325    * write operations to be executed immediately
326    * It is assumed that all operations added in preExecute() are
327    * defined 'before' operations added in postExecute().
328    * To facilitate this, the transaction's list of operations is
329    * pre-emptively split when a Blob operation is encountered.
330    * preExecute can add operations before and after the operation being
331    * processed, and if no batch execute is required, the list is rejoined.
332    * If batch execute is required, then execute() is performed, and then
333    * the postExecute() actions (which can add operations) are called before
334    * the list is rejoined.  See NdbBlob::preExecute() and
335    * NdbBlob::postExecute() for more info.
336    */
337 
338   NdbOperation* tPrepOp;
339 
340   if (abortOption != NdbOperation::DefaultAbortOption)
341   {
342     DBUG_PRINT("info", ("Forcing operations to take execute() abortOption %d",
343                         abortOption));
344     /* For Blobs, we have to execute with DefaultAbortOption
345      * If the user supplied a non default AbortOption to execute()
346      * then we need to make sure that all of the operations in their
347      * batch are set to use the supplied AbortOption so that the
348      * expected behaviour is obtained when executing below
349      */
350     tPrepOp= theFirstOpInList;
351     while(tPrepOp != NULL)
352     {
353       DBUG_PRINT("info", ("Changing abortOption from %d",
354                           tPrepOp->m_abortOption));
355       tPrepOp->m_abortOption= abortOption;
356       tPrepOp= tPrepOp->next();
357     }
358   }
359 
360 
361   ExecType tExecType;
362   NdbOperation* tCompletedFirstOp = NULL;
363   NdbOperation* tCompletedLastOp = NULL;
364 
365   int ret = 0;
366   do {
367     NdbOperation* firstSavedOp= NULL;
368     NdbOperation* lastSavedOp= NULL;
369 
370     tExecType = aTypeOfExec;
371     tPrepOp = theFirstOpInList;
372     while (tPrepOp != NULL) {
373       if (tPrepOp->theError.code == 0) {
374         bool batch = false;
375         NdbBlob* tBlob = tPrepOp->theBlobList;
376         if (tBlob !=NULL) {
377           /* We split the operation list just after this
378            * operation, in case it adds extra ops
379            */
380           firstSavedOp = tPrepOp->next(); // Could be NULL
381           lastSavedOp = theLastOpInList;
382           DBUG_PRINT("info", ("Splitting ops list between %p and %p",
383                               firstSavedOp, lastSavedOp));
384           tPrepOp->next(NULL);
385           theLastOpInList= tPrepOp;
386         }
387         while (tBlob != NULL) {
388           if (tBlob->preExecute(tExecType, batch) == -1)
389 	  {
390             ret = -1;
391 	    if (firstTransError.code==0)
392 	      firstTransError= theError;
393 	  }
394           tBlob = tBlob->theNext;
395         }
396         if (batch) {
397           // blob asked to execute all up to lastOpInBatch now
398           tExecType = NoCommit;
399           break;
400         }
401         else {
402           /* No batching yet - rejoin the current and
403            * saved operation lists
404            */
405           DBUG_PRINT("info", ("Rejoining ops list after preExecute between %p and %p",
406                               theLastOpInList,
407                               firstSavedOp));
408           if (firstSavedOp != NULL && lastSavedOp != NULL) {
409             if (theFirstOpInList == NULL)
410               theFirstOpInList = firstSavedOp;
411             else
412               theLastOpInList->next(firstSavedOp);
413             theLastOpInList = lastSavedOp;
414           }
415           firstSavedOp= lastSavedOp= NULL;
416         }
417       }
418       tPrepOp = tPrepOp->next();
419     }
420 
421     if (tExecType == Commit) {
422       NdbOperation* tOp = theCompletedFirstOp;
423       while (tOp != NULL) {
424         if (tOp->theError.code == 0) {
425           NdbBlob* tBlob = tOp->theBlobList;
426           while (tBlob != NULL) {
427             if (tBlob->preCommit() == -1)
428 	    {
429 	      ret = -1;
430 	      if (firstTransError.code==0)
431 		firstTransError= theError;
432 	    }
433             tBlob = tBlob->theNext;
434           }
435         }
436         tOp = tOp->next();
437       }
438     }
439 
440     // completed ops are in unspecified order
441     if (theCompletedFirstOp != NULL) {
442       if (tCompletedFirstOp == NULL) {
443         tCompletedFirstOp = theCompletedFirstOp;
444         tCompletedLastOp = theCompletedLastOp;
445       } else {
446         tCompletedLastOp->next(theCompletedFirstOp);
447         tCompletedLastOp = theCompletedLastOp;
448       }
449       theCompletedFirstOp = NULL;
450       theCompletedLastOp = NULL;
451     }
452 
453     if (executeNoBlobs(tExecType,
454 		       NdbOperation::DefaultAbortOption,
455 		       forceSend) == -1)
456     {
457       /**
458        * We abort the execute here. But we still need to put the split-off
459        * operation list back into the transaction object, or we will get a
460        * memory leak.
461        */
462       if (firstSavedOp != NULL && lastSavedOp != NULL) {
463         DBUG_PRINT("info", ("Rejoining ops list after postExecute between "
464                             "%p and %p", theLastOpInList, firstSavedOp));
465         if (theFirstOpInList == NULL)
466           theFirstOpInList = firstSavedOp;
467         else
468           theLastOpInList->next(firstSavedOp);
469         theLastOpInList = lastSavedOp;
470       }
471       if (tCompletedFirstOp != NULL) {
472         tCompletedLastOp->next(theCompletedFirstOp);
473         theCompletedFirstOp = tCompletedFirstOp;
474         if (theCompletedLastOp == NULL)
475           theCompletedLastOp = tCompletedLastOp;
476       }
477 
478       /* executeNoBlobs will have set transaction error */
479       DBUG_RETURN(-1);
480     }
481 
482     /* Capture any trans error left by the execute() in case it gets trampled */
483     if (firstTransError.code==0)
484       firstTransError= theError;
485 
486 #ifdef ndb_api_crash_on_complex_blob_abort
487     assert(theFirstOpInList == NULL && theLastOpInList == NULL);
488 #else
489     theFirstOpInList = theLastOpInList = NULL;
490 #endif
491 
492     {
493       NdbOperation* tOp = theCompletedFirstOp;
494       while (tOp != NULL) {
495         if (tOp->theError.code == 0) {
496           NdbBlob* tBlob = tOp->theBlobList;
497           while (tBlob != NULL) {
498             // may add new operations if batch
499             if (tBlob->postExecute(tExecType) == -1)
500 	    {
501               ret = -1;
502 	      if (firstTransError.code==0)
503 		firstTransError= theError;
504 	    }
505             tBlob = tBlob->theNext;
506           }
507         }
508         tOp = tOp->next();
509       }
510     }
511 
512     // Restore any saved prepared ops if we batched
513     if (firstSavedOp != NULL && lastSavedOp != NULL) {
514       DBUG_PRINT("info", ("Rejoining ops list after postExecute between %p and %p",
515                           theLastOpInList,
516                           firstSavedOp));
517       if (theFirstOpInList == NULL)
518         theFirstOpInList = firstSavedOp;
519       else
520         theLastOpInList->next(firstSavedOp);
521       theLastOpInList = lastSavedOp;
522     }
523     assert(theFirstOpInList == NULL || tExecType == NoCommit);
524   } while (theFirstOpInList != NULL || tExecType != aTypeOfExec);
525 
526   if (tCompletedFirstOp != NULL) {
527     tCompletedLastOp->next(theCompletedFirstOp);
528     theCompletedFirstOp = tCompletedFirstOp;
529     if (theCompletedLastOp == NULL)
530       theCompletedLastOp = tCompletedLastOp;
531   }
532 #if ndb_api_count_completed_ops_after_blob_execute
533   { NdbOperation* tOp; unsigned n = 0;
534     for (tOp = theCompletedFirstOp; tOp != NULL; tOp = tOp->next()) n++;
535     ndbout << "completed ops: " << n << endl;
536   }
537 #endif
538 
539   /* Sometimes the original error is trampled by 'Trans already aborted',
540    * detect this case and attempt to restore the original error
541    */
542   if (theError.code == 4350) // Trans already aborted
543   {
544     DBUG_PRINT("info", ("Trans already aborted, existingTransError.code %u, "
545                         "firstTransError.code %u",
546                         existingTransError.code,
547                         firstTransError.code));
548     if (existingTransError.code != 0)
549     {
550       theError = existingTransError;
551     }
552     else if (firstTransError.code != 0)
553     {
554       theError = firstTransError;
555     }
556   }
557 
558   /* Generally return the first error which we encountered as
559    * the Trans error.  Caller can traverse the op list to
560    * get the full picture
561    */
562   if (firstTransError.code != 0)
563   {
564     DBUG_PRINT("info", ("Setting error to first error.  firstTransError.code = %u, "
565                         "theError.code = %u",
566                         firstTransError.code,
567                         theError.code));
568     theError = firstTransError;
569   }
570 
571   DBUG_RETURN(ret);
572 }
573 
574 int
executeNoBlobs(NdbTransaction::ExecType aTypeOfExec,NdbOperation::AbortOption abortOption,int forceSend)575 NdbTransaction::executeNoBlobs(NdbTransaction::ExecType aTypeOfExec,
576 			       NdbOperation::AbortOption abortOption,
577 			       int forceSend)
578 {
579   DBUG_ENTER("NdbTransaction::executeNoBlobs");
580   DBUG_PRINT("enter", ("aTypeOfExec: %d, abortOption: %d",
581 		       aTypeOfExec, abortOption));
582 
583 //------------------------------------------------------------------------
584 // We will start by preparing all operations in the transaction defined
585 // since last execute or since beginning. If this works ok we will continue
586 // by calling the poll with wait method. This method will return when
587 // the NDB kernel has completed its task or when 10 seconds have passed.
588 // The NdbTransactionCallBack-method will receive the return code of the
589 // transaction. The normal methods of reading error codes still apply.
590 //------------------------------------------------------------------------
591   Ndb* tNdb = theNdb;
592 
593   Uint32 timeout = theNdb->theImpl->get_waitfor_timeout();
594   m_waitForReply = false;
595   executeAsynchPrepare(aTypeOfExec, NULL, NULL, abortOption);
596   if (m_waitForReply){
597     while (1) {
598       int noOfComp = tNdb->sendPollNdb(3 * timeout, 1, forceSend);
599       if (unlikely(noOfComp == 0)) {
600         /*
601          * Just for fun, this is only one of two places where
602          * we could hit this error... It's quite possible we
603          * hit it in Ndbif.cpp in Ndb::check_send_timeout()
604          *
605          * We behave rather similarly in both places.
606          * Hitting this is certainly a bug though...
607          */
608         g_eventLogger->error("WARNING: Timeout in executeNoBlobs() waiting for "
609                              "response from NDB data nodes. This should NEVER "
610                              "occur. You have likely hit a NDB Bug. Please "
611                              "file a bug.");
612         DBUG_PRINT("error",("This timeout should never occure, execute()"));
613         g_eventLogger->error("Forcibly trying to rollback txn (%p"
614                              ") to try to clean up data node resources.",
615                              this);
616         executeNoBlobs(NdbTransaction::Rollback);
617         theError.code = 4012;
618         theError.status= NdbError::PermanentError;
619         theError.classification= NdbError::TimeoutExpired;
620         setOperationErrorCodeAbort(4012); // ndbd timeout
621         DBUG_RETURN(-1);
622       }//if
623 
624       /*
625        * Check that the completed transactions include this one.  There
626        * could be another thread running asynchronously.  Even in pure
627        * async case rollback is done synchronously.
628        */
629       if (theListState != NotInList)
630         continue;
631 #ifdef VM_TRACE
632       unsigned anyway = 0;
633       for (unsigned i = 0; i < theNdb->theNoOfPreparedTransactions; i++)
634         anyway += theNdb->thePreparedTransactionsArray[i] == this;
635       for (unsigned i = 0; i < theNdb->theNoOfSentTransactions; i++)
636         anyway += theNdb->theSentTransactionsArray[i] == this;
637       for (unsigned i = 0; i < theNdb->theNoOfCompletedTransactions; i++)
638         anyway += theNdb->theCompletedTransactionsArray[i] == this;
639       if (anyway) {
640         theNdb->printState("execute %lx", (long)this);
641         abort();
642       }
643 #endif
644       if (theReturnStatus == ReturnFailure) {
645         DBUG_RETURN(-1);
646       }//if
647       break;
648     }
649   }
650   thePendingBlobOps = 0;
651   pendingBlobReadBytes = 0;
652   pendingBlobWriteBytes = 0;
653   DBUG_RETURN(0);
654 }//NdbTransaction::executeNoBlobs()
655 
656 /**
657  * Get the first query in the current transaction that has a lookup operation
658  * as its root.
659  */
getFirstLookupQuery(NdbQueryImpl * firstQuery)660 static NdbQueryImpl* getFirstLookupQuery(NdbQueryImpl* firstQuery)
661 {
662   NdbQueryImpl* current = firstQuery;
663   while (current != NULL && current->getQueryDef().isScanQuery()) {
664     current = current->getNext();
665   }
666   return current;
667 }
668 
669 /**
670  * Get the last query in the current transaction that has a lookup operation
671  * as its root.
672  */
getLastLookupQuery(NdbQueryImpl * firstQuery)673 static NdbQueryImpl* getLastLookupQuery(NdbQueryImpl* firstQuery)
674 {
675   NdbQueryImpl* current = firstQuery;
676   NdbQueryImpl* last = NULL;
677   while (current != NULL) {
678     if (!current->getQueryDef().isScanQuery()) {
679       last = current;
680     }
681     current = current->getNext();
682   }
683   return last;
684 }
685 
686 /*****************************************************************************
687 void executeAsynchPrepare(ExecType           aTypeOfExec,
688                           NdbAsynchCallback  callBack,
689                           void*              anyObject,
690                           CommitType         aTypeOfCommit);
691 
692 Return Value:  No return value
693 Parameters :   aTypeOfExec:   Type of execute.
694                anyObject:     An object provided in the callback method
695                callBack:      The callback method
696                aTypeOfCommit: What to do when read/updated/deleted records
697                               are missing or inserted records already exist.
698 
699 Remark:        Prepare a part of a transaction in an asynchronous manner.
700 *****************************************************************************/
701 void
executeAsynchPrepare(NdbTransaction::ExecType aTypeOfExec,NdbAsynchCallback aCallback,void * anyObject,NdbOperation::AbortOption abortOption)702 NdbTransaction::executeAsynchPrepare(NdbTransaction::ExecType aTypeOfExec,
703                                      NdbAsynchCallback  aCallback,
704                                      void*              anyObject,
705                                      NdbOperation::AbortOption abortOption)
706 {
707   DBUG_ENTER("NdbTransaction::executeAsynchPrepare");
708   DBUG_PRINT("enter", ("aTypeOfExec: %d, aCallback: 0x%lx, anyObject: Ox%lx",
709 		       aTypeOfExec, (long) aCallback, (long) anyObject));
710 
711   /**
712    * Reset error.code on execute
713    */
714 #ifndef NDEBUG
715   if (theError.code != 0)
716     DBUG_PRINT("enter", ("Resetting error %d on execute", theError.code));
717 #endif
718   {
719     switch (aTypeOfExec)
720     {
721     case NdbTransaction::Commit:
722       theNdb->theImpl->incClientStat(Ndb::TransCommitCount, 1);
723       break;
724     case NdbTransaction::Rollback:
725       theNdb->theImpl->incClientStat(Ndb::TransAbortCount, 1);
726       break;
727     default:
728       break;
729     }
730   }
731   /**
732    * for timeout (4012) we want sendROLLBACK to behave differently.
733    * Else, normal behaviour of reset errcode
734    */
735   if (theError.code != 4012)
736     theError.code = 0;
737 
738   /***************************************************************************
739    * Eager garbage collect queries which has completed execution
740    * w/ all its results made available to client.
741    * TODO: Add a member 'doEagerRelease' to check below.
742    **************************************************************************/
743   if (false) {
744     releaseCompletedQueries();
745   }
746 
747   bool tTransactionIsStarted = theTransactionIsStarted;
748   NdbOperation*	tLastOp = theLastOpInList;
749   Ndb* tNdb = theNdb;
750   CommitStatusType tCommitStatus = theCommitStatus;
751   Uint32 tnoOfPreparedTransactions = tNdb->theNoOfPreparedTransactions;
752 
753   theReturnStatus     = ReturnSuccess;
754   theCallbackFunction = aCallback;
755   theCallbackObject   = anyObject;
756   m_waitForReply = true;
757   tNdb->thePreparedTransactionsArray[tnoOfPreparedTransactions] = this;
758   theTransArrayIndex = tnoOfPreparedTransactions;
759   theListState = InPreparedList;
760   tNdb->theNoOfPreparedTransactions = tnoOfPreparedTransactions + 1;
761 
762   theNoOfOpSent		= 0;
763   theNoOfOpCompleted	= 0;
764   NdbNodeBitmask::clear(m_db_nodes);
765   NdbNodeBitmask::clear(m_failed_db_nodes);
766 
767   if ((tCommitStatus != Started) ||
768       (aTypeOfExec == Rollback)) {
769 /*****************************************************************************
770  *	Rollback have been ordered on a started transaction. Call rollback.
771  *      Could also be state problem or previous problem which leads to the
772  *      same action.
773  ****************************************************************************/
774     if (aTypeOfExec == Rollback) {
775       if (theTransactionIsStarted == false || theSimpleState) {
776 	theCommitStatus = Aborted;
777 	theSendStatus = sendCompleted;
778       } else {
779 	theSendStatus = sendABORT;
780       }
781     } else {
782       theSendStatus = sendABORTfail;
783     }//if
784     if (theCommitStatus == Aborted){
785       DBUG_PRINT("exit", ("theCommitStatus: Aborted"));
786       setErrorCode(4350);
787     }
788     DBUG_VOID_RETURN;
789   }//if
790 
791   NdbQueryImpl* const lastLookupQuery = getLastLookupQuery(m_firstQuery);
792 
793   if (tTransactionIsStarted == true) {
794     if (tLastOp != NULL) {
795       if (aTypeOfExec == Commit) {
796 /*****************************************************************************
797  *	Set commit indicator on last operation when commit has been ordered
798  *      and also a number of operations.
799 ******************************************************************************/
800         tLastOp->theCommitIndicator = 1;
801       }//if
802     } else if (lastLookupQuery != NULL) {
803       if (aTypeOfExec == Commit) {
804         lastLookupQuery->setCommitIndicator();
805       }
806     } else if (m_firstQuery == NULL) {
807       if (aTypeOfExec == Commit && !theSimpleState) {
808 	/**********************************************************************
809 	 *   A Transaction have been started and no more operations exist.
810 	 *   We will use the commit method.
811 	 *********************************************************************/
812         theSendStatus = sendCOMMITstate;
813 	DBUG_VOID_RETURN;
814       } else {
815 	/**********************************************************************
816 	 * We need to put it into the array of completed transactions to
817 	 * ensure that we report the completion in a proper way.
818 	 * We cannot do this here since that would endanger the completed
819 	 * transaction array since that is also updated from the receiver
820 	 * thread and thus we need to do it under mutex lock and thus we
821 	 * set the sendStatus to ensure that the send method will
822 	 * put it into the completed array.
823 	 **********************************************************************/
824         theSendStatus = sendCompleted;
825 	DBUG_VOID_RETURN; // No Commit with no operations is OK
826       }//if
827     }//if
828   } else if (tTransactionIsStarted == false) {
829     NdbOperation* tFirstOp = theFirstOpInList;
830 
831     /*
832      * Lookups that are roots of queries are sent before non-linked lookups.
833      * If both types are present, then the start indicator should be set
834      * on a query root lookup, and the commit indicator on a non-linked
835      * lookup.
836      */
837     if (lastLookupQuery != NULL) {
838       getFirstLookupQuery(m_firstQuery)->setStartIndicator();
839     } else if (tFirstOp != NULL) {
840       tFirstOp->setStartIndicator();
841     }
842 
843     if (tFirstOp != NULL) {
844       if (aTypeOfExec == Commit) {
845         tLastOp->theCommitIndicator = 1;
846       }//if
847     } else if (lastLookupQuery != NULL) {
848       if (aTypeOfExec == Commit) {
849         lastLookupQuery->setCommitIndicator();
850       }//if
851     } else if (m_firstQuery == NULL) {
852       /***********************************************************************
853        *    No operations are defined and we have not started yet.
854        *    Simply return OK. Set commit status if Commit.
855        ***********************************************************************/
856       if (aTypeOfExec == Commit) {
857         theCommitStatus = Committed;
858       }//if
859       /***********************************************************************
860        * We need to put it into the array of completed transactions to
861        * ensure that we report the completion in a proper way. We
862        * cannot do this here since that would endanger the completed
863        * transaction array since that is also updated from the
864        * receiver thread and thus we need to do it under mutex lock
865        * and thus we set the sendStatus to ensure that the send method
866        * will put it into the completed array.
867        ***********************************************************************/
868       theSendStatus = sendCompleted;
869       DBUG_VOID_RETURN;
870     }//if
871   }
872 
873   theCompletionStatus = NotCompleted;
874 
875   // Prepare sending of all pending NdbQuery's
876   if (m_firstQuery) {
877     NdbQueryImpl* query = m_firstQuery;
878     NdbQueryImpl* last = NULL;
879     while (query!=NULL) {
880       const int tReturnCode = query->prepareSend();
881       if (unlikely(tReturnCode != 0)) {
882         theSendStatus = sendABORTfail;
883         DBUG_VOID_RETURN;
884       }//if
885       last  = query;
886       query = query->getNext();
887     }
888     assert (m_firstExecQuery==NULL);
889     last->setNext(m_firstExecQuery);
890     m_firstExecQuery = m_firstQuery;
891     m_firstQuery = NULL;
892   }
893 
894   // Prepare sending of all pending (non-scan) NdbOperations's
895   NdbOperation* tOp = theFirstOpInList;
896   Uint32 pkOpCount = 0;
897   Uint32 ukOpCount = 0;
898   while (tOp) {
899     int tReturnCode;
900     NdbOperation* tNextOp = tOp->next();
901 
902     /* Count operation */
903     if (tOp->theTCREQ->theVerId_signalNumber == GSN_TCINDXREQ)
904       ukOpCount++;
905     else
906       pkOpCount++;
907 
908     if (tOp->Status() == NdbOperation::UseNdbRecord)
909       tReturnCode = tOp->prepareSendNdbRecord(abortOption);
910     else
911       tReturnCode= tOp->prepareSend(theTCConPtr, theTransactionId, abortOption);
912 
913     if (tReturnCode == -1) {
914       theSendStatus = sendABORTfail;
915       DBUG_VOID_RETURN;
916     }//if
917 
918     /*************************************************************************
919      * Now that we have successfully prepared the send of this operation we
920      * move it to the list of executing operations and remove it from the
921      * list of defined operations.
922      ************************************************************************/
923     tOp = tNextOp;
924   }
925 
926   theNdb->theImpl->incClientStat(Ndb::PkOpCount, pkOpCount);
927   theNdb->theImpl->incClientStat(Ndb::UkOpCount, ukOpCount);
928 
929   NdbOperation* tLastOpInList = theLastOpInList;
930   NdbOperation* tFirstOpInList = theFirstOpInList;
931 
932   theFirstOpInList = NULL;
933   theLastOpInList = NULL;
934   theFirstExecOpInList = tFirstOpInList;
935   theLastExecOpInList = tLastOpInList;
936 
937   theCompletionStatus = CompletedSuccess;
938   theSendStatus = sendOperations;
939   DBUG_VOID_RETURN;
940 }//NdbTransaction::executeAsynchPrepare()
941 
942 void
executeAsynch(ExecType aTypeOfExec,NdbAsynchCallback aCallback,void * anyObject,NdbOperation::AbortOption abortOption,int forceSend)943 NdbTransaction::executeAsynch(ExecType aTypeOfExec,
944                               NdbAsynchCallback aCallback,
945                               void* anyObject,
946                               NdbOperation::AbortOption abortOption,
947                               int forceSend)
948 {
949   executeAsynchPrepare(aTypeOfExec, aCallback, anyObject, abortOption);
950   theNdb->sendPreparedTransactions(forceSend);
951 }
952 
close()953 void NdbTransaction::close()
954 {
955   theNdb->closeTransaction(this);
956 }
957 
refresh()958 int NdbTransaction::refresh()
959 {
960   for(NdbIndexScanOperation* scan_op = m_firstExecutedScanOp;
961       scan_op != 0; scan_op = (NdbIndexScanOperation *) scan_op->theNext)
962   {
963     NdbTransaction* scan_trans = scan_op->theNdbCon;
964     if (scan_trans)
965     {
966       scan_trans->sendTC_HBREP();
967     }
968   }
969   return sendTC_HBREP();
970 }
971 
972 /*****************************************************************************
973 int sendTC_HBREP();
974 
975 Return Value:  No return value.
976 Parameters :   None.
977 Remark:        Order NDB to refresh the timeout counter of the transaction.
978 ******************************************************************************/
979 int
sendTC_HBREP()980 NdbTransaction::sendTC_HBREP()		// Send a TC_HBREP signal;
981 {
982   NdbApiSignal* tSignal;
983   Ndb* tNdb = theNdb;
984   Uint32 tTransId1, tTransId2;
985 
986   tSignal = tNdb->getSignal();
987   if (tSignal == NULL) {
988     return -1;
989   }
990 
991   if (tSignal->setSignal(GSN_TC_HBREP, refToBlock(m_tcRef)) == -1) {
992     return -1;
993   }
994 
995   TcHbRep * const tcHbRep = CAST_PTR(TcHbRep, tSignal->getDataPtrSend());
996 
997   tcHbRep->apiConnectPtr = theTCConPtr;
998 
999   tTransId1 = (Uint32) theTransactionId;
1000   tTransId2 = (Uint32) (theTransactionId >> 32);
1001   tcHbRep->transId1      = tTransId1;
1002   tcHbRep->transId2      = tTransId2;
1003 
1004   tNdb->theImpl->lock();
1005   const int res = tNdb->theImpl->sendSignal(tSignal,theDBnode);
1006   tNdb->theImpl->flush_send_buffers();
1007   tNdb->theImpl->unlock();
1008   tNdb->releaseSignal(tSignal);
1009 
1010   if (res == -1){
1011     return -1;
1012   }
1013 
1014   return 0;
1015 }//NdbTransaction::sendTC_HBREP()
1016 
1017 /*****************************************************************************
1018 int doSend();
1019 
1020 Return Value:  Return 0 : send was successful.
1021                Return -1: In all other case.
1022 Remark:        Send all operations and queries belonging to this connection.
1023                The caller of this method has the responsibility to remove the
1024                object from the prepared transactions array on the Ndb-object.
1025 *****************************************************************************/
1026 int
doSend()1027 NdbTransaction::doSend()
1028 {
1029   DBUG_ENTER("NdbTransaction::doSend");
1030   /*
1031   This method assumes that at least one operation or query have been defined.
1032   This is ensured by the caller of this routine (=execute).
1033   */
1034   NdbScanOperation* tcOp = m_theFirstScanOperation;
1035   if (tcOp != 0){
1036     // Execute any cursor operations
1037     while (tcOp != NULL) {
1038       int tReturnCode;
1039       tReturnCode = tcOp->executeCursor(theDBnode);
1040       /**
1041         Previously this code executed in executeAsynchPrepare and we
1042         had no way of reporting an error. Thus this particular failure
1043         won't immediately cause a transaction failure in the old code.
1044         To retain this old behaviour we don't cause a transaction
1045         failure here either. We avoid postExecuteRelease in the
1046         failure case just in case we want to analyse the error a
1047         bit more.
1048       */
1049       if (tReturnCode != -1) {
1050         tcOp->postExecuteRelease(); // Release unneeded resources
1051                                     // outside TP mutex
1052       }//if
1053       tcOp = (NdbScanOperation*)tcOp->next();
1054     } // while
1055     m_theLastScanOperation->next(m_firstExecutedScanOp);
1056     m_firstExecutedScanOp = m_theFirstScanOperation;
1057     // Discard cursor operations, since these are also
1058     // in the complete operations list we do not need
1059     // to release them.
1060     m_theFirstScanOperation = m_theLastScanOperation = NULL;
1061   }
1062 
1063   switch(theSendStatus){
1064   case sendOperations: {
1065     assert (m_firstExecQuery!=NULL || theFirstExecOpInList!=NULL);
1066 
1067     const NdbQueryImpl* const lastLookupQuery
1068       = getLastLookupQuery(m_firstExecQuery);
1069     if (m_firstExecQuery!=NULL) {
1070       NdbQueryImpl* query = m_firstExecQuery;
1071       NdbQueryImpl* last  = NULL;
1072       while (query!=NULL) {
1073         const bool lastFlag =
1074           query == lastLookupQuery && theFirstExecOpInList == NULL;
1075         const int tReturnCode = query->doSend(theDBnode, lastFlag);
1076         if (tReturnCode == -1) {
1077           goto fail;
1078         }
1079         last = query;
1080         query = query->getNext();
1081       } // while
1082 
1083       // Append to list of active queries
1084       last->setNext(m_firstActiveQuery);
1085       m_firstActiveQuery = m_firstExecQuery;
1086       m_firstExecQuery = NULL;
1087     }
1088 
1089     NdbOperation * tOp = theFirstExecOpInList;
1090     while (tOp != NULL) {
1091       NdbOperation* tNext = tOp->next();
1092       const Uint32 lastFlag = ((tNext == NULL) ? 1 : 0);
1093       const int tReturnCode = tOp->doSend(theDBnode, lastFlag);
1094       if (tReturnCode == -1) {
1095         goto fail;
1096       }//if
1097       tOp = tNext;
1098     }
1099 
1100     if (theFirstExecOpInList || lastLookupQuery != NULL) {
1101       theSendStatus = sendTC_OP;
1102       theTransactionIsStarted = true;
1103       theNdb->insert_sent_list(this);      // Lookup: completes with KEYCONF/REF
1104     } else {
1105       theSendStatus = sendCompleted;
1106       theNdb->insert_completed_list(this); // Scans query completes after send
1107     }
1108     DBUG_RETURN(0);
1109   }//case
1110   case sendABORT:
1111   case sendABORTfail:{
1112   /***********************************************************************
1113    * Rollback have been ordered on a not started transaction.
1114    * Simply return OK and set abort status.
1115    ***********************************************************************/
1116     if (theSendStatus == sendABORTfail) {
1117       theReturnStatus = ReturnFailure;
1118     }//if
1119     if (sendROLLBACK() == 0) {
1120       DBUG_RETURN(0);
1121     }//if
1122     break;
1123   }//case
1124   case sendCOMMITstate:
1125     if (sendCOMMIT() == 0) {
1126       DBUG_RETURN(0);
1127     }//if
1128     break;
1129   case sendCompleted:
1130     theNdb->insert_completed_list(this);
1131     DBUG_RETURN(0);
1132   default:
1133     ndbout << "Inconsistent theSendStatus = "
1134 	   << (Uint32) theSendStatus << endl;
1135     abort();
1136     break;
1137   }//switch
1138 
1139   theReleaseOnClose = true;
1140   theTransactionIsStarted = false;
1141   theCommitStatus = Aborted;
1142 fail:
1143   setOperationErrorCodeAbort(4002);
1144   DBUG_RETURN(-1);
1145 }//NdbTransaction::doSend()
1146 
1147 /**************************************************************************
1148 int sendROLLBACK();
1149 
1150 Return Value:  Return -1 if send unsuccessful.
1151 Parameters :   None.
1152 Remark:        Order NDB to rollback the transaction.
1153 **************************************************************************/
1154 int
sendROLLBACK()1155 NdbTransaction::sendROLLBACK()      // Send a TCROLLBACKREQ signal;
1156 {
1157   Ndb* tNdb = theNdb;
1158   if ((theTransactionIsStarted == true) &&
1159       (theCommitStatus != Committed) &&
1160       (theCommitStatus != Aborted)) {
1161 /**************************************************************************
1162  *	The user did not perform any rollback but simply closed the
1163  *      transaction. We must rollback Ndb since Ndb have been contacted.
1164  *************************************************************************/
1165     NdbApiSignal tSignal(tNdb->theMyRef);
1166     Uint32 tTransId1, tTransId2;
1167     NdbImpl * impl = theNdb->theImpl;
1168     int	  tReturnCode;
1169 
1170     tTransId1 = (Uint32) theTransactionId;
1171     tTransId2 = (Uint32) (theTransactionId >> 32);
1172     tSignal.setSignal(GSN_TCROLLBACKREQ, refToBlock(m_tcRef));
1173     tSignal.setData(theTCConPtr, 1);
1174     tSignal.setData(tTransId1, 2);
1175     tSignal.setData(tTransId2, 3);
1176     if(theError.code == 4012)
1177     {
1178       g_eventLogger->error("Sending TCROLLBACKREQ with Bad flag");
1179       tSignal.setLength(tSignal.getLength() + 1); // + flags
1180       tSignal.setData(0x1, 4); // potentially bad data
1181     }
1182     tReturnCode = impl->sendSignal(&tSignal,theDBnode);
1183     if (tReturnCode != -1) {
1184       theSendStatus = sendTC_ROLLBACK;
1185       tNdb->insert_sent_list(this);
1186       return 0;
1187     }//if
1188    /*********************************************************************
1189     * It was not possible to abort the transaction towards the NDB kernel
1190     * and thus we put it into the array of completed transactions that
1191     * are ready for reporting to the application.
1192     *********************************************************************/
1193     return -1;
1194   } else {
1195     /*
1196      It is not necessary to abort the transaction towards the NDB kernel and
1197      thus we put it into the array of completed transactions that are ready
1198      for reporting to the application.
1199      */
1200     theSendStatus = sendCompleted;
1201     tNdb->insert_completed_list(this);
1202     return 0;
1203     ;
1204   }//if
1205 }//NdbTransaction::sendROLLBACK()
1206 
1207 /***************************************************************************
1208 int sendCOMMIT();
1209 
1210 Return Value:  Return 0 : send was successful.
1211                Return -1: In all other case.
1212 Parameters :   None.
1213 Remark:        Order NDB to commit the transaction.
1214 ***************************************************************************/
1215 int
sendCOMMIT()1216 NdbTransaction::sendCOMMIT()    // Send a TC_COMMITREQ signal;
1217 {
1218   NdbApiSignal tSignal(theNdb->theMyRef);
1219   Uint32 tTransId1, tTransId2;
1220   NdbImpl * impl = theNdb->theImpl;
1221   int	  tReturnCode;
1222 
1223   tTransId1 = (Uint32) theTransactionId;
1224   tTransId2 = (Uint32) (theTransactionId >> 32);
1225   tSignal.setSignal(GSN_TC_COMMITREQ, refToBlock(m_tcRef));
1226   tSignal.setData(theTCConPtr, 1);
1227   tSignal.setData(tTransId1, 2);
1228   tSignal.setData(tTransId2, 3);
1229 
1230   tReturnCode = impl->sendSignal(&tSignal,theDBnode);
1231   if (tReturnCode != -1) {
1232     theSendStatus = sendTC_COMMIT;
1233     theNdb->insert_sent_list(this);
1234     return 0;
1235   } else {
1236     return -1;
1237   }//if
1238 }//NdbTransaction::sendCOMMIT()
1239 
1240 /******************************************************************************
1241 void release();
1242 
1243 Remark:         Release all operations.
1244 ******************************************************************************/
1245 void
release()1246 NdbTransaction::release(){
1247   releaseOperations();
1248   releaseLockHandles();
1249   if ( (theTransactionIsStarted == true) &&
1250        ((theCommitStatus != Committed) &&
1251 	(theCommitStatus != Aborted))) {
1252     /************************************************************************
1253      *	The user did not perform any rollback but simply closed the
1254      *      transaction. We must rollback Ndb since Ndb have been contacted.
1255      ************************************************************************/
1256     if (!theSimpleState)
1257     {
1258       execute(Rollback);
1259     }
1260   }//if
1261   theMagicNumber = 0xFE11DC;
1262   theInUseState = false;
1263 #ifdef VM_TRACE
1264   if (theListState != NotInList) {
1265     theNdb->printState("release %lx", (long)this);
1266     abort();
1267   }
1268 #endif
1269 }//NdbTransaction::release()
1270 
1271 void
releaseOps(NdbOperation * tOp)1272 NdbTransaction::releaseOps(NdbOperation* tOp){
1273   while (tOp != NULL) {
1274     NdbOperation* tmp = tOp;
1275     tOp->release();
1276     tOp = tOp->next();
1277     theNdb->releaseOperation(tmp);
1278   }//while
1279 }
1280 
1281 /******************************************************************************
1282 void releaseOperations();
1283 
1284 Remark:         Release all operations.
1285 ******************************************************************************/
1286 void
releaseOperations()1287 NdbTransaction::releaseOperations()
1288 {
1289   // Release any open scans
1290   releaseScanOperations(m_theFirstScanOperation);
1291   releaseScanOperations(m_firstExecutedScanOp);
1292 
1293   releaseQueries(m_firstQuery);
1294   releaseQueries(m_firstExecQuery);
1295   releaseQueries(m_firstActiveQuery);
1296   releaseOps(theCompletedFirstOp);
1297   releaseOps(theFirstOpInList);
1298   releaseOps(theFirstExecOpInList);
1299 
1300   theCompletedFirstOp = NULL;
1301   theCompletedLastOp = NULL;
1302   theFirstOpInList = NULL;
1303   theFirstExecOpInList = NULL;
1304   theLastOpInList = NULL;
1305   theLastExecOpInList = NULL;
1306   theScanningOp = NULL;
1307   m_scanningQuery = NULL;
1308   m_theFirstScanOperation = NULL;
1309   m_theLastScanOperation = NULL;
1310   m_firstExecutedScanOp = NULL;
1311   m_firstQuery = NULL;
1312   m_firstExecQuery = NULL;
1313   m_firstActiveQuery = NULL;
1314 
1315 }//NdbTransaction::releaseOperations()
1316 
1317 void
releaseCompletedOperations()1318 NdbTransaction::releaseCompletedOperations()
1319 {
1320   releaseOps(theCompletedFirstOp);
1321   theCompletedFirstOp = NULL;
1322   theCompletedLastOp = NULL;
1323 }//NdbTransaction::releaseCompletedOperations()
1324 
1325 
1326 void
releaseCompletedQueries()1327 NdbTransaction::releaseCompletedQueries()
1328 {
1329   /**
1330    * Find & release all active queries which as completed.
1331    */
1332   NdbQueryImpl* prev  = NULL;
1333   NdbQueryImpl* query = m_firstActiveQuery;
1334   while (query != NULL) {
1335     NdbQueryImpl* next = query->getNext();
1336 
1337     if (query->hasCompleted()) {
1338       // Unlink from completed-query list
1339       if (prev)
1340         prev->setNext(next);
1341       else
1342         m_firstActiveQuery = next;
1343 
1344       query->release();
1345     } else {
1346       prev = query;
1347     }
1348     query = next;
1349   } // while
1350 }//NdbTransaction::releaseCompletedQueries()
1351 
1352 
1353 /******************************************************************************
1354 void releaseQueries();
1355 
1356 Remark:         Release all queries
1357 ******************************************************************************/
1358 void
releaseQueries(NdbQueryImpl * query)1359 NdbTransaction::releaseQueries(NdbQueryImpl* query)
1360 {
1361   while (query != NULL) {
1362     NdbQueryImpl* next = query->getNext();
1363     query->release();
1364     query = next;
1365   }
1366 }//NdbTransaction::releaseQueries
1367 
1368 /******************************************************************************
1369 void releaseScanOperations();
1370 
1371 Remark:         Release all cursor operations.
1372                 (NdbScanOperation and NdbIndexOperation)
1373 ******************************************************************************/
1374 void
releaseScanOperations(NdbIndexScanOperation * cursorOp)1375 NdbTransaction::releaseScanOperations(NdbIndexScanOperation* cursorOp)
1376 {
1377   while(cursorOp != 0){
1378     NdbIndexScanOperation* next = (NdbIndexScanOperation*)cursorOp->next();
1379     cursorOp->release();
1380     theNdb->releaseScanOperation(cursorOp);
1381     cursorOp = next;
1382   }
1383 }//NdbTransaction::releaseScanOperations()
1384 
1385 bool
releaseScanOperation(NdbIndexScanOperation ** listhead,NdbIndexScanOperation ** listtail,NdbIndexScanOperation * op)1386 NdbTransaction::releaseScanOperation(NdbIndexScanOperation** listhead,
1387 				     NdbIndexScanOperation** listtail,
1388 				     NdbIndexScanOperation* op)
1389 {
1390   if (* listhead == op)
1391   {
1392     * listhead = (NdbIndexScanOperation*)op->theNext;
1393     if (listtail && *listtail == op)
1394     {
1395       assert(* listhead == 0);
1396       * listtail = 0;
1397     }
1398 
1399   }
1400   else
1401   {
1402     NdbIndexScanOperation* tmp = * listhead;
1403     while (tmp != NULL)
1404     {
1405       if (tmp->theNext == op)
1406       {
1407 	tmp->theNext = (NdbIndexScanOperation*)op->theNext;
1408 	if (listtail && *listtail == op)
1409 	{
1410 	  assert(op->theNext == 0);
1411 	  *listtail = tmp;
1412 	}
1413 	break;
1414       }
1415       tmp = (NdbIndexScanOperation*)tmp->theNext;
1416     }
1417     if (tmp == NULL)
1418       op = NULL;
1419   }
1420 
1421   if (op != NULL)
1422   {
1423     op->release();
1424     theNdb->releaseScanOperation(op);
1425     return true;
1426   }
1427 
1428   return false;
1429 }
1430 
1431 void
releaseLockHandles()1432 NdbTransaction::releaseLockHandles()
1433 {
1434   NdbLockHandle* lh = m_theFirstLockHandle;
1435 
1436   while (lh)
1437   {
1438     NdbLockHandle* next = lh->next();
1439     lh->next(NULL);
1440 
1441     theNdb->releaseLockHandle(lh);
1442     lh = next;
1443   }
1444 
1445   m_theFirstLockHandle = NULL;
1446   m_theLastLockHandle = NULL;
1447 }
1448 
1449 /*****************************************************************************
1450 NdbOperation* getNdbOperation(const char* aTableName);
1451 
1452 Return Value    Return a pointer to a NdbOperation object if getNdbOperation
1453                 was succesful.
1454                 Return NULL : In all other case.
1455 Parameters:     aTableName : Name of the database table.
1456 Remark:         Get an operation from NdbOperation idlelist and get the
1457                 NdbTransaction object
1458 		who was fetch by startTransaction pointing to this  operation
1459 		getOperation will set the theTableId in the NdbOperation object.
1460                 synchronous
1461 ******************************************************************************/
1462 NdbOperation*
getNdbOperation(const char * aTableName)1463 NdbTransaction::getNdbOperation(const char* aTableName)
1464 {
1465   if (theCommitStatus == Started){
1466     NdbTableImpl* table = theNdb->theDictionary->getTable(aTableName);
1467     if (table != 0){
1468       return getNdbOperation(table);
1469     } else {
1470       setErrorCode(theNdb->theDictionary->getNdbError().code);
1471       return NULL;
1472     }//if
1473   }
1474 
1475   setOperationErrorCodeAbort(4114);
1476 
1477   return NULL;
1478 }//NdbTransaction::getNdbOperation()
1479 
1480 /*****************************************************************************
1481 NdbTransaction::checkSchemaObjects(const NdbTableImpl *tab,
1482                                    const NdbIndexImpl *idx)
1483 Return value:    true if objects are all valid, false otherwise
1484 Parameters:      table, optional index
1485 Remark:          If the schema object ownership check is enabled while creating
1486                  the Ndb_cluster_connection, check that the connection is not
1487                  using schema objects which have been acquired by another
1488                  connection.
1489 *****************************************************************************/
1490 bool
checkSchemaObjects(const NdbTableImpl * tab,const NdbIndexImpl * idx)1491 NdbTransaction::checkSchemaObjects(const NdbTableImpl *tab,
1492                                    const NdbIndexImpl *idx)
1493 {
1494   bool ret = true;
1495   if(m_enable_schema_obj_owner_check)
1496   {
1497     if(tab->m_indexType != NdbDictionary::Object::TypeUndefined)
1498       return ret; // skip index table passed by getNdbIndexScanOperation
1499 
1500     // check that table and index objects are owned by current connection - get
1501     // dict objects from current connection and compare.
1502     char db[MAX_TAB_NAME_SIZE];
1503     tab->getDbName(db, sizeof(db));
1504 
1505     const char *old_db= theNdb->getDatabaseName();
1506 
1507     bool change_db= false;
1508     if(strcmp(db, old_db) != 0)
1509       change_db = true;
1510     if(change_db && (strcmp(db, "") != 0)) // switch to db of current table if not blank
1511       theNdb->setDatabaseName(db);
1512 
1513     NdbDictionary::Table *dictTab = NULL;
1514     NdbDictionary::Index *dictIdx = NULL;
1515 
1516     dictTab = theNdb->theDictionary->getTable(tab->getName());
1517     if(idx)
1518       dictIdx = theNdb->theDictionary->getIndex(idx->getName(), tab->getName());
1519 
1520     if(change_db && strcmp(old_db, "") != 0) // restore original value of db if not blank
1521       theNdb->setDatabaseName(old_db);
1522 
1523     if(dictTab && (dictTab->getObjectId() == tab->getObjectId())
1524                && (dictTab->getObjectVersion() == tab->getObjectVersion())
1525                && (tab != &(NdbTableImpl::getImpl(*dictTab))))
1526     {
1527       ndbout << "Schema object ownership check failed: table " << tab->getName()
1528              << " not owned by connection" << endl;
1529       ret = false;
1530     }
1531     if(idx && dictIdx && (dictTab->getObjectId() == idx->getObjectId())
1532                && (dictIdx->getObjectVersion() == idx->getObjectVersion())
1533                && (idx != &(NdbIndexImpl::getImpl(*dictIdx))))
1534     {
1535       ndbout << "Schema object ownership check failed: index "
1536              << idx->getName() << " not owned by connection" << endl;
1537       ret = false;
1538     }
1539   }
1540   return ret;
1541 } //NdbTransaction::checkSchemaObjects
1542 
1543 
1544 /*****************************************************************************
1545 NdbOperation* getNdbOperation(const NdbTableImpl* tab, NdbOperation* aNextOp,
1546                               bool useRec)
1547 
1548 Return Value    Return a pointer to a NdbOperation object if getNdbOperation
1549                 was succesful.
1550                 Return NULL: In all other case.
1551 Parameters:     tableId : Id of the database table beeing deleted.
1552 Remark:         Get an operation from NdbOperation object idlelist and
1553                 get the NdbTransaction object who was fetch by
1554                 startTransaction pointing to this operation
1555   	        getOperation will set the theTableId in the NdbOperation
1556                 object, synchronous.
1557 *****************************************************************************/
1558 NdbOperation*
getNdbOperation(const NdbTableImpl * tab,NdbOperation * aNextOp)1559 NdbTransaction::getNdbOperation(const NdbTableImpl * tab,
1560                                 NdbOperation* aNextOp)
1561 {
1562   NdbOperation* tOp;
1563 
1564   if (theScanningOp != NULL || m_scanningQuery != NULL){
1565     setErrorCode(4607);
1566     return NULL;
1567   }
1568 
1569   tOp = theNdb->getOperation();
1570   if (tOp == NULL)
1571     goto getNdbOp_error1;
1572 
1573   if (!checkSchemaObjects(tab))
1574   {
1575     setErrorCode(1231);
1576     return NULL;
1577   }
1578   if (aNextOp == NULL) {
1579     if (theLastOpInList != NULL) {
1580        theLastOpInList->next(tOp);
1581        theLastOpInList = tOp;
1582     } else {
1583        theLastOpInList = tOp;
1584        theFirstOpInList = tOp;
1585     }//if
1586     tOp->next(NULL);
1587   } else {
1588     // add before the given op
1589     if (theFirstOpInList == aNextOp) {
1590       theFirstOpInList = tOp;
1591     } else {
1592       NdbOperation* aLoopOp = theFirstOpInList;
1593       while (aLoopOp != NULL && aLoopOp->next() != aNextOp)
1594         aLoopOp = aLoopOp->next();
1595       assert(aLoopOp != NULL);
1596       aLoopOp->next(tOp);
1597     }
1598     tOp->next(aNextOp);
1599   }
1600   if (tOp->init(tab, this) != -1) {
1601     return tOp;
1602   } else {
1603     theNdb->releaseOperation(tOp);
1604   }//if
1605   return NULL;
1606 
1607  getNdbOp_error1:
1608   setOperationErrorCodeAbort(4000);
1609   return NULL;
1610 }//NdbTransaction::getNdbOperation()
1611 
getNdbOperation(const NdbDictionary::Table * table)1612 NdbOperation* NdbTransaction::getNdbOperation(const NdbDictionary::Table * table)
1613 {
1614   if (table)
1615     return getNdbOperation(& NdbTableImpl::getImpl(*table));
1616   else
1617     return NULL;
1618 }//NdbTransaction::getNdbOperation()
1619 
1620 
1621 // NdbScanOperation
1622 /*****************************************************************************
1623 NdbScanOperation* getNdbScanOperation(const char* aTableName);
1624 
1625 Return Value    Return a pointer to a NdbScanOperation object if getNdbScanOperation was succesful.
1626                 Return NULL : In all other case.
1627 Parameters:     aTableName : Name of the database table.
1628 Remark:         Get an operation from NdbScanOperation idlelist and get the NdbTransaction object
1629 		who was fetch by startTransaction pointing to this  operation
1630 		getOperation will set the theTableId in the NdbOperation object.synchronous
1631 ******************************************************************************/
1632 NdbScanOperation*
getNdbScanOperation(const char * aTableName)1633 NdbTransaction::getNdbScanOperation(const char* aTableName)
1634 {
1635   if (theCommitStatus == Started){
1636     NdbTableImpl* tab = theNdb->theDictionary->getTable(aTableName);
1637     if (tab != 0){
1638       return getNdbScanOperation(tab);
1639     } else {
1640       setOperationErrorCodeAbort(theNdb->theDictionary->m_error.code);
1641       return NULL;
1642     }//if
1643   }
1644 
1645   setOperationErrorCodeAbort(4114);
1646   return NULL;
1647 }//NdbTransaction::getNdbScanOperation()
1648 
1649 /*****************************************************************************
1650 NdbScanOperation* getNdbIndexScanOperation(const char* anIndexName, const char* aTableName);
1651 
1652 Return Value    Return a pointer to a NdbIndexScanOperation object if getNdbIndexScanOperation was succesful.
1653                 Return NULL : In all other case.
1654 Parameters:     anIndexName : Name of the index to use.
1655                 aTableName : Name of the database table.
1656 Remark:         Get an operation from NdbIndexScanOperation idlelist and get the NdbTransaction object
1657 		who was fetch by startTransaction pointing to this  operation
1658 		getOperation will set the theTableId in the NdbIndexScanOperation object.synchronous
1659 ******************************************************************************/
1660 NdbIndexScanOperation*
getNdbIndexScanOperation(const char * anIndexName,const char * aTableName)1661 NdbTransaction::getNdbIndexScanOperation(const char* anIndexName,
1662 					const char* aTableName)
1663 {
1664   NdbIndexImpl* index =
1665     theNdb->theDictionary->getIndex(anIndexName, aTableName);
1666   if (index == 0)
1667   {
1668     setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
1669     return 0;
1670   }
1671   NdbTableImpl* table = theNdb->theDictionary->getTable(aTableName);
1672   if (table == 0)
1673   {
1674     setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
1675     return 0;
1676   }
1677 
1678   return getNdbIndexScanOperation(index, table);
1679 }
1680 
1681 NdbIndexScanOperation*
getNdbIndexScanOperation(const NdbIndexImpl * index,const NdbTableImpl * table)1682 NdbTransaction::getNdbIndexScanOperation(const NdbIndexImpl* index,
1683 					const NdbTableImpl* table)
1684 {
1685   if (theCommitStatus == Started){
1686     const NdbTableImpl * indexTable = index->getIndexTable();
1687     if (indexTable != 0){
1688       NdbIndexScanOperation* tOp = getNdbScanOperation(indexTable);
1689       if (!checkSchemaObjects(table, index))
1690       {
1691         setErrorCode(1231);
1692         return NULL;
1693       }
1694       if(tOp)
1695       {
1696 	tOp->m_currentTable = table;
1697         // Mark that this really is an NdbIndexScanOperation
1698         tOp->m_type = NdbOperation::OrderedIndexScan;
1699       }
1700       return tOp;
1701     } else {
1702       setOperationErrorCodeAbort(4271);
1703       return NULL;
1704     }//if
1705   }
1706 
1707   setOperationErrorCodeAbort(4114);
1708   return NULL;
1709 }//NdbTransaction::getNdbIndexScanOperation()
1710 
1711 NdbIndexScanOperation*
getNdbIndexScanOperation(const NdbDictionary::Index * index)1712 NdbTransaction::getNdbIndexScanOperation(const NdbDictionary::Index * index)
1713 {
1714   if (index)
1715   {
1716     /* This fetches the underlying table being indexed. */
1717     const NdbDictionary::Table *table=
1718       theNdb->theDictionary->getTable(index->getTable());
1719 
1720     if (table)
1721       return getNdbIndexScanOperation(index, table);
1722 
1723     setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
1724     return NULL;
1725   }
1726   setOperationErrorCodeAbort(4271);
1727   return NULL;
1728 }
1729 
1730 NdbIndexScanOperation*
getNdbIndexScanOperation(const NdbDictionary::Index * index,const NdbDictionary::Table * table)1731 NdbTransaction::getNdbIndexScanOperation(const NdbDictionary::Index * index,
1732 					const NdbDictionary::Table * table)
1733 {
1734   if (index && table)
1735     return getNdbIndexScanOperation(& NdbIndexImpl::getImpl(*index),
1736 				    & NdbTableImpl::getImpl(*table));
1737   setOperationErrorCodeAbort(4271);
1738   return NULL;
1739 }//NdbTransaction::getNdbIndexScanOperation()
1740 
1741 /*****************************************************************************
1742 NdbScanOperation* getNdbScanOperation(int aTableId);
1743 
1744 Return Value    Return a pointer to a NdbScanOperation object if getNdbScanOperation was succesful.
1745                 Return NULL: In all other case.
1746 Parameters:     tableId : Id of the database table beeing deleted.
1747 Remark:         Get an operation from NdbScanOperation object idlelist and get the NdbTransaction
1748                 object who was fetch by startTransaction pointing to this  operation
1749   	        getOperation will set the theTableId in the NdbScanOperation object, synchronous.
1750 *****************************************************************************/
1751 NdbIndexScanOperation*
getNdbScanOperation(const NdbTableImpl * tab)1752 NdbTransaction::getNdbScanOperation(const NdbTableImpl * tab)
1753 {
1754   NdbIndexScanOperation* tOp;
1755 
1756   tOp = theNdb->getScanOperation();
1757   if (tOp == NULL)
1758     goto getNdbOp_error1;
1759   if (!checkSchemaObjects(tab))
1760   {
1761     setErrorCode(1231);
1762     return NULL;
1763   }
1764 
1765   if (tOp->init(tab, this) != -1) {
1766     define_scan_op(tOp);
1767     // Mark that this NdbIndexScanOperation is used as NdbScanOperation
1768     tOp->m_type = NdbOperation::TableScan;
1769     return tOp;
1770   } else {
1771     theNdb->releaseScanOperation(tOp);
1772   }//if
1773   return NULL;
1774 
1775 getNdbOp_error1:
1776   setOperationErrorCodeAbort(4000);
1777   return NULL;
1778 }//NdbTransaction::getNdbScanOperation()
1779 
1780 void
remove_list(NdbOperation * & list,NdbOperation * op)1781 NdbTransaction::remove_list(NdbOperation*& list, NdbOperation* op){
1782   NdbOperation* tmp= list;
1783   if(tmp == op)
1784     list = op->next();
1785   else {
1786     while(tmp && tmp->next() != op) tmp = tmp->next();
1787     if(tmp)
1788       tmp->next(op->next());
1789   }
1790   op->next(NULL);
1791 }
1792 
1793 void
define_scan_op(NdbIndexScanOperation * tOp)1794 NdbTransaction::define_scan_op(NdbIndexScanOperation * tOp){
1795   // Link scan operation into list of cursor operations
1796   if (m_theLastScanOperation == NULL)
1797     m_theFirstScanOperation = m_theLastScanOperation = tOp;
1798   else {
1799     m_theLastScanOperation->next(tOp);
1800     m_theLastScanOperation = tOp;
1801   }
1802   tOp->next(NULL);
1803 }
1804 
1805 NdbScanOperation*
getNdbScanOperation(const NdbDictionary::Table * table)1806 NdbTransaction::getNdbScanOperation(const NdbDictionary::Table * table)
1807 {
1808   if (table)
1809     return getNdbScanOperation(& NdbTableImpl::getImpl(*table));
1810   else
1811     return NULL;
1812 }//NdbTransaction::getNdbScanOperation()
1813 
1814 
1815 // IndexOperation
1816 /*****************************************************************************
1817 NdbIndexOperation* getNdbIndexOperation(const char* anIndexName,
1818 					const char* aTableName);
1819 
1820 Return Value    Return a pointer to an NdbIndexOperation object if
1821                 getNdbIndexOperation was succesful.
1822                 Return NULL : In all other case.
1823 Parameters:     aTableName : Name of the database table.
1824 Remark:         Get an operation from NdbIndexOperation idlelist and get the NdbTransaction object
1825 		who was fetch by startTransaction pointing to this operation
1826 		getOperation will set the theTableId in the NdbIndexOperation object.synchronous
1827 ******************************************************************************/
1828 NdbIndexOperation*
getNdbIndexOperation(const char * anIndexName,const char * aTableName)1829 NdbTransaction::getNdbIndexOperation(const char* anIndexName,
1830                                     const char* aTableName)
1831 {
1832   if (theCommitStatus == Started) {
1833     NdbTableImpl * table = theNdb->theDictionary->getTable(aTableName);
1834     NdbIndexImpl * index;
1835 
1836     if (table == 0)
1837     {
1838       setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
1839       return NULL;
1840     }
1841 
1842     if (table->m_frm.get_data())
1843     {
1844       // This unique index is defined from SQL level
1845       static const char* uniqueSuffix= "$unique";
1846       BaseString uniqueIndexName(anIndexName);
1847       uniqueIndexName.append(uniqueSuffix);
1848       index = theNdb->theDictionary->getIndex(uniqueIndexName.c_str(),
1849 					      aTableName);
1850     }
1851     else
1852       index = theNdb->theDictionary->getIndex(anIndexName,
1853 					      aTableName);
1854     if(table != 0 && index != 0){
1855       return getNdbIndexOperation(index, table);
1856     }
1857 
1858     if(index == 0){
1859       setOperationErrorCodeAbort(4243);
1860       return NULL;
1861     }
1862 
1863     setOperationErrorCodeAbort(4243);
1864     return NULL;
1865   }
1866 
1867   setOperationErrorCodeAbort(4114);
1868   return 0;
1869 }//NdbTransaction::getNdbIndexOperation()
1870 
1871 /*****************************************************************************
1872 NdbIndexOperation* getNdbIndexOperation(int anIndexId, int aTableId);
1873 
1874 Return Value    Return a pointer to a NdbIndexOperation object if getNdbIndexOperation was succesful.
1875                 Return NULL: In all other case.
1876 Parameters:     tableId : Id of the database table beeing deleted.
1877 Remark:         Get an operation from NdbIndexOperation object idlelist and get the NdbTransaction
1878                 object who was fetch by startTransaction pointing to this  operation
1879   	        getOperation will set the theTableId in the NdbIndexOperation object, synchronous.
1880 *****************************************************************************/
1881 NdbIndexOperation*
getNdbIndexOperation(const NdbIndexImpl * anIndex,const NdbTableImpl * aTable,NdbOperation * aNextOp)1882 NdbTransaction::getNdbIndexOperation(const NdbIndexImpl * anIndex,
1883                                      const NdbTableImpl * aTable,
1884                                      NdbOperation* aNextOp)
1885 {
1886   NdbIndexOperation* tOp;
1887 
1888   tOp = theNdb->getIndexOperation();
1889   if (tOp == NULL)
1890     goto getNdbOp_error1;
1891 
1892   if (!checkSchemaObjects(aTable, anIndex))
1893   {
1894     setErrorCode(1231);
1895     return NULL;
1896   }
1897   if (aNextOp == NULL) {
1898     if (theLastOpInList != NULL) {
1899        theLastOpInList->next(tOp);
1900        theLastOpInList = tOp;
1901     } else {
1902        theLastOpInList = tOp;
1903        theFirstOpInList = tOp;
1904     }//if
1905     tOp->next(NULL);
1906   } else {
1907     // add before the given op
1908     if (theFirstOpInList == aNextOp) {
1909       theFirstOpInList = tOp;
1910     } else {
1911       NdbOperation* aLoopOp = theFirstOpInList;
1912       while (aLoopOp != NULL && aLoopOp->next() != aNextOp)
1913         aLoopOp = aLoopOp->next();
1914       assert(aLoopOp != NULL);
1915       aLoopOp->next(tOp);
1916     }
1917     tOp->next(aNextOp);
1918   }
1919   if (tOp->indxInit(anIndex, aTable, this)!= -1) {
1920     return tOp;
1921   } else {
1922     theNdb->releaseOperation(tOp);
1923   }//if
1924   return NULL;
1925 
1926  getNdbOp_error1:
1927   setOperationErrorCodeAbort(4000);
1928   return NULL;
1929 }//NdbTransaction::getNdbIndexOperation()
1930 
1931 NdbIndexOperation*
getNdbIndexOperation(const NdbDictionary::Index * index)1932 NdbTransaction::getNdbIndexOperation(const NdbDictionary::Index * index)
1933 {
1934   if (index)
1935   {
1936     const NdbDictionary::Table *table=
1937       theNdb->theDictionary->getTable(index->getTable());
1938 
1939     if (table)
1940       return getNdbIndexOperation(index, table);
1941 
1942     setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
1943     return NULL;
1944   }
1945   setOperationErrorCodeAbort(4271);
1946   return NULL;
1947 }
1948 
1949 NdbIndexOperation*
getNdbIndexOperation(const NdbDictionary::Index * index,const NdbDictionary::Table * table)1950 NdbTransaction::getNdbIndexOperation(const NdbDictionary::Index * index,
1951 				    const NdbDictionary::Table * table)
1952 {
1953   if (index && table)
1954     return getNdbIndexOperation(& NdbIndexImpl::getImpl(*index),
1955 				& NdbTableImpl::getImpl(*table));
1956 
1957   setOperationErrorCodeAbort(4271);
1958   return NULL;
1959 }//NdbTransaction::getNdbIndexOperation()
1960 
1961 
1962 /*******************************************************************************
1963 int  receiveTCSEIZECONF(NdbApiSignal* aSignal);
1964 
1965 Return Value:  Return 0 : receiveTCSEIZECONF was successful.
1966                Return -1: In all other case.
1967 Parameters:    aSignal: The signal object pointer.
1968 Remark:        Sets TC Connect pointer at reception of TCSEIZECONF.
1969 *******************************************************************************/
1970 int
receiveTCSEIZECONF(const NdbApiSignal * aSignal)1971 NdbTransaction::receiveTCSEIZECONF(const NdbApiSignal* aSignal)
1972 {
1973   if (theStatus != Connecting)
1974   {
1975     return -1;
1976   } else
1977   {
1978     theTCConPtr = (Uint32)aSignal->readData(2);
1979     if (aSignal->getLength() >= 3)
1980     {
1981       m_tcRef = aSignal->readData(3);
1982     }
1983     else
1984     {
1985       m_tcRef = numberToRef(DBTC, theDBnode);
1986     }
1987 
1988     assert(m_tcRef == aSignal->theSendersBlockRef);
1989 
1990     theStatus = Connected;
1991   }
1992   return 0;
1993 }//NdbTransaction::receiveTCSEIZECONF()
1994 
1995 /*******************************************************************************
1996 int  receiveTCSEIZEREF(NdbApiSignal* aSignal);
1997 
1998 Return Value:  Return 0 : receiveTCSEIZEREF was successful.
1999                Return -1: In all other case.
2000 Parameters:    aSignal: The signal object pointer.
2001 Remark:        Sets TC Connect pointer.
2002 *******************************************************************************/
2003 int
receiveTCSEIZEREF(const NdbApiSignal * aSignal)2004 NdbTransaction::receiveTCSEIZEREF(const NdbApiSignal* aSignal)
2005 {
2006   DBUG_ENTER("NdbTransaction::receiveTCSEIZEREF");
2007   if (theStatus != Connecting)
2008   {
2009     DBUG_RETURN(-1);
2010   } else
2011   {
2012     theStatus = ConnectFailure;
2013     theNdb->theError.code = aSignal->readData(2);
2014     DBUG_PRINT("info",("error code %d, %s",
2015 		       theNdb->getNdbError().code,
2016 		       theNdb->getNdbError().message));
2017     DBUG_RETURN(0);
2018   }
2019 }//NdbTransaction::receiveTCSEIZEREF()
2020 
2021 /*******************************************************************************
2022 int  receiveTCRELEASECONF(NdbApiSignal* aSignal);
2023 
2024 Return Value:  Return 0 : receiveTCRELEASECONF was successful.
2025                Return -1: In all other case.
2026 Parameters:    aSignal: The signal object pointer.
2027 Remark:         DisConnect TC Connect pointer to NDBAPI.
2028 *******************************************************************************/
2029 int
receiveTCRELEASECONF(const NdbApiSignal * aSignal)2030 NdbTransaction::receiveTCRELEASECONF(const NdbApiSignal* aSignal)
2031 {
2032   if (theStatus != DisConnecting)
2033   {
2034     return -1;
2035   } else
2036   {
2037     theStatus = NotConnected;
2038   }
2039   return 0;
2040 }//NdbTransaction::receiveTCRELEASECONF()
2041 
2042 /*******************************************************************************
2043 int  receiveTCRELEASEREF(NdbApiSignal* aSignal);
2044 
2045 Return Value:  Return 0 : receiveTCRELEASEREF was successful.
2046                Return -1: In all other case.
2047 Parameters:    aSignal: The signal object pointer.
2048 Remark:        DisConnect TC Connect pointer to NDBAPI Failure.
2049 *******************************************************************************/
2050 int
receiveTCRELEASEREF(const NdbApiSignal * aSignal)2051 NdbTransaction::receiveTCRELEASEREF(const NdbApiSignal* aSignal)
2052 {
2053   if (theStatus != DisConnecting) {
2054     return -1;
2055   } else {
2056     theStatus = ConnectFailure;
2057     theNdb->theError.code = aSignal->readData(2);
2058     return 0;
2059   }//if
2060 }//NdbTransaction::receiveTCRELEASEREF()
2061 
2062 /******************************************************************************
2063 int  receiveTC_COMMITCONF(NdbApiSignal* aSignal);
2064 
2065 Return Value:  Return 0 : receiveTC_COMMITCONF was successful.
2066                Return -1: In all other case.
2067 Parameters:    aSignal: The signal object pointer.
2068 Remark:
2069 ******************************************************************************/
2070 int
receiveTC_COMMITCONF(const TcCommitConf * commitConf,Uint32 len)2071 NdbTransaction::receiveTC_COMMITCONF(const TcCommitConf * commitConf,
2072                                      Uint32 len)
2073 {
2074   if(checkState_TransId(&commitConf->transId1)){
2075     theCommitStatus = Committed;
2076     theCompletionStatus = CompletedSuccess;
2077     Uint32 tGCI_hi = commitConf->gci_hi;
2078     Uint32 tGCI_lo = commitConf->gci_lo;
2079     if (unlikely(len < TcCommitConf::SignalLength))
2080     {
2081       tGCI_lo = 0;
2082     }
2083     Uint64 tGCI = Uint64(tGCI_lo) | (Uint64(tGCI_hi) << 32);
2084     theGlobalCheckpointId = tGCI;
2085     // theGlobalCheckpointId == 0 if NoOp transaction
2086     if (tGCI)
2087       *p_latest_trans_gci = tGCI;
2088     return 0;
2089   } else {
2090 #ifdef NDB_NO_DROPPED_SIGNAL
2091     abort();
2092 #endif
2093   }
2094   return -1;
2095 }//NdbTransaction::receiveTC_COMMITCONF()
2096 
2097 /******************************************************************************
2098 int  receiveTC_COMMITREF(NdbApiSignal* aSignal);
2099 
2100 Return Value:  Return 0 : receiveTC_COMMITREF was successful.
2101                Return -1: In all other case.
2102 Parameters:    aSignal: The signal object pointer.
2103 Remark:
2104 ******************************************************************************/
2105 int
receiveTC_COMMITREF(const NdbApiSignal * aSignal)2106 NdbTransaction::receiveTC_COMMITREF(const NdbApiSignal* aSignal)
2107 {
2108   const TcCommitRef * ref = CAST_CONSTPTR(TcCommitRef, aSignal->getDataPtr());
2109   if(checkState_TransId(&ref->transId1)){
2110     setOperationErrorCodeAbort(ref->errorCode);
2111     theCommitStatus = Aborted;
2112     theCompletionStatus = CompletedFailure;
2113     theReturnStatus = ReturnFailure;
2114     theTransactionId = InvalidTransactionId; /* No further signals please */
2115     return 0;
2116   } else {
2117 #ifdef NDB_NO_DROPPED_SIGNAL
2118     abort();
2119 #endif
2120   }
2121 
2122   return -1;
2123 }//NdbTransaction::receiveTC_COMMITREF()
2124 
2125 /******************************************************************************
2126 int  receiveTCROLLBACKCONF(NdbApiSignal* aSignal);
2127 
2128 Return Value:  Return 0 : receiveTCROLLBACKCONF was successful.
2129                Return -1: In all other case.
2130 Parameters:    aSignal: The signal object pointer.
2131 Remark:
2132 ******************************************************************************/
2133 int
receiveTCROLLBACKCONF(const NdbApiSignal * aSignal)2134 NdbTransaction::receiveTCROLLBACKCONF(const NdbApiSignal* aSignal)
2135 {
2136   if(checkState_TransId(aSignal->getDataPtr() + 1)){
2137     theCommitStatus = Aborted;
2138     theCompletionStatus = CompletedSuccess;
2139     return 0;
2140   } else {
2141 #ifdef NDB_NO_DROPPED_SIGNAL
2142     abort();
2143 #endif
2144   }
2145 
2146   return -1;
2147 }//NdbTransaction::receiveTCROLLBACKCONF()
2148 
2149 /*******************************************************************************
2150 int  receiveTCROLLBACKREF(NdbApiSignal* aSignal);
2151 
2152 Return Value:  Return 0 : receiveTCROLLBACKREF was successful.
2153                Return -1: In all other case.
2154 Parameters:    aSignal: The signal object pointer.
2155 Remark:
2156 *******************************************************************************/
2157 int
receiveTCROLLBACKREF(const NdbApiSignal * aSignal)2158 NdbTransaction::receiveTCROLLBACKREF(const NdbApiSignal* aSignal)
2159 {
2160   if(checkState_TransId(aSignal->getDataPtr() + 1)){
2161     setOperationErrorCodeAbort(aSignal->readData(4));
2162     theCommitStatus = Aborted;
2163     theCompletionStatus = CompletedFailure;
2164     theReturnStatus = ReturnFailure;
2165     theTransactionId = InvalidTransactionId; /* No further signals please */
2166     return 0;
2167   } else {
2168 #ifdef NDB_NO_DROPPED_SIGNAL
2169     abort();
2170 #endif
2171   }
2172 
2173   return -1;
2174 }//NdbTransaction::receiveTCROLLBACKREF()
2175 
2176 /*****************************************************************************
2177 int receiveTCROLLBACKREP( NdbApiSignal* aSignal)
2178 
2179 Return Value:   Return 0 : send was succesful.
2180                 Return -1: In all other case.
2181 Parameters:     aSignal: the signal object that contains the
2182                 TCROLLBACKREP signal from TC.
2183 Remark:         Handles the reception of the ROLLBACKREP signal.
2184 *****************************************************************************/
2185 int
receiveTCROLLBACKREP(const NdbApiSignal * aSignal)2186 NdbTransaction::receiveTCROLLBACKREP( const NdbApiSignal* aSignal)
2187 {
2188   DBUG_ENTER("NdbTransaction::receiveTCROLLBACKREP");
2189 
2190   /****************************************************************************
2191 Check that we are expecting signals from this transaction and that it doesn't
2192 belong to a transaction already completed. Simply ignore messages from other
2193 transactions.
2194   ****************************************************************************/
2195   if(checkState_TransId(aSignal->getDataPtr() + 1)){
2196     theError.code = aSignal->readData(4);// Override any previous errors
2197     if (aSignal->getLength() == TcRollbackRep::SignalLength)
2198     {
2199       // Signal may contain additional error data
2200       theError.details = (char *)UintPtr(aSignal->readData(5));
2201     }
2202 
2203     /**********************************************************************/
2204     /*	A serious error has occured. This could be due to deadlock or */
2205     /*	lack of resources or simply a programming error in NDB. This  */
2206     /*	transaction will be aborted. Actually it has already been     */
2207     /*	and we only need to report completion and return with the     */
2208     /*	error code to the application.				      */
2209     /**********************************************************************/
2210     theTransactionId = InvalidTransactionId; /* No further signals please */
2211     theCompletionStatus = CompletedFailure;
2212     theCommitStatus = Aborted;
2213     theReturnStatus = ReturnFailure;
2214     DBUG_RETURN(0);
2215   } else {
2216 #ifdef NDB_NO_DROPPED_SIGNAL
2217     abort();
2218 #endif
2219   }
2220 
2221   DBUG_RETURN(-1);
2222 }//NdbTransaction::receiveTCROLLBACKREP()
2223 
2224 /*******************************************************************************
2225 int  receiveTCKEYCONF(NdbApiSignal* aSignal, Uint32 long_short_ind);
2226 
2227 Return Value:  Return 0 : receiveTCKEYCONF was successful.
2228                Return -1: In all other case.
2229 Parameters:    aSignal: The signal object pointer.
2230 Remark:
2231 *******************************************************************************/
2232 int
receiveTCKEYCONF(const TcKeyConf * keyConf,Uint32 aDataLength)2233 NdbTransaction::receiveTCKEYCONF(const TcKeyConf * keyConf, Uint32 aDataLength)
2234 {
2235   const Uint32 tTemp = keyConf->confInfo;
2236   /***************************************************************************
2237 Check that we are expecting signals from this transaction and that it
2238 doesn't belong to a transaction already completed. Simply ignore messages
2239 from other transactions.
2240   ***************************************************************************/
2241   if(checkState_TransId(&keyConf->transId1)){
2242 
2243     const Uint32 tNoOfOperations = TcKeyConf::getNoOfOperations(tTemp);
2244     const Uint32 tCommitFlag = TcKeyConf::getCommitFlag(tTemp);
2245 
2246     const Uint32* tPtr = (Uint32 *)&keyConf->operations[0];
2247     Uint32 tNoComp = theNoOfOpCompleted;
2248     for (Uint32 i = 0; i < tNoOfOperations ; i++) {
2249       NdbReceiver* const tReceiver =
2250         NdbImpl::void2rec(theNdb->theImpl->int2void(*tPtr++));
2251       const Uint32 tAttrInfoLen = *tPtr++;
2252       if(tReceiver && tReceiver->checkMagicNumber()){
2253         Uint32 done;
2254         if(tReceiver->getType()==NdbReceiver::NDB_QUERY_OPERATION){
2255           /* This signal is part of a linked operation.*/
2256           done = ((NdbQueryOperationImpl*)(tReceiver->m_owner))
2257             ->getQuery().execTCKEYCONF();
2258         }else{
2259           done = tReceiver->execTCOPCONF(tAttrInfoLen);
2260         }
2261 	if(tAttrInfoLen > TcKeyConf::DirtyReadBit){
2262 	  Uint32 node = tAttrInfoLen & (~TcKeyConf::DirtyReadBit);
2263           NdbNodeBitmask::set(m_db_nodes, node);
2264           if(NdbNodeBitmask::get(m_failed_db_nodes, node) && !done)
2265 	  {
2266             done = 1;
2267             // 4119 = "Simple/dirty read failed due to node failure"
2268             tReceiver->setErrorCode(4119);
2269             theCompletionStatus = CompletedFailure;
2270             theReturnStatus = NdbTransaction::ReturnFailure;
2271 	  }
2272 	}
2273 	tNoComp += done;
2274       } else { // if(tReceiver && tReceiver->checkMagicNumber())
2275  	return -1;
2276       }//if
2277     }//for
2278     theNoOfOpCompleted = tNoComp;
2279     const Uint32 tNoSent = theNoOfOpSent;
2280     const Uint32 tGCI_hi = keyConf->gci_hi;
2281     Uint32       tGCI_lo = * tPtr; // After op(s)
2282     if (unlikely(aDataLength < TcKeyConf::StaticLength+1 + 2*tNoOfOperations))
2283     {
2284       tGCI_lo = 0;
2285     }
2286     const Uint64 tGCI = Uint64(tGCI_lo) | (Uint64(tGCI_hi) << 32);
2287     if (tCommitFlag == 1)
2288     {
2289       theCommitStatus = Committed;
2290       theGlobalCheckpointId = tGCI;
2291       if (tGCI) // Read(dirty) only transaction doesnt get GCI
2292       {
2293 	*p_latest_trans_gci = tGCI;
2294       }
2295     }
2296     else if (theLastExecOpInList &&
2297              theLastExecOpInList->theCommitIndicator == 1)
2298     {
2299       /**
2300        * We're waiting for a commit reply...
2301        */
2302       return -1;
2303     }//if
2304     if (tNoComp >= tNoSent)
2305     {
2306       return 0;	// No more operations to wait for
2307     }//if
2308      // Not completed the reception yet.
2309   } else {
2310 #ifdef NDB_NO_DROPPED_SIGNAL
2311     abort();
2312 #endif
2313   }
2314 
2315   return -1;
2316 }//NdbTransaction::receiveTCKEYCONF()
2317 
2318 /*****************************************************************************
2319 int receiveTCKEY_FAILCONF( NdbApiSignal* aSignal)
2320 
2321 Return Value:   Return 0 : receive was completed.
2322                 Return -1: In all other case.
2323 Parameters:     aSignal: the signal object that contains the
2324                 TCKEY_FAILCONF signal from TC.
2325 Remark:         Handles the reception of the TCKEY_FAILCONF signal.
2326 *****************************************************************************/
2327 int
receiveTCKEY_FAILCONF(const TcKeyFailConf * failConf)2328 NdbTransaction::receiveTCKEY_FAILCONF(const TcKeyFailConf * failConf)
2329 {
2330   NdbOperation*	tOp;
2331   /*
2332     Check that we are expecting signals from this transaction and that it
2333     doesn't belong  to a transaction already completed. Simply ignore
2334     messages from other transactions.
2335   */
2336   if(checkState_TransId(&failConf->transId1)){
2337     /*
2338       A node failure of the TC node occured. The transaction has
2339       been committed.
2340     */
2341     theCommitStatus = Committed;
2342     theTransactionId = InvalidTransactionId; /* No further signals please */
2343     tOp = theFirstExecOpInList;
2344     while (tOp != NULL) {
2345       /*
2346        * Check if the transaction expected read values...
2347        * If it did some of them might have gotten lost even if we succeeded
2348        * in committing the transaction.
2349        */
2350       switch(tOp->theOperationType){
2351       case NdbOperation::UpdateRequest:
2352       case NdbOperation::InsertRequest:
2353       case NdbOperation::DeleteRequest:
2354       case NdbOperation::WriteRequest:
2355       case NdbOperation::UnlockRequest:
2356       case NdbOperation::RefreshRequest:
2357 	tOp = tOp->next();
2358 	break;
2359       case NdbOperation::ReadRequest:
2360       case NdbOperation::ReadExclusive:
2361       case NdbOperation::OpenScanRequest:
2362       case NdbOperation::OpenRangeScanRequest:
2363 	theCompletionStatus = CompletedFailure;
2364 	theReturnStatus = NdbTransaction::ReturnFailure;
2365 	setOperationErrorCodeAbort(4115);
2366 	tOp = NULL;
2367 	break;
2368       case NdbOperation::NotDefined:
2369       case NdbOperation::NotDefined2:
2370 	assert(false);
2371 	break;
2372       }//if
2373     }//while
2374     theReleaseOnClose = true;
2375     return 0;
2376   } else {
2377 #ifdef VM_TRACE
2378     ndbout_c("Recevied TCKEY_FAILCONF wo/ operation");
2379 #endif
2380   }
2381   return -1;
2382 }//NdbTransaction::receiveTCKEY_FAILCONF()
2383 
2384 /*************************************************************************
2385 int receiveTCKEY_FAILREF( NdbApiSignal* aSignal)
2386 
2387 Return Value:   Return 0 : receive was completed.
2388                 Return -1: In all other case.
2389 Parameters:     aSignal: the signal object that contains the
2390                 TCKEY_FAILREF signal from TC.
2391 Remark:         Handles the reception of the TCKEY_FAILREF signal.
2392 **************************************************************************/
2393 int
receiveTCKEY_FAILREF(const NdbApiSignal * aSignal)2394 NdbTransaction::receiveTCKEY_FAILREF(const NdbApiSignal* aSignal)
2395 {
2396   /*
2397     Check that we are expecting signals from this transaction and
2398     that it doesn't belong to a transaction already
2399     completed. Simply ignore messages from other transactions.
2400   */
2401   if(checkState_TransId(aSignal->getDataPtr()+1)){
2402     /*
2403       We received an indication of that this transaction was aborted due to a
2404       node failure.
2405     */
2406     if (theSendStatus == NdbTransaction::sendTC_ROLLBACK) {
2407       /*
2408 	We were in the process of sending a rollback anyways. We will
2409 	report it as a success.
2410       */
2411       theCompletionStatus = NdbTransaction::CompletedSuccess;
2412     } else {
2413       theReturnStatus = NdbTransaction::ReturnFailure;
2414       theCompletionStatus = NdbTransaction::CompletedFailure;
2415       theError.code = 4031;
2416     }//if
2417     theReleaseOnClose = true;
2418     theCommitStatus = NdbTransaction::Aborted;
2419     theTransactionId = InvalidTransactionId; /* No further signals please */
2420     return 0;
2421   } else {
2422 #ifdef VM_TRACE
2423     ndbout_c("Recevied TCKEY_FAILREF wo/ operation");
2424 #endif
2425   }
2426   return -1;
2427 }//NdbTransaction::receiveTCKEY_FAILREF()
2428 
2429 /*******************************************************************************
2430 int OpCompletedFailure();
2431 
2432 Return Value:  Return 0 : OpCompleteSuccess was successful.
2433                Return -1: In all other case.
2434 Remark:        An operation was completed with failure.
2435 *******************************************************************************/
2436 int
OpCompleteFailure()2437 NdbTransaction::OpCompleteFailure()
2438 {
2439   Uint32 tNoComp = theNoOfOpCompleted;
2440   Uint32 tNoSent = theNoOfOpSent;
2441 
2442   tNoComp++;
2443   theNoOfOpCompleted = tNoComp;
2444 
2445   return (tNoComp == tNoSent) ? 0 : -1;
2446 }//NdbTransaction::OpCompleteFailure()
2447 
2448 /******************************************************************************
2449 int OpCompleteSuccess();
2450 
2451 Return Value:  Return 0 : OpCompleteSuccess was successful.
2452                Return -1: In all other case.
2453 Remark:        An operation was completed with success.
2454 *******************************************************************************/
2455 int
OpCompleteSuccess()2456 NdbTransaction::OpCompleteSuccess()
2457 {
2458   Uint32 tNoComp = theNoOfOpCompleted;
2459   Uint32 tNoSent = theNoOfOpSent;
2460   tNoComp++;
2461   theNoOfOpCompleted = tNoComp;
2462 #ifdef JW_TEST
2463   ndbout << "NdbTransaction::OpCompleteSuccess() tNoComp=" << tNoComp
2464 	 << " tNoSent=" << tNoSent << endl;
2465 #endif
2466   if (tNoComp == tNoSent) { // Last operation completed
2467     return 0;
2468   } else if (tNoComp < tNoSent) {
2469     return -1;	// Continue waiting for more signals
2470   } else {
2471     setOperationErrorCodeAbort(4113);	// Too many operations,
2472                                         // stop waiting for more
2473     theCompletionStatus = NdbTransaction::CompletedFailure;
2474     theReturnStatus = NdbTransaction::ReturnFailure;
2475     return 0;
2476   }//if
2477 }//NdbTransaction::OpCompleteSuccess()
2478 
2479 /******************************************************************************
2480  int            getGCI();
2481 
2482 Remark:		Get global checkpoint identity of the transaction
2483 *******************************************************************************/
2484 int
getGCI()2485 NdbTransaction::getGCI()
2486 {
2487   Uint64 val;
2488   if (getGCI(&val) == 0)
2489   {
2490     return (int)(val >> 32);
2491   }
2492   return -1;
2493 }
2494 
2495 int
getGCI(Uint64 * val)2496 NdbTransaction::getGCI(Uint64 * val)
2497 {
2498   if (theCommitStatus == NdbTransaction::Committed)
2499   {
2500     if (val)
2501     {
2502       * val = theGlobalCheckpointId;
2503     }
2504     return 0;
2505   }
2506   return -1;
2507 }
2508 
2509 /*******************************************************************************
2510 Uint64 getTransactionId(void);
2511 
2512 Remark:        Get the transaction identity.
2513 *******************************************************************************/
2514 Uint64
getTransactionId()2515 NdbTransaction::getTransactionId()
2516 {
2517   return theTransactionId;
2518 }//NdbTransaction::getTransactionId()
2519 
2520 NdbTransaction::CommitStatusType
commitStatus()2521 NdbTransaction::commitStatus()
2522 {
2523   return theCommitStatus;
2524 }//NdbTransaction::commitStatus()
2525 
2526 int
getNdbErrorLine()2527 NdbTransaction::getNdbErrorLine()
2528 {
2529   return theErrorLine;
2530 }
2531 
2532 NdbOperation*
getNdbErrorOperation()2533 NdbTransaction::getNdbErrorOperation()
2534 {
2535   return theErrorOperation;
2536 }//NdbTransaction::getNdbErrorOperation()
2537 
2538 
2539 const NdbOperation*
getNdbErrorOperation() const2540 NdbTransaction::getNdbErrorOperation() const
2541 {
2542   return theErrorOperation;
2543 }//NdbTransaction::getNdbErrorOperation()
2544 
2545 
2546 const NdbOperation *
getNextCompletedOperation(const NdbOperation * current) const2547 NdbTransaction::getNextCompletedOperation(const NdbOperation * current) const {
2548   if(current == 0)
2549     return theCompletedFirstOp;
2550   return current->theNext;
2551 }
2552 
2553 NdbOperation *
setupRecordOp(NdbOperation::OperationType type,NdbOperation::LockMode lock_mode,NdbOperation::AbortOption default_ao,const NdbRecord * key_record,const char * key_row,const NdbRecord * attribute_record,const char * attribute_row,const unsigned char * mask,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions,const NdbLockHandle * lh)2554 NdbTransaction::setupRecordOp(NdbOperation::OperationType type,
2555                               NdbOperation::LockMode lock_mode,
2556                               NdbOperation::AbortOption default_ao,
2557                               const NdbRecord *key_record,
2558                               const char *key_row,
2559                               const NdbRecord *attribute_record,
2560                               const char *attribute_row,
2561                               const unsigned char *mask,
2562                               const NdbOperation::OperationOptions *opts,
2563                               Uint32 sizeOfOptions,
2564                               const NdbLockHandle* lh)
2565 {
2566   NdbOperation *op;
2567 
2568   /* Check that we've got a base table record for the attribute record */
2569   if (attribute_record->flags & NdbRecord::RecIsIndex)
2570   {
2571     /* Result or attribute record must be a base
2572        table ndbrecord, not an index ndbrecord */
2573     setOperationErrorCodeAbort(4340);
2574     return NULL;
2575   }
2576   /*
2577     We are actually passing the table object for the index here, not the table
2578     object of the underlying table. But we only need it to keep the existing
2579     NdbOperation code happy, it is not actually used for NdbRecord operation.
2580     We will eliminate the need for passing table and index completely when
2581     implementing WL#3707.
2582   */
2583   if (key_record->flags & NdbRecord::RecIsIndex)
2584   {
2585     op= getNdbIndexOperation(key_record->table->m_index,
2586                              attribute_record->table, NULL);
2587   }
2588   else
2589   {
2590     if (key_record->tableId != attribute_record->tableId)
2591     {
2592       setOperationErrorCodeAbort(4287);
2593       return NULL;
2594     }
2595     op= getNdbOperation(attribute_record->table, NULL);
2596   }
2597   if(!op)
2598     return NULL;
2599 
2600   op->theStatus= NdbOperation::UseNdbRecord;
2601   op->theOperationType= type;
2602   op->theErrorLine++;
2603   op->theLockMode= lock_mode;
2604   op->m_key_record= key_record;
2605   op->m_key_row= key_row;
2606   op->m_attribute_record= attribute_record;
2607   op->m_attribute_row= attribute_row;
2608   op->m_abortOption=default_ao;
2609   op->theLockHandle = const_cast<NdbLockHandle*>(lh);
2610 
2611   AttributeMask readMask;
2612   attribute_record->copyMask(readMask.rep.data, mask);
2613 
2614   /*
2615    * Handle options
2616    */
2617   if (opts != NULL)
2618   {
2619     /* Delegate to static method in NdbOperation */
2620     Uint32 result = NdbOperation::handleOperationOptions (type,
2621                                                           opts,
2622                                                           sizeOfOptions,
2623                                                           op);
2624     if (result !=0)
2625     {
2626       setOperationErrorCodeAbort(result);
2627       return NULL;
2628     }
2629   }
2630 
2631   /* Handle delete + blobs */
2632   if (type == NdbOperation::DeleteRequest &&
2633       (attribute_record->flags & NdbRecord::RecTableHasBlob))
2634   {
2635     /* Need to link in all the Blob handles for delete
2636      * If there is a pre-read, check that no Blobs have
2637      * been asked for
2638      */
2639     if (op->getBlobHandlesNdbRecordDelete(this,
2640                                           (attribute_row != NULL),
2641                                           readMask.rep.data) == -1)
2642       return NULL;
2643   }
2644   else if (unlikely((attribute_record->flags & NdbRecord::RecHasBlob) &&
2645                     (type != NdbOperation::UnlockRequest)))
2646   {
2647     /* Create blob handles for non-delete, non-unlock operations */
2648     if (op->getBlobHandlesNdbRecord(this, readMask.rep.data) == -1)
2649       return NULL;
2650   }
2651 
2652   /*
2653    * Now prepare the signals to be sent...
2654    *
2655    */
2656   int returnCode=op->buildSignalsNdbRecord(theTCConPtr, theTransactionId,
2657                                            readMask.rep.data);
2658 
2659   if (returnCode)
2660   {
2661     // buildSignalsNdbRecord should have set the error status
2662     // So we can return NULL
2663     return NULL;
2664   }
2665 
2666   return op;
2667 }
2668 
2669 
2670 
2671 const NdbOperation *
readTuple(const NdbRecord * key_rec,const char * key_row,const NdbRecord * result_rec,char * result_row,NdbOperation::LockMode lock_mode,const unsigned char * result_mask,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions)2672 NdbTransaction::readTuple(const NdbRecord *key_rec, const char *key_row,
2673                           const NdbRecord *result_rec, char *result_row,
2674                           NdbOperation::LockMode lock_mode,
2675                           const unsigned char *result_mask,
2676                           const NdbOperation::OperationOptions *opts,
2677                           Uint32 sizeOfOptions)
2678 {
2679   /* Check that the NdbRecord specifies the full primary key. */
2680   if (!(key_rec->flags & NdbRecord::RecHasAllKeys))
2681   {
2682     setOperationErrorCodeAbort(4292);
2683     return NULL;
2684   }
2685 
2686   /* It appears that unique index operations do no support readCommitted. */
2687   if (key_rec->flags & NdbRecord::RecIsIndex &&
2688       lock_mode == NdbOperation::LM_CommittedRead)
2689     lock_mode= NdbOperation::LM_Read;
2690 
2691   NdbOperation::OperationType opType=
2692     (lock_mode == NdbOperation::LM_Exclusive ?
2693        NdbOperation::ReadExclusive : NdbOperation::ReadRequest);
2694   NdbOperation *op= setupRecordOp(opType, lock_mode,
2695                                   NdbOperation::AO_IgnoreError,
2696                                   key_rec, key_row,
2697                                   result_rec, result_row, result_mask,
2698                                   opts,
2699                                   sizeOfOptions);
2700   if (!op)
2701     return NULL;
2702 
2703   if (op->theLockMode == NdbOperation::LM_CommittedRead)
2704   {
2705     op->theDirtyIndicator= 1;
2706     op->theSimpleIndicator= 1;
2707   }
2708   else
2709   {
2710     if (op->theLockMode == NdbOperation::LM_SimpleRead)
2711     {
2712       op->theSimpleIndicator = 1;
2713     }
2714 
2715 
2716     theSimpleState= 0;
2717   }
2718 
2719   /* Setup the record/row for receiving the results. */
2720   op->theReceiver.getValues(result_rec, result_row);
2721 
2722   return op;
2723 }
2724 
2725 const NdbOperation *
insertTuple(const NdbRecord * key_rec,const char * key_row,const NdbRecord * attr_rec,const char * attr_row,const unsigned char * mask,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions)2726 NdbTransaction::insertTuple(const NdbRecord *key_rec, const char *key_row,
2727                             const NdbRecord *attr_rec, const char *attr_row,
2728                             const unsigned char *mask,
2729                             const NdbOperation::OperationOptions *opts,
2730                             Uint32 sizeOfOptions)
2731 {
2732   /* Check that the NdbRecord specifies the full primary key. */
2733   if (!(key_rec->flags & NdbRecord::RecHasAllKeys))
2734   {
2735     setOperationErrorCodeAbort(4292);
2736     return NULL;
2737   }
2738 
2739   NdbOperation *op= setupRecordOp(NdbOperation::InsertRequest,
2740                                   NdbOperation::LM_Exclusive,
2741                                   NdbOperation::AbortOnError,
2742                                   key_rec, key_row,
2743                                   attr_rec, attr_row, mask,
2744                                   opts,
2745                                   sizeOfOptions);
2746   if (!op)
2747     return NULL;
2748 
2749   theSimpleState= 0;
2750 
2751   return op;
2752 }
2753 
2754 const NdbOperation *
insertTuple(const NdbRecord * combined_rec,const char * combined_row,const unsigned char * mask,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions)2755 NdbTransaction::insertTuple(const NdbRecord *combined_rec, const char *combined_row,
2756                             const unsigned char *mask,
2757                             const NdbOperation::OperationOptions *opts,
2758                             Uint32 sizeOfOptions)
2759 {
2760   return insertTuple(combined_rec, combined_row,
2761                      combined_rec, combined_row,
2762                      mask,
2763                      opts,
2764                      sizeOfOptions);
2765 }
2766 
2767 const NdbOperation *
updateTuple(const NdbRecord * key_rec,const char * key_row,const NdbRecord * attr_rec,const char * attr_row,const unsigned char * mask,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions)2768 NdbTransaction::updateTuple(const NdbRecord *key_rec, const char *key_row,
2769                             const NdbRecord *attr_rec, const char *attr_row,
2770                             const unsigned char *mask,
2771                             const NdbOperation::OperationOptions *opts,
2772                             Uint32 sizeOfOptions)
2773 {
2774   /* Check that the NdbRecord specifies the full primary key. */
2775   if (!(key_rec->flags & NdbRecord::RecHasAllKeys))
2776   {
2777     setOperationErrorCodeAbort(4292);
2778     return NULL;
2779   }
2780 
2781   NdbOperation *op= setupRecordOp(NdbOperation::UpdateRequest,
2782                                   NdbOperation::LM_Exclusive,
2783                                   NdbOperation::AbortOnError,
2784                                   key_rec, key_row,
2785                                   attr_rec, attr_row, mask,
2786                                   opts,
2787                                   sizeOfOptions);
2788   if(!op)
2789     return op;
2790 
2791   theSimpleState= 0;
2792 
2793   return op;
2794 }
2795 
2796 const NdbOperation *
deleteTuple(const NdbRecord * key_rec,const char * key_row,const NdbRecord * result_rec,char * result_row,const unsigned char * result_mask,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions)2797 NdbTransaction::deleteTuple(const NdbRecord *key_rec,
2798                             const char *key_row,
2799                             const NdbRecord *result_rec,
2800                             char *result_row,
2801                             const unsigned char *result_mask,
2802                             const NdbOperation::OperationOptions* opts,
2803                             Uint32 sizeOfOptions)
2804 {
2805   /* Check that the key NdbRecord specifies the full primary key. */
2806   if (!(key_rec->flags & NdbRecord::RecHasAllKeys))
2807   {
2808     setOperationErrorCodeAbort(4292);
2809     return NULL;
2810   }
2811 
2812   NdbOperation *op= setupRecordOp(NdbOperation::DeleteRequest,
2813                                   NdbOperation::LM_Exclusive,
2814                                   NdbOperation::AbortOnError,
2815                                   key_rec, key_row,
2816                                   result_rec, result_row, result_mask,
2817                                   opts,
2818                                   sizeOfOptions);
2819   if(!op)
2820     return op;
2821 
2822   theSimpleState= 0;
2823 
2824   if (result_row != NULL) // readBeforeDelete
2825   {
2826     /* Setup the record/row for receiving the results. */
2827     op->theReceiver.getValues(result_rec, result_row);
2828   }
2829 
2830   return op;
2831 }
2832 
2833 const NdbOperation *
writeTuple(const NdbRecord * key_rec,const char * key_row,const NdbRecord * attr_rec,const char * attr_row,const unsigned char * mask,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions)2834 NdbTransaction::writeTuple(const NdbRecord *key_rec, const char *key_row,
2835                            const NdbRecord *attr_rec, const char *attr_row,
2836                            const unsigned char *mask,
2837                            const NdbOperation::OperationOptions *opts,
2838                            Uint32 sizeOfOptions)
2839 {
2840   /* Check that the NdbRecord specifies the full primary key. */
2841   if (!(key_rec->flags & NdbRecord::RecHasAllKeys))
2842   {
2843     setOperationErrorCodeAbort(4292);
2844     return NULL;
2845   }
2846 
2847   NdbOperation *op= setupRecordOp(NdbOperation::WriteRequest,
2848                                   NdbOperation::LM_Exclusive,
2849                                   NdbOperation::AbortOnError,
2850                                   key_rec, key_row,
2851                                   attr_rec, attr_row, mask,
2852                                   opts,
2853                                   sizeOfOptions);
2854   if(!op)
2855     return op;
2856 
2857   theSimpleState= 0;
2858 
2859   return op;
2860 }
2861 
2862 const NdbOperation *
refreshTuple(const NdbRecord * key_rec,const char * key_row,const NdbOperation::OperationOptions * opts,Uint32 sizeOfOptions)2863 NdbTransaction::refreshTuple(const NdbRecord *key_rec, const char *key_row,
2864                              const NdbOperation::OperationOptions *opts,
2865                              Uint32 sizeOfOptions)
2866 {
2867   /* Check TC node version lockless */
2868   {
2869     Uint32 tcVer = theNdb->theImpl->getNodeInfo(theDBnode).m_info.m_version;
2870     if (unlikely(! ndb_refresh_tuple(tcVer)))
2871     {
2872       /* Function not implemented yet */
2873       setOperationErrorCodeAbort(4003);
2874       return NULL;
2875     }
2876   }
2877 
2878   /* Check that the NdbRecord specifies the full primary key. */
2879   if (!(key_rec->flags & NdbRecord::RecHasAllKeys))
2880   {
2881     setOperationErrorCodeAbort(4292);
2882     return NULL;
2883   }
2884 
2885   Uint8 keymask[NDB_MAX_ATTRIBUTES_IN_TABLE/8];
2886   bzero(keymask, sizeof(keymask));
2887   for (Uint32 i = 0; i<key_rec->key_index_length; i++)
2888   {
2889     Uint32 id = key_rec->columns[key_rec->key_indexes[i]].attrId;
2890     keymask[(id / 8)] |= (1 << (id & 7));
2891   }
2892 
2893   NdbOperation *op= setupRecordOp(NdbOperation::RefreshRequest,
2894                                   NdbOperation::LM_Exclusive,
2895                                   NdbOperation::AbortOnError,
2896                                   key_rec, key_row,
2897                                   key_rec, key_row,
2898                                   keymask /* mask */,
2899                                   opts,
2900                                   sizeOfOptions);
2901   if(!op)
2902     return op;
2903 
2904   theSimpleState= 0;
2905 
2906   return op;
2907 }
2908 
2909 NdbScanOperation *
scanTable(const NdbRecord * result_record,NdbOperation::LockMode lock_mode,const unsigned char * result_mask,const NdbScanOperation::ScanOptions * options,Uint32 sizeOfOptions)2910 NdbTransaction::scanTable(const NdbRecord *result_record,
2911                           NdbOperation::LockMode lock_mode,
2912                           const unsigned char *result_mask,
2913                           const NdbScanOperation::ScanOptions *options,
2914                           Uint32 sizeOfOptions)
2915 {
2916   DBUG_ENTER("NdbTransaction::scanTable");
2917   DBUG_PRINT("info", ("Options=%p(0x%x)", options,
2918                       (options ? (unsigned)(options->optionsPresent) : 0)));
2919   /*
2920     Normal scan operations are created as NdbIndexScanOperations.
2921     The reason for this is that they can then share a pool of allocated
2922     objects.
2923   */
2924   NdbIndexScanOperation *op_idx=
2925     getNdbScanOperation(result_record->table);
2926 
2927   if (op_idx == NULL)
2928   {
2929     /* Memory allocation error */
2930     setOperationErrorCodeAbort(4000);
2931     DBUG_RETURN(NULL);
2932   }
2933 
2934   op_idx->m_scanUsingOldApi= false;
2935 
2936   /* The real work is done in NdbScanOperation */
2937   if (op_idx->scanTableImpl(result_record,
2938                             lock_mode,
2939                             result_mask,
2940                             options,
2941                             sizeOfOptions) == 0)
2942   {
2943     DBUG_RETURN(op_idx);
2944   }
2945 
2946   releaseScanOperation(&m_theFirstScanOperation, &m_theLastScanOperation,
2947                        op_idx);
2948   DBUG_RETURN(NULL);
2949 }
2950 
2951 
2952 
2953 NdbIndexScanOperation *
scanIndex(const NdbRecord * key_record,const NdbRecord * result_record,NdbOperation::LockMode lock_mode,const unsigned char * result_mask,const NdbIndexScanOperation::IndexBound * bound,const NdbScanOperation::ScanOptions * options,Uint32 sizeOfOptions)2954 NdbTransaction::scanIndex(const NdbRecord *key_record,
2955                           const NdbRecord *result_record,
2956                                 NdbOperation::LockMode lock_mode,
2957                           const unsigned char *result_mask,
2958                           const NdbIndexScanOperation::IndexBound *bound,
2959                           const NdbScanOperation::ScanOptions *options,
2960                           Uint32 sizeOfOptions)
2961 {
2962   /*
2963     Normal scan operations are created as NdbIndexScanOperations.
2964     The reason for this is that they can then share a pool of allocated
2965     objects.
2966   */
2967   NdbIndexScanOperation *op= getNdbScanOperation(key_record->table);
2968   if (op==NULL)
2969   {
2970     /* Memory allocation error */
2971     setOperationErrorCodeAbort(4000);
2972     return NULL;
2973   }
2974 
2975   op->m_scanUsingOldApi= false;
2976 
2977   /* Defer the rest of the work to NdbIndexScanOperation */
2978   if (op->scanIndexImpl(key_record,
2979                         result_record,
2980                         lock_mode,
2981                         result_mask,
2982                         bound,
2983                         options,
2984                         sizeOfOptions) != 0)
2985   {
2986     releaseScanOperation(&m_theFirstScanOperation, &m_theLastScanOperation, op);
2987     return NULL;
2988   }
2989 
2990   return op;
2991 } // ::scanIndex();
2992 
2993 Uint32
getMaxPendingBlobReadBytes() const2994 NdbTransaction::getMaxPendingBlobReadBytes() const
2995 {
2996   /* 0 == max */
2997   return (maxPendingBlobReadBytes ==
2998           (~Uint32(0)) ? 0 : maxPendingBlobReadBytes);
2999 };
3000 
3001 Uint32
getMaxPendingBlobWriteBytes() const3002 NdbTransaction::getMaxPendingBlobWriteBytes() const
3003 {
3004   /* 0 == max */
3005   return (maxPendingBlobWriteBytes ==
3006           (~Uint32(0)) ? 0 : maxPendingBlobWriteBytes);
3007 };
3008 
3009 void
setMaxPendingBlobReadBytes(Uint32 bytes)3010 NdbTransaction::setMaxPendingBlobReadBytes(Uint32 bytes)
3011 {
3012   /* 0 == max */
3013   maxPendingBlobReadBytes = (bytes?bytes : (~ Uint32(0)));
3014 }
3015 
3016 void
setMaxPendingBlobWriteBytes(Uint32 bytes)3017 NdbTransaction::setMaxPendingBlobWriteBytes(Uint32 bytes)
3018 {
3019   /* 0 == max */
3020   maxPendingBlobWriteBytes = (bytes?bytes : (~ Uint32(0)));
3021 }
3022 
3023 #ifdef VM_TRACE
3024 #define CASE(x) case x: ndbout << " " << #x; break
3025 void
printState()3026 NdbTransaction::printState()
3027 {
3028   ndbout << "con=" << hex << this << dec;
3029   ndbout << " node=" << getConnectedNodeId();
3030   switch (theStatus) {
3031   CASE(NotConnected);
3032   CASE(Connecting);
3033   CASE(Connected);
3034   CASE(DisConnecting);
3035   CASE(ConnectFailure);
3036   default: ndbout << (Uint32) theStatus;
3037   }
3038   switch (theListState) {
3039   CASE(NotInList);
3040   CASE(InPreparedList);
3041   CASE(InSendList);
3042   CASE(InCompletedList);
3043   default: ndbout << (Uint32) theListState;
3044   }
3045   switch (theSendStatus) {
3046   CASE(NotInit);
3047   CASE(InitState);
3048   CASE(sendOperations);
3049   CASE(sendCompleted);
3050   CASE(sendCOMMITstate);
3051   CASE(sendABORT);
3052   CASE(sendABORTfail);
3053   CASE(sendTC_ROLLBACK);
3054   CASE(sendTC_COMMIT);
3055   CASE(sendTC_OP);
3056   default: ndbout << (Uint32) theSendStatus;
3057   }
3058   switch (theCommitStatus) {
3059   CASE(NotStarted);
3060   CASE(Started);
3061   CASE(Committed);
3062   CASE(Aborted);
3063   CASE(NeedAbort);
3064   default: ndbout << (Uint32) theCommitStatus;
3065   }
3066   switch (theCompletionStatus) {
3067   CASE(NotCompleted);
3068   CASE(CompletedSuccess);
3069   CASE(CompletedFailure);
3070   CASE(DefinitionFailure);
3071   default: ndbout << (Uint32) theCompletionStatus;
3072   }
3073   ndbout << endl;
3074 }
3075 #undef CASE
3076 #endif
3077 
3078 int
report_node_failure(Uint32 id)3079 NdbTransaction::report_node_failure(Uint32 id){
3080   NdbNodeBitmask::set(m_failed_db_nodes, id);
3081   if(!NdbNodeBitmask::get(m_db_nodes, id))
3082   {
3083     return 0;
3084   }
3085 
3086   /**
3087    *   Arrived
3088    *   TCKEYCONF   TRANSIDAI
3089    * 1)   -           -
3090    * 2)   -           X
3091    * 3)   X           -
3092    * 4)   X           X
3093    */
3094   NdbOperation* tmp = theFirstExecOpInList;
3095   const Uint32 len = TcKeyConf::DirtyReadBit | id;
3096   Uint32 tNoComp = theNoOfOpCompleted;
3097   Uint32 tNoSent = theNoOfOpSent;
3098   Uint32 count = 0;
3099   while(tmp != 0)
3100   {
3101     if(tmp->theReceiver.m_expected_result_length == len &&
3102        tmp->theReceiver.m_received_result_length == 0)
3103     {
3104       count++;
3105       tmp->theError.code = 4119;
3106     }
3107     tmp = tmp->next();
3108   }
3109 
3110   /**
3111    * TODO, only abort ones really needing abort
3112    */
3113   NdbQueryImpl* qtmp = m_firstActiveQuery;
3114   while (qtmp != 0)
3115   {
3116     if (qtmp->getQueryDef().isScanQuery() == false)
3117     {
3118       count++;
3119       qtmp->setErrorCode(4119);
3120     }
3121     qtmp = qtmp->getNext();
3122   }
3123 
3124   tNoComp += count;
3125   theNoOfOpCompleted = tNoComp;
3126   if(count)
3127   {
3128     theReturnStatus = NdbTransaction::ReturnFailure;
3129     if(tNoComp == tNoSent)
3130     {
3131       theError.code = 4119;
3132       theCompletionStatus = NdbTransaction::CompletedFailure;
3133       return 1;
3134     }
3135   }
3136   return 0;
3137 }
3138 
3139 NdbQuery*
createQuery(const NdbQueryDef * def,const NdbQueryParamValue paramValues[],NdbOperation::LockMode lock_mode)3140 NdbTransaction::createQuery(const NdbQueryDef* def,
3141                             const NdbQueryParamValue paramValues[],
3142                             NdbOperation::LockMode lock_mode)
3143 {
3144   NdbQueryImpl* query = NdbQueryImpl::buildQuery(*this, def->getImpl());
3145   if (unlikely(query == NULL)) {
3146     return NULL; // Error code for transaction is already set.
3147   }
3148 
3149   const int error = query->assignParameters(paramValues);
3150   if (unlikely(error)) {
3151     // Error code for transaction is already set.
3152     query->release();
3153     return NULL;
3154   }
3155 
3156   query->setNext(m_firstQuery);
3157   m_firstQuery = query;
3158 
3159   return &query->getInterface();
3160 }
3161 
3162 NdbLockHandle*
getLockHandle()3163 NdbTransaction::getLockHandle()
3164 {
3165   NdbLockHandle* lh;
3166 
3167   /* Get a LockHandle object from the Ndb pool and
3168    * link it into our transaction
3169    */
3170   lh = theNdb->getLockHandle();
3171 
3172   if (lh)
3173   {
3174     lh->thePrev = m_theLastLockHandle;
3175     if (m_theLastLockHandle == NULL)
3176     {
3177       m_theFirstLockHandle = lh;
3178       m_theLastLockHandle = lh;
3179     }
3180     else
3181     {
3182       lh->next(NULL);
3183       m_theLastLockHandle->next(lh);
3184       m_theLastLockHandle = lh;
3185     }
3186   }
3187 
3188   return lh;
3189 }
3190 
3191 const NdbOperation*
unlock(const NdbLockHandle * lockHandle,NdbOperation::AbortOption ao)3192 NdbTransaction::unlock(const NdbLockHandle* lockHandle,
3193                        NdbOperation::AbortOption ao)
3194 {
3195   switch(lockHandle->m_state)
3196   {
3197   case NdbLockHandle::FREE:
3198     /* LockHandle already released */
3199     setErrorCode(4551);
3200     return NULL;
3201   case NdbLockHandle::PREPARED:
3202     if (likely(lockHandle->isLockRefValid()))
3203     {
3204       /* Looks ok */
3205       break;
3206     }
3207     /* Fall through */
3208   case NdbLockHandle::ALLOCATED:
3209     /* NdbLockHandle original operation not executed successfully */
3210     setErrorCode(4553);
3211     return NULL;
3212   default:
3213     abort();
3214     return NULL;
3215   }
3216 
3217   if (m_theFirstLockHandle == NULL)
3218   {
3219     /* NdbLockHandle does not belong to transaction */
3220     setErrorCode(4552);
3221     return NULL;
3222   }
3223 
3224 #ifdef VM_TRACE
3225   /* Check that this transaction 'owns' this lockhandle */
3226   {
3227     NdbLockHandle* tmp = m_theLastLockHandle;
3228     while (tmp && (tmp != lockHandle))
3229     {
3230       tmp = tmp->thePrev;
3231     }
3232 
3233     if (tmp != lockHandle)
3234     {
3235       /* NdbLockHandle does not belong to transaction */
3236       setErrorCode(4552);
3237       return NULL;
3238     }
3239   }
3240 #endif
3241 
3242   assert(theSimpleState == 0);
3243 
3244   /* Use the first work of the Lock reference as the unlock
3245    * operation's partition id
3246    * The other two words form the key.
3247    */
3248   NdbOperation::OperationOptions opts;
3249 
3250   opts.optionsPresent = NdbOperation::OperationOptions::OO_PARTITION_ID;
3251   opts.partitionId = lockHandle->getDistKey();
3252 
3253   if (ao != NdbOperation::DefaultAbortOption)
3254   {
3255     /* User supplied a preference, pass it on */
3256     opts.optionsPresent |= NdbOperation::OperationOptions::OO_ABORTOPTION;
3257     opts.abortOption = ao;
3258   }
3259 
3260   NdbOperation* unlockOp = setupRecordOp(NdbOperation::UnlockRequest,
3261                                          NdbOperation::LM_CommittedRead,
3262                                          NdbOperation::AbortOnError, // Default
3263                                          lockHandle->m_table->m_ndbrecord,
3264                                          NULL, // key_row
3265                                          lockHandle->m_table->m_ndbrecord,
3266                                          NULL,             // attr_row
3267                                          NULL,             // mask
3268                                          &opts,            // opts,
3269                                          sizeof(opts),     // sizeOfOptions
3270                                          lockHandle);
3271 
3272   return unlockOp;
3273 }
3274 
3275 int
releaseLockHandle(const NdbLockHandle * lockHandle)3276 NdbTransaction::releaseLockHandle(const NdbLockHandle* lockHandle)
3277 {
3278   NdbLockHandle* prev = lockHandle->thePrev;
3279   NdbLockHandle* next = lockHandle->theNext;
3280 
3281   switch(lockHandle->m_state)
3282   {
3283   case NdbLockHandle::FREE:
3284     /* NdbLockHandle already released */
3285     setErrorCode(4551);
3286     return -1;
3287   case NdbLockHandle::PREPARED:
3288     if (! lockHandle->isLockRefValid())
3289     {
3290       /* It's not safe to release the lockHandle after it's
3291        * defined and before the operation's executed.
3292        * The lockhandle memory is needed to receive the
3293        * Lock Reference during execution
3294        */
3295       /* Cannot releaseLockHandle until operation executed */
3296       setErrorCode(4550);
3297       return -1;
3298     }
3299     /* Fall through */
3300   case NdbLockHandle::ALLOCATED:
3301     /* Ok to release */
3302     break;
3303   default:
3304     /* Bad state */
3305     abort();
3306     return -1;
3307   }
3308 
3309 #ifdef VM_TRACE
3310   /* Check lockhandle is known to this transaction */
3311   NdbLockHandle* tmp = m_theFirstLockHandle;
3312   while (tmp &&
3313          (tmp != lockHandle))
3314   {
3315     tmp = tmp->next();
3316   }
3317 
3318   if (tmp != lockHandle)
3319   {
3320     abort();
3321     return -1;
3322   }
3323 #endif
3324 
3325   /* Repair list around lock handle */
3326   if (prev)
3327     prev->next(next);
3328 
3329   if (next)
3330     next->thePrev = prev;
3331 
3332   /* Repair list head and tail ptrs */
3333   if (lockHandle == m_theFirstLockHandle)
3334   {
3335     m_theFirstLockHandle = next;
3336   }
3337   if (lockHandle == m_theLastLockHandle)
3338   {
3339     m_theLastLockHandle = prev;
3340   }
3341 
3342   /* Now return it to the Ndb's freelist */
3343   NdbLockHandle* lh = const_cast<NdbLockHandle*>(lockHandle);
3344 
3345   lh->thePrev = NULL;
3346   lh->theNext = NULL;
3347 
3348   theNdb->releaseLockHandle(lh);
3349 
3350   return 0;
3351 }
3352