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