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