diff options
Diffstat (limited to 'storage/ndb/src/ndbapi/NdbTransaction.cpp')
-rw-r--r-- | storage/ndb/src/ndbapi/NdbTransaction.cpp | 2124 |
1 files changed, 2124 insertions, 0 deletions
diff --git a/storage/ndb/src/ndbapi/NdbTransaction.cpp b/storage/ndb/src/ndbapi/NdbTransaction.cpp new file mode 100644 index 00000000000..67581e4a0f8 --- /dev/null +++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp @@ -0,0 +1,2124 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include <NdbOut.hpp> +#include <NdbTransaction.hpp> +#include <NdbOperation.hpp> +#include <NdbScanOperation.hpp> +#include <NdbIndexScanOperation.hpp> +#include <NdbIndexOperation.hpp> +#include "NdbApiSignal.hpp" +#include "TransporterFacade.hpp" +#include "API.hpp" +#include "NdbBlob.hpp" +#include <ndb_limits.h> + +#include <signaldata/TcKeyConf.hpp> +#include <signaldata/TcIndx.hpp> +#include <signaldata/TcCommit.hpp> +#include <signaldata/TcKeyFailConf.hpp> +#include <signaldata/TcHbRep.hpp> + +/***************************************************************************** +NdbTransaction( Ndb* aNdb ); + +Return Value: None +Parameters: aNdb: Pointers to the Ndb object +Remark: Creates a connection object. +*****************************************************************************/ +NdbTransaction::NdbTransaction( Ndb* aNdb ) : + theSendStatus(NotInit), + theCallbackFunction(NULL), + theCallbackObject(NULL), + theTransArrayIndex(0), + theStartTransTime(0), + theErrorLine(0), + theErrorOperation(NULL), + theNdb(aNdb), + theNext(NULL), + theFirstOpInList(NULL), + theLastOpInList(NULL), + theFirstExecOpInList(NULL), + theLastExecOpInList(NULL), + theCompletedFirstOp(NULL), + theCompletedLastOp(NULL), + theNoOfOpSent(0), + theNoOfOpCompleted(0), + theNoOfOpFetched(0), + theMyRef(0), + theTCConPtr(0), + theTransactionId(0), + theGlobalCheckpointId(0), + theStatus(NotConnected), + theCompletionStatus(NotCompleted), + theCommitStatus(NotStarted), + theMagicNumber(0xFE11DC), + theTransactionIsStarted(false), + theDBnode(0), + theReleaseOnClose(false), + // Scan operations + m_waitForReply(true), + m_theFirstScanOperation(NULL), + m_theLastScanOperation(NULL), + m_firstExecutedScanOp(NULL), + // Scan operations + theScanningOp(NULL), + theBuddyConPtr(0xFFFFFFFF), + theBlobFlag(false), + thePendingBlobOps(0) +{ + theListState = NotInList; + theError.code = 0; + theId = theNdb->theImpl->theNdbObjectIdMap.map(this); + +#define CHECK_SZ(mask, sz) assert((sizeof(mask)/sizeof(mask[0])) == sz) + + CHECK_SZ(m_db_nodes, NdbNodeBitmask::Size); + CHECK_SZ(m_failed_db_nodes, NdbNodeBitmask::Size); +}//NdbTransaction::NdbTransaction() + +/***************************************************************************** +~NdbTransaction(); + +Remark: Deletes the connection object. +*****************************************************************************/ +NdbTransaction::~NdbTransaction() +{ + DBUG_ENTER("NdbTransaction::~NdbTransaction"); + theNdb->theImpl->theNdbObjectIdMap.unmap(theId, this); + DBUG_VOID_RETURN; +}//NdbTransaction::~NdbTransaction() + +/***************************************************************************** +void init(); + +Remark: Initialise connection object for new transaction. +*****************************************************************************/ +void +NdbTransaction::init() +{ + theListState = NotInList; + theInUseState = true; + theTransactionIsStarted = false; + theNext = NULL; + + theFirstOpInList = NULL; + theLastOpInList = NULL; + + theScanningOp = NULL; + + theFirstExecOpInList = NULL; + theLastExecOpInList = NULL; + + theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; + + theGlobalCheckpointId = 0; + theCommitStatus = Started; + theCompletionStatus = NotCompleted; + m_abortOption = AbortOnError; + + theError.code = 0; + theErrorLine = 0; + theErrorOperation = NULL; + + theReleaseOnClose = false; + theSimpleState = true; + theSendStatus = InitState; + theMagicNumber = 0x37412619; + // Scan operations + m_waitForReply = true; + m_theFirstScanOperation = NULL; + m_theLastScanOperation = NULL; + m_firstExecutedScanOp = 0; + theBuddyConPtr = 0xFFFFFFFF; + // + theBlobFlag = false; + thePendingBlobOps = 0; +}//NdbTransaction::init() + +/***************************************************************************** +setOperationErrorCode(int error); + +Remark: Sets an error code on the connection object from an + operation object. +*****************************************************************************/ +void +NdbTransaction::setOperationErrorCode(int error) +{ + DBUG_ENTER("NdbTransaction::setOperationErrorCode"); + setErrorCode(error); + DBUG_VOID_RETURN; +} + +/***************************************************************************** +setOperationErrorCodeAbort(int error); + +Remark: Sets an error code on the connection object from an + operation object. +*****************************************************************************/ +void +NdbTransaction::setOperationErrorCodeAbort(int error, int abortOption) +{ + DBUG_ENTER("NdbTransaction::setOperationErrorCodeAbort"); + if (abortOption == -1) + abortOption = m_abortOption; + if (theTransactionIsStarted == false) { + theCommitStatus = Aborted; + } else if ((abortOption == AbortOnError) && + (theCommitStatus != Committed) && + (theCommitStatus != Aborted)) { + theCommitStatus = NeedAbort; + }//if + setErrorCode(error); + DBUG_VOID_RETURN; +} + +/***************************************************************************** +setErrorCode(int anErrorCode); + +Remark: Sets an error indication on the connection object. +*****************************************************************************/ +void +NdbTransaction::setErrorCode(int error) +{ + DBUG_ENTER("NdbTransaction::setErrorCode"); + DBUG_PRINT("enter", ("error: %d, theError.code: %d", error, theError.code)); + + if (theError.code == 0) + theError.code = error; + + DBUG_VOID_RETURN; +}//NdbTransaction::setErrorCode() + +int +NdbTransaction::restart(){ + DBUG_ENTER("NdbTransaction::restart"); + if(theCompletionStatus == CompletedSuccess){ + releaseCompletedOperations(); + Uint64 tTransid = theNdb->theFirstTransId; + theTransactionId = tTransid; + if ((tTransid & 0xFFFFFFFF) == 0xFFFFFFFF) { + theNdb->theFirstTransId = (tTransid >> 32) << 32; + } else { + theNdb->theFirstTransId = tTransid + 1; + } + theCommitStatus = Started; + theCompletionStatus = NotCompleted; + theTransactionIsStarted = false; + DBUG_RETURN(0); + } + DBUG_PRINT("error",("theCompletionStatus != CompletedSuccess")); + DBUG_RETURN(-1); +} + +/***************************************************************************** +void handleExecuteCompletion(void); + +Remark: Handle time-out on a transaction object. +*****************************************************************************/ +void +NdbTransaction::handleExecuteCompletion() +{ + /*************************************************************************** + * Move the NdbOperation objects from the list of executing + * operations to list of completed + **************************************************************************/ + NdbOperation* tFirstExecOp = theFirstExecOpInList; + NdbOperation* tLastExecOp = theLastExecOpInList; + if (tLastExecOp != NULL) { + tLastExecOp->next(theCompletedFirstOp); + theCompletedFirstOp = tFirstExecOp; + if (theCompletedLastOp == NULL) + theCompletedLastOp = tLastExecOp; + theFirstExecOpInList = NULL; + theLastExecOpInList = NULL; + }//if + theSendStatus = InitState; + return; +}//NdbTransaction::handleExecuteCompletion() + +/***************************************************************************** +int execute(ExecType aTypeOfExec, CommitType aTypeOfCommit, int forceSend); + +Return Value: Return 0 : execute was successful. + Return -1: In all other case. +Parameters : aTypeOfExec: Type of execute. +Remark: Initialise connection object for new transaction. +*****************************************************************************/ +int +NdbTransaction::execute(ExecType aTypeOfExec, + AbortOption abortOption, + int forceSend) +{ + DBUG_ENTER("NdbTransaction::execute"); + DBUG_PRINT("enter", ("aTypeOfExec: %d, abortOption: %d", + aTypeOfExec, abortOption)); + + if (! theBlobFlag) + DBUG_RETURN(executeNoBlobs(aTypeOfExec, abortOption, forceSend)); + + /* + * execute prepared ops in batches, as requested by blobs + * - blob error does not terminate execution + * - blob error sets error on operation + * - if error on operation skip blob calls + */ + + ExecType tExecType; + NdbOperation* tPrepOp; + NdbOperation* tCompletedFirstOp = NULL; + NdbOperation* tCompletedLastOp = NULL; + + int ret = 0; + do { + tExecType = aTypeOfExec; + tPrepOp = theFirstOpInList; + while (tPrepOp != NULL) { + if (tPrepOp->theError.code == 0) { + bool batch = false; + NdbBlob* tBlob = tPrepOp->theBlobList; + while (tBlob != NULL) { + if (tBlob->preExecute(tExecType, batch) == -1) + ret = -1; + tBlob = tBlob->theNext; + } + if (batch) { + // blob asked to execute all up to here now + tExecType = NoCommit; + break; + } + } + tPrepOp = tPrepOp->next(); + } + + // save rest of prepared ops if batch + NdbOperation* tRestOp= 0; + NdbOperation* tLastOp= 0; + if (tPrepOp != NULL) { + tRestOp = tPrepOp->next(); + tPrepOp->next(NULL); + tLastOp = theLastOpInList; + theLastOpInList = tPrepOp; + } + + if (tExecType == Commit) { + NdbOperation* tOp = theCompletedFirstOp; + while (tOp != NULL) { + if (tOp->theError.code == 0) { + NdbBlob* tBlob = tOp->theBlobList; + while (tBlob != NULL) { + if (tBlob->preCommit() == -1) + ret = -1; + tBlob = tBlob->theNext; + } + } + tOp = tOp->next(); + } + } + + // completed ops are in unspecified order + if (theCompletedFirstOp != NULL) { + if (tCompletedFirstOp == NULL) { + tCompletedFirstOp = theCompletedFirstOp; + tCompletedLastOp = theCompletedLastOp; + } else { + tCompletedLastOp->next(theCompletedFirstOp); + tCompletedLastOp = theCompletedLastOp; + } + theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; + } + + if (executeNoBlobs(tExecType, abortOption, forceSend) == -1) + ret = -1; +#ifdef ndb_api_crash_on_complex_blob_abort + assert(theFirstOpInList == NULL && theLastOpInList == NULL); +#else + theFirstOpInList = theLastOpInList = NULL; +#endif + + { + NdbOperation* tOp = theCompletedFirstOp; + while (tOp != NULL) { + if (tOp->theError.code == 0) { + NdbBlob* tBlob = tOp->theBlobList; + while (tBlob != NULL) { + // may add new operations if batch + if (tBlob->postExecute(tExecType) == -1) + ret = -1; + tBlob = tBlob->theNext; + } + } + tOp = tOp->next(); + } + } + + // add saved prepared ops if batch + if (tPrepOp != NULL && tRestOp != NULL) { + if (theFirstOpInList == NULL) + theFirstOpInList = tRestOp; + else + theLastOpInList->next(tRestOp); + theLastOpInList = tLastOp; + } + assert(theFirstOpInList == NULL || tExecType == NoCommit); + } while (theFirstOpInList != NULL || tExecType != aTypeOfExec); + + if (tCompletedFirstOp != NULL) { + tCompletedLastOp->next(theCompletedFirstOp); + theCompletedFirstOp = tCompletedFirstOp; + if (theCompletedLastOp == NULL) + theCompletedLastOp = tCompletedLastOp; + } +#if ndb_api_count_completed_ops_after_blob_execute + { NdbOperation* tOp; unsigned n = 0; + for (tOp = theCompletedFirstOp; tOp != NULL; tOp = tOp->next()) n++; + ndbout << "completed ops: " << n << endl; + } +#endif + DBUG_RETURN(ret); +} + +int +NdbTransaction::executeNoBlobs(ExecType aTypeOfExec, + AbortOption abortOption, + int forceSend) +{ + DBUG_ENTER("NdbTransaction::executeNoBlobs"); + DBUG_PRINT("enter", ("aTypeOfExec: %d, abortOption: %d", + aTypeOfExec, abortOption)); + +//------------------------------------------------------------------------ +// We will start by preparing all operations in the transaction defined +// since last execute or since beginning. If this works ok we will continue +// by calling the poll with wait method. This method will return when +// the NDB kernel has completed its task or when 10 seconds have passed. +// The NdbTransactionCallBack-method will receive the return code of the +// transaction. The normal methods of reading error codes still apply. +//------------------------------------------------------------------------ + Ndb* tNdb = theNdb; + + m_waitForReply = false; + executeAsynchPrepare(aTypeOfExec, NULL, NULL, abortOption); + if (m_waitForReply){ + while (1) { + int noOfComp = tNdb->sendPollNdb((3 * WAITFOR_RESPONSE_TIMEOUT), + 1, forceSend); + if (noOfComp == 0) { + /** + * This timeout situation can occur if NDB crashes. + */ + ndbout << "This timeout should never occur, execute(..)" << endl; + setOperationErrorCodeAbort(4012); // Error code for "Cluster Failure" + DBUG_RETURN(-1); + }//if + + /* + * Check that the completed transactions include this one. There + * could be another thread running asynchronously. Even in pure + * async case rollback is done synchronously. + */ + if (theListState != NotInList) + continue; +#ifdef VM_TRACE + unsigned anyway = 0; + for (unsigned i = 0; i < theNdb->theNoOfPreparedTransactions; i++) + anyway += theNdb->thePreparedTransactionsArray[i] == this; + for (unsigned i = 0; i < theNdb->theNoOfSentTransactions; i++) + anyway += theNdb->theSentTransactionsArray[i] == this; + for (unsigned i = 0; i < theNdb->theNoOfCompletedTransactions; i++) + anyway += theNdb->theCompletedTransactionsArray[i] == this; + if (anyway) { + theNdb->printState("execute %x", this); + abort(); + } +#endif + if (theReturnStatus == ReturnFailure) { + DBUG_RETURN(-1); + }//if + break; + } + } + thePendingBlobOps = 0; + DBUG_RETURN(0); +}//NdbTransaction::execute() + +/***************************************************************************** +void executeAsynchPrepare(ExecType aTypeOfExec, + NdbAsynchCallback callBack, + void* anyObject, + CommitType aTypeOfCommit); + +Return Value: No return value +Parameters : aTypeOfExec: Type of execute. + anyObject: An object provided in the callback method + callBack: The callback method + aTypeOfCommit: What to do when read/updated/deleted records + are missing or inserted records already exist. + +Remark: Prepare a part of a transaction in an asynchronous manner. +*****************************************************************************/ +void +NdbTransaction::executeAsynchPrepare( ExecType aTypeOfExec, + NdbAsynchCallback aCallback, + void* anyObject, + AbortOption abortOption) +{ + DBUG_ENTER("NdbTransaction::executeAsynchPrepare"); + DBUG_PRINT("enter", ("aTypeOfExec: %d, aCallback: %x, anyObject: %x", + aTypeOfExec, aCallback, anyObject)); + + /** + * Reset error.code on execute + */ + if (theError.code != 0) + DBUG_PRINT("enter", ("Resetting error %d on execute", theError.code)); + theError.code = 0; + NdbScanOperation* tcOp = m_theFirstScanOperation; + if (tcOp != 0){ + // Execute any cursor operations + while (tcOp != NULL) { + int tReturnCode; + tReturnCode = tcOp->executeCursor(theDBnode); + if (tReturnCode == -1) { + DBUG_VOID_RETURN; + }//if + tcOp = (NdbScanOperation*)tcOp->next(); + } // while + m_theLastScanOperation->next(m_firstExecutedScanOp); + m_firstExecutedScanOp = m_theFirstScanOperation; + // Discard cursor operations, since these are also + // in the complete operations list we do not need + // to release them. + m_theFirstScanOperation = m_theLastScanOperation = NULL; + } + + bool tTransactionIsStarted = theTransactionIsStarted; + NdbOperation* tLastOp = theLastOpInList; + Ndb* tNdb = theNdb; + CommitStatusType tCommitStatus = theCommitStatus; + Uint32 tnoOfPreparedTransactions = tNdb->theNoOfPreparedTransactions; + + theReturnStatus = ReturnSuccess; + theCallbackFunction = aCallback; + theCallbackObject = anyObject; + m_abortOption = abortOption; + m_waitForReply = true; + tNdb->thePreparedTransactionsArray[tnoOfPreparedTransactions] = this; + theTransArrayIndex = tnoOfPreparedTransactions; + theListState = InPreparedList; + tNdb->theNoOfPreparedTransactions = tnoOfPreparedTransactions + 1; + + if ((tCommitStatus != Started) || + (aTypeOfExec == Rollback)) { +/***************************************************************************** + * Rollback have been ordered on a started transaction. Call rollback. + * Could also be state problem or previous problem which leads to the + * same action. + ****************************************************************************/ + if (aTypeOfExec == Rollback) { + if (theTransactionIsStarted == false || theSimpleState) { + theCommitStatus = Aborted; + theSendStatus = sendCompleted; + } else { + theSendStatus = sendABORT; + } + } else { + theSendStatus = sendABORTfail; + }//if + if (theCommitStatus == Aborted){ + DBUG_PRINT("exit", ("theCommitStatus: Aborted")); + setErrorCode(4350); + } + DBUG_VOID_RETURN; + }//if + if (tTransactionIsStarted == true) { + if (tLastOp != NULL) { + if (aTypeOfExec == Commit) { +/***************************************************************************** + * Set commit indicator on last operation when commit has been ordered + * and also a number of operations. +******************************************************************************/ + tLastOp->theCommitIndicator = 1; + }//if + } else { + if (aTypeOfExec == Commit && !theSimpleState) { + /********************************************************************** + * A Transaction have been started and no more operations exist. + * We will use the commit method. + *********************************************************************/ + theSendStatus = sendCOMMITstate; + DBUG_VOID_RETURN; + } else { + /********************************************************************** + * We need to put it into the array of completed transactions to + * ensure that we report the completion in a proper way. + * We cannot do this here since that would endanger the completed + * transaction array since that is also updated from the receiver + * thread and thus we need to do it under mutex lock and thus we + * set the sendStatus to ensure that the send method will + * put it into the completed array. + **********************************************************************/ + theSendStatus = sendCompleted; + DBUG_VOID_RETURN; // No Commit with no operations is OK + }//if + }//if + } else if (tTransactionIsStarted == false) { + NdbOperation* tFirstOp = theFirstOpInList; + if (tLastOp != NULL) { + tFirstOp->setStartIndicator(); + if (aTypeOfExec == Commit) { + tLastOp->theCommitIndicator = 1; + }//if + } else { + /*********************************************************************** + * No operations are defined and we have not started yet. + * Simply return OK. Set commit status if Commit. + ***********************************************************************/ + if (aTypeOfExec == Commit) { + theCommitStatus = Committed; + }//if + /*********************************************************************** + * We need to put it into the array of completed transactions to + * ensure that we report the completion in a proper way. We + * cannot do this here since that would endanger the completed + * transaction array since that is also updated from the + * receiver thread and thus we need to do it under mutex lock + * and thus we set the sendStatus to ensure that the send method + * will put it into the completed array. + ***********************************************************************/ + theSendStatus = sendCompleted; + DBUG_VOID_RETURN; + }//if + } + + NdbOperation* tOp = theFirstOpInList; + theCompletionStatus = NotCompleted; + while (tOp) { + int tReturnCode; + NdbOperation* tNextOp = tOp->next(); + + tReturnCode = tOp->prepareSend(theTCConPtr, theTransactionId); + if (tReturnCode == -1) { + theSendStatus = sendABORTfail; + DBUG_VOID_RETURN; + }//if + + /************************************************************************* + * Now that we have successfully prepared the send of this operation we + * move it to the list of executing operations and remove it from the + * list of defined operations. + ************************************************************************/ + tOp = tNextOp; + } + + NdbOperation* tLastOpInList = theLastOpInList; + NdbOperation* tFirstOpInList = theFirstOpInList; + + theFirstOpInList = NULL; + theLastOpInList = NULL; + theFirstExecOpInList = tFirstOpInList; + theLastExecOpInList = tLastOpInList; + + theCompletionStatus = CompletedSuccess; + theNoOfOpSent = 0; + theNoOfOpCompleted = 0; + theSendStatus = sendOperations; + NdbNodeBitmask::clear(m_db_nodes); + NdbNodeBitmask::clear(m_failed_db_nodes); + DBUG_VOID_RETURN; +}//NdbTransaction::executeAsynchPrepare() + +void NdbTransaction::close() +{ + theNdb->closeTransaction(this); +} + +int NdbTransaction::refresh(){ + return sendTC_HBREP(); +} + +/***************************************************************************** +int sendTC_HBREP(); + +Return Value: No return value. +Parameters : None. +Remark: Order NDB to refresh the timeout counter of the transaction. +******************************************************************************/ +int +NdbTransaction::sendTC_HBREP() // Send a TC_HBREP signal; +{ + NdbApiSignal* tSignal; + Ndb* tNdb = theNdb; + Uint32 tTransId1, tTransId2; + + tSignal = tNdb->getSignal(); + if (tSignal == NULL) { + return -1; + } + + if (tSignal->setSignal(GSN_TC_HBREP) == -1) { + return -1; + } + + TcHbRep * const tcHbRep = CAST_PTR(TcHbRep, tSignal->getDataPtrSend()); + + tcHbRep->apiConnectPtr = theTCConPtr; + + tTransId1 = (Uint32) theTransactionId; + tTransId2 = (Uint32) (theTransactionId >> 32); + tcHbRep->transId1 = tTransId1; + tcHbRep->transId2 = tTransId2; + + TransporterFacade *tp = TransporterFacade::instance(); + tp->lock_mutex(); + const int res = tp->sendSignal(tSignal,theDBnode); + tp->unlock_mutex(); + tNdb->releaseSignal(tSignal); + + if (res == -1){ + return -1; + } + + return 0; +}//NdbTransaction::sendTC_HBREP() + +/***************************************************************************** +int doSend(); + +Return Value: Return 0 : send was successful. + Return -1: In all other case. +Remark: Send all operations belonging to this connection. + The caller of this method has the responsibility to remove the + object from the prepared transactions array on the Ndb-object. +*****************************************************************************/ +int +NdbTransaction::doSend() +{ + DBUG_ENTER("NdbTransaction::doSend"); + + /* + This method assumes that at least one operation have been defined. This + is ensured by the caller of this routine (=execute). + */ + + switch(theSendStatus){ + case sendOperations: { + NdbOperation * tOp = theFirstExecOpInList; + do { + NdbOperation* tNextOp = tOp->next(); + const Uint32 lastFlag = ((tNextOp == NULL) ? 1 : 0); + const int tReturnCode = tOp->doSend(theDBnode, lastFlag); + if (tReturnCode == -1) { + theReturnStatus = ReturnFailure; + break; + }//if + tOp = tNextOp; + } while (tOp != NULL); + Ndb* tNdb = theNdb; + theSendStatus = sendTC_OP; + theTransactionIsStarted = true; + tNdb->insert_sent_list(this); + DBUG_RETURN(0); + }//case + case sendABORT: + case sendABORTfail:{ + /*********************************************************************** + * Rollback have been ordered on a not started transaction. + * Simply return OK and set abort status. + ***********************************************************************/ + if (theSendStatus == sendABORTfail) { + theReturnStatus = ReturnFailure; + }//if + if (sendROLLBACK() == 0) { + DBUG_RETURN(0); + }//if + break; + }//case + case sendCOMMITstate: + if (sendCOMMIT() == 0) { + DBUG_RETURN(0); + }//if + break; + case sendCompleted: + theNdb->insert_completed_list(this); + DBUG_RETURN(0); + default: + ndbout << "Inconsistent theSendStatus = " + << (Uint32) theSendStatus << endl; + abort(); + break; + }//switch + setOperationErrorCodeAbort(4002); + theReleaseOnClose = true; + theTransactionIsStarted = false; + theCommitStatus = Aborted; + DBUG_RETURN(-1); +}//NdbTransaction::doSend() + +/************************************************************************** +int sendROLLBACK(); + +Return Value: Return -1 if send unsuccessful. +Parameters : None. +Remark: Order NDB to rollback the transaction. +**************************************************************************/ +int +NdbTransaction::sendROLLBACK() // Send a TCROLLBACKREQ signal; +{ + Ndb* tNdb = theNdb; + if ((theTransactionIsStarted == true) && + (theCommitStatus != Committed) && + (theCommitStatus != Aborted)) { +/************************************************************************** + * The user did not perform any rollback but simply closed the + * transaction. We must rollback Ndb since Ndb have been contacted. + *************************************************************************/ + NdbApiSignal tSignal(tNdb->theMyRef); + Uint32 tTransId1, tTransId2; + TransporterFacade *tp = TransporterFacade::instance(); + int tReturnCode; + + tTransId1 = (Uint32) theTransactionId; + tTransId2 = (Uint32) (theTransactionId >> 32); + tSignal.setSignal(GSN_TCROLLBACKREQ); + tSignal.setData(theTCConPtr, 1); + tSignal.setData(tTransId1, 2); + tSignal.setData(tTransId2, 3); + tReturnCode = tp->sendSignal(&tSignal,theDBnode); + if (tReturnCode != -1) { + theSendStatus = sendTC_ROLLBACK; + tNdb->insert_sent_list(this); + return 0; + }//if + /********************************************************************* + * It was not possible to abort the transaction towards the NDB kernel + * and thus we put it into the array of completed transactions that + * are ready for reporting to the application. + *********************************************************************/ + return -1; + } else { + /* + It is not necessary to abort the transaction towards the NDB kernel and + thus we put it into the array of completed transactions that are ready + for reporting to the application. + */ + theSendStatus = sendCompleted; + tNdb->insert_completed_list(this); + return 0; + ; + }//if +}//NdbTransaction::sendROLLBACK() + +/*************************************************************************** +int sendCOMMIT(); + +Return Value: Return 0 : send was successful. + Return -1: In all other case. +Parameters : None. +Remark: Order NDB to commit the transaction. +***************************************************************************/ +int +NdbTransaction::sendCOMMIT() // Send a TC_COMMITREQ signal; +{ + NdbApiSignal tSignal(theNdb->theMyRef); + Uint32 tTransId1, tTransId2; + TransporterFacade *tp = TransporterFacade::instance(); + int tReturnCode; + + tTransId1 = (Uint32) theTransactionId; + tTransId2 = (Uint32) (theTransactionId >> 32); + tSignal.setSignal(GSN_TC_COMMITREQ); + tSignal.setData(theTCConPtr, 1); + tSignal.setData(tTransId1, 2); + tSignal.setData(tTransId2, 3); + + tReturnCode = tp->sendSignal(&tSignal,theDBnode); + if (tReturnCode != -1) { + theSendStatus = sendTC_COMMIT; + theNdb->insert_sent_list(this); + return 0; + } else { + return -1; + }//if +}//NdbTransaction::sendCOMMIT() + +/****************************************************************************** +void release(); + +Remark: Release all operations. +******************************************************************************/ +void +NdbTransaction::release(){ + releaseOperations(); + if ( (theTransactionIsStarted == true) && + ((theCommitStatus != Committed) && + (theCommitStatus != Aborted))) { + /************************************************************************ + * The user did not perform any rollback but simply closed the + * transaction. We must rollback Ndb since Ndb have been contacted. + ************************************************************************/ + execute(Rollback); + }//if + theMagicNumber = 0xFE11DC; + theInUseState = false; +#ifdef VM_TRACE + if (theListState != NotInList) { + theNdb->printState("release %x", this); + abort(); + } +#endif +}//NdbTransaction::release() + +void +NdbTransaction::releaseOps(NdbOperation* tOp){ + while (tOp != NULL) { + NdbOperation* tmp = tOp; + tOp->release(); + tOp = tOp->next(); + theNdb->releaseOperation(tmp); + }//while +} + +/****************************************************************************** +void releaseOperations(); + +Remark: Release all operations. +******************************************************************************/ +void +NdbTransaction::releaseOperations() +{ + // Release any open scans + releaseScanOperations(m_theFirstScanOperation); + releaseScanOperations(m_firstExecutedScanOp); + + releaseOps(theCompletedFirstOp); + releaseOps(theFirstOpInList); + releaseOps(theFirstExecOpInList); + + theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; + theFirstOpInList = NULL; + theFirstExecOpInList = NULL; + theLastOpInList = NULL; + theLastExecOpInList = NULL; + theScanningOp = NULL; + m_theFirstScanOperation = NULL; + m_theLastScanOperation = NULL; + m_firstExecutedScanOp = NULL; +}//NdbTransaction::releaseOperations() + +void +NdbTransaction::releaseCompletedOperations() +{ + releaseOps(theCompletedFirstOp); + theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; +}//NdbTransaction::releaseOperations() + +/****************************************************************************** +void releaseScanOperations(); + +Remark: Release all cursor operations. + (NdbScanOperation and NdbIndexOperation) +******************************************************************************/ +void +NdbTransaction::releaseScanOperations(NdbIndexScanOperation* cursorOp) +{ + while(cursorOp != 0){ + NdbIndexScanOperation* next = (NdbIndexScanOperation*)cursorOp->next(); + cursorOp->release(); + theNdb->releaseScanOperation(cursorOp); + cursorOp = next; + } +}//NdbTransaction::releaseScanOperations() + +/***************************************************************************** +NdbOperation* getNdbOperation(const char* aTableName); + +Return Value Return a pointer to a NdbOperation object if getNdbOperation + was succesful. + Return NULL : In all other case. +Parameters: aTableName : Name of the database table. +Remark: Get an operation from NdbOperation idlelist and get the + NdbTransaction object + who was fetch by startTransaction pointing to this operation + getOperation will set the theTableId in the NdbOperation object. + synchronous +******************************************************************************/ +NdbOperation* +NdbTransaction::getNdbOperation(const char* aTableName) +{ + if (theCommitStatus == Started){ + NdbTableImpl* table = theNdb->theDictionary->getTable(aTableName); + if (table != 0){ + return getNdbOperation(table); + } else { + setErrorCode(theNdb->theDictionary->getNdbError().code); + return NULL; + }//if + } + + setOperationErrorCodeAbort(4114); + + return NULL; +}//NdbTransaction::getNdbOperation() + +/***************************************************************************** +NdbOperation* getNdbOperation(int aTableId); + +Return Value Return a pointer to a NdbOperation object if getNdbOperation + was succesful. + Return NULL: In all other case. +Parameters: tableId : Id of the database table beeing deleted. +Remark: Get an operation from NdbOperation object idlelist and + get the NdbTransaction object who was fetch by + startTransaction pointing to this operation + getOperation will set the theTableId in the NdbOperation + object, synchronous. +*****************************************************************************/ +NdbOperation* +NdbTransaction::getNdbOperation(const NdbTableImpl * tab, NdbOperation* aNextOp) +{ + NdbOperation* tOp; + + if (theScanningOp != NULL){ + setErrorCode(4607); + return NULL; + } + + tOp = theNdb->getOperation(); + if (tOp == NULL) + goto getNdbOp_error1; + if (aNextOp == NULL) { + if (theLastOpInList != NULL) { + theLastOpInList->next(tOp); + theLastOpInList = tOp; + } else { + theLastOpInList = tOp; + theFirstOpInList = tOp; + }//if + tOp->next(NULL); + } else { + // add before the given op + if (theFirstOpInList == aNextOp) { + theFirstOpInList = tOp; + } else { + NdbOperation* aLoopOp = theFirstOpInList; + while (aLoopOp != NULL && aLoopOp->next() != aNextOp) + aLoopOp = aLoopOp->next(); + assert(aLoopOp != NULL); + aLoopOp->next(tOp); + } + tOp->next(aNextOp); + } + if (tOp->init(tab, this) != -1) { + return tOp; + } else { + theNdb->releaseOperation(tOp); + }//if + return NULL; + + getNdbOp_error1: + setOperationErrorCodeAbort(4000); + return NULL; +}//NdbTransaction::getNdbOperation() + +NdbOperation* NdbTransaction::getNdbOperation(const NdbDictionary::Table * table) +{ + if (table) + return getNdbOperation(& NdbTableImpl::getImpl(*table)); + else + return NULL; +}//NdbTransaction::getNdbOperation() + +// NdbScanOperation +/***************************************************************************** +NdbScanOperation* getNdbScanOperation(const char* aTableName); + +Return Value Return a pointer to a NdbScanOperation object if getNdbScanOperation was succesful. + Return NULL : In all other case. +Parameters: aTableName : Name of the database table. +Remark: Get an operation from NdbScanOperation idlelist and get the NdbTransaction object + who was fetch by startTransaction pointing to this operation + getOperation will set the theTableId in the NdbOperation object.synchronous +******************************************************************************/ +NdbScanOperation* +NdbTransaction::getNdbScanOperation(const char* aTableName) +{ + if (theCommitStatus == Started){ + NdbTableImpl* tab = theNdb->theDictionary->getTable(aTableName); + if (tab != 0){ + return getNdbScanOperation(tab); + } else { + setOperationErrorCodeAbort(theNdb->theDictionary->m_error.code); + return NULL; + }//if + } + + setOperationErrorCodeAbort(4114); + return NULL; +}//NdbTransaction::getNdbScanOperation() + +/***************************************************************************** +NdbScanOperation* getNdbIndexScanOperation(const char* anIndexName, const char* aTableName); + +Return Value Return a pointer to a NdbIndexScanOperation object if getNdbIndexScanOperation was succesful. + Return NULL : In all other case. +Parameters: anIndexName : Name of the index to use. + aTableName : Name of the database table. +Remark: Get an operation from NdbIndexScanOperation idlelist and get the NdbTransaction object + who was fetch by startTransaction pointing to this operation + getOperation will set the theTableId in the NdbIndexScanOperation object.synchronous +******************************************************************************/ +NdbIndexScanOperation* +NdbTransaction::getNdbIndexScanOperation(const char* anIndexName, + const char* aTableName) +{ + NdbIndexImpl* index = + theNdb->theDictionary->getIndex(anIndexName, aTableName); + if (index == 0) + { + setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code); + return 0; + } + NdbTableImpl* table = theNdb->theDictionary->getTable(aTableName); + if (table == 0) + { + setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code); + return 0; + } + + return getNdbIndexScanOperation(index, table); +} + +NdbIndexScanOperation* +NdbTransaction::getNdbIndexScanOperation(const NdbIndexImpl* index, + const NdbTableImpl* table) +{ + if (theCommitStatus == Started){ + const NdbTableImpl * indexTable = index->getIndexTable(); + if (indexTable != 0){ + NdbIndexScanOperation* tOp = getNdbScanOperation(indexTable); + if(tOp) + { + tOp->m_currentTable = table; + } + return tOp; + } else { + setOperationErrorCodeAbort(4271); + return NULL; + }//if + } + + setOperationErrorCodeAbort(4114); + return NULL; +}//NdbTransaction::getNdbIndexScanOperation() + +NdbIndexScanOperation* +NdbTransaction::getNdbIndexScanOperation(const NdbDictionary::Index * index) +{ + if (index) + { + const NdbDictionary::Table *table= + theNdb->theDictionary->getTable(index->getTable()); + + if (table) + return getNdbIndexScanOperation(index, table); + + setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code); + return NULL; + } + setOperationErrorCodeAbort(4271); + return NULL; +} + +NdbIndexScanOperation* +NdbTransaction::getNdbIndexScanOperation(const NdbDictionary::Index * index, + const NdbDictionary::Table * table) +{ + if (index && table) + return getNdbIndexScanOperation(& NdbIndexImpl::getImpl(*index), + & NdbTableImpl::getImpl(*table)); + setOperationErrorCodeAbort(4271); + return NULL; +}//NdbTransaction::getNdbIndexScanOperation() + +/***************************************************************************** +NdbScanOperation* getNdbScanOperation(int aTableId); + +Return Value Return a pointer to a NdbScanOperation object if getNdbScanOperation was succesful. + Return NULL: In all other case. +Parameters: tableId : Id of the database table beeing deleted. +Remark: Get an operation from NdbScanOperation object idlelist and get the NdbTransaction + object who was fetch by startTransaction pointing to this operation + getOperation will set the theTableId in the NdbScanOperation object, synchronous. +*****************************************************************************/ +NdbIndexScanOperation* +NdbTransaction::getNdbScanOperation(const NdbTableImpl * tab) +{ + NdbIndexScanOperation* tOp; + + tOp = theNdb->getScanOperation(); + if (tOp == NULL) + goto getNdbOp_error1; + + if (tOp->init(tab, this) != -1) { + define_scan_op(tOp); + return tOp; + } else { + theNdb->releaseScanOperation(tOp); + }//if + return NULL; + +getNdbOp_error1: + setOperationErrorCodeAbort(4000); + return NULL; +}//NdbTransaction::getNdbScanOperation() + +void +NdbTransaction::remove_list(NdbOperation*& list, NdbOperation* op){ + NdbOperation* tmp= list; + if(tmp == op) + list = op->next(); + else { + while(tmp && tmp->next() != op) tmp = tmp->next(); + if(tmp) + tmp->next(op->next()); + } + op->next(NULL); +} + +void +NdbTransaction::define_scan_op(NdbIndexScanOperation * tOp){ + // Link scan operation into list of cursor operations + if (m_theLastScanOperation == NULL) + m_theFirstScanOperation = m_theLastScanOperation = tOp; + else { + m_theLastScanOperation->next(tOp); + m_theLastScanOperation = tOp; + } + tOp->next(NULL); +} + +NdbScanOperation* +NdbTransaction::getNdbScanOperation(const NdbDictionary::Table * table) +{ + if (table) + return getNdbScanOperation(& NdbTableImpl::getImpl(*table)); + else + return NULL; +}//NdbTransaction::getNdbScanOperation() + + +// IndexOperation +/***************************************************************************** +NdbIndexOperation* getNdbIndexOperation(const char* anIndexName, + const char* aTableName); + +Return Value Return a pointer to a NdbOperation object if getNdbIndexOperation was succesful. + Return NULL : In all other case. +Parameters: aTableName : Name of the database table. +Remark: Get an operation from NdbIndexOperation idlelist and get the NdbTransaction object + who was fetch by startTransaction pointing to this operation + getOperation will set the theTableId in the NdbIndexOperation object.synchronous +******************************************************************************/ +NdbIndexOperation* +NdbTransaction::getNdbIndexOperation(const char* anIndexName, + const char* aTableName) +{ + if (theCommitStatus == Started) { + NdbTableImpl * table = theNdb->theDictionary->getTable(aTableName); + NdbIndexImpl * index; + + if (table == 0) + { + setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code); + return NULL; + } + + if (table->m_frm.get_data()) + { + // This unique index is defined from SQL level + static const char* uniqueSuffix= "$unique"; + char uniqueIndexName[MAX_TAB_NAME_SIZE]; + + strxnmov(uniqueIndexName, MAX_TAB_NAME_SIZE, anIndexName, uniqueSuffix, NullS); + index = theNdb->theDictionary->getIndex(uniqueIndexName, + aTableName); + } + else + index = theNdb->theDictionary->getIndex(anIndexName, + aTableName); + if(table != 0 && index != 0){ + return getNdbIndexOperation(index, table); + } + + if(index == 0){ + setOperationErrorCodeAbort(4243); + return NULL; + } + + setOperationErrorCodeAbort(4243); + return NULL; + } + + setOperationErrorCodeAbort(4114); + return 0; +}//NdbTransaction::getNdbIndexOperation() + +/***************************************************************************** +NdbIndexOperation* getNdbIndexOperation(int anIndexId, int aTableId); + +Return Value Return a pointer to a NdbIndexOperation object if getNdbIndexOperation was succesful. + Return NULL: In all other case. +Parameters: tableId : Id of the database table beeing deleted. +Remark: Get an operation from NdbIndexOperation object idlelist and get the NdbTransaction + object who was fetch by startTransaction pointing to this operation + getOperation will set the theTableId in the NdbIndexOperation object, synchronous. +*****************************************************************************/ +NdbIndexOperation* +NdbTransaction::getNdbIndexOperation(const NdbIndexImpl * anIndex, + const NdbTableImpl * aTable, + NdbOperation* aNextOp) +{ + NdbIndexOperation* tOp; + + tOp = theNdb->getIndexOperation(); + if (tOp == NULL) + goto getNdbOp_error1; + if (aNextOp == NULL) { + if (theLastOpInList != NULL) { + theLastOpInList->next(tOp); + theLastOpInList = tOp; + } else { + theLastOpInList = tOp; + theFirstOpInList = tOp; + }//if + tOp->next(NULL); + } else { + // add before the given op + if (theFirstOpInList == aNextOp) { + theFirstOpInList = tOp; + } else { + NdbOperation* aLoopOp = theFirstOpInList; + while (aLoopOp != NULL && aLoopOp->next() != aNextOp) + aLoopOp = aLoopOp->next(); + assert(aLoopOp != NULL); + aLoopOp->next(tOp); + } + tOp->next(aNextOp); + } + if (tOp->indxInit(anIndex, aTable, this)!= -1) { + return tOp; + } else { + theNdb->releaseOperation(tOp); + }//if + return NULL; + + getNdbOp_error1: + setOperationErrorCodeAbort(4000); + return NULL; +}//NdbTransaction::getNdbIndexOperation() + +NdbIndexOperation* +NdbTransaction::getNdbIndexOperation(const NdbDictionary::Index * index) +{ + if (index) + { + const NdbDictionary::Table *table= + theNdb->theDictionary->getTable(index->getTable()); + + if (table) + return getNdbIndexOperation(index, table); + + setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code); + return NULL; + } + setOperationErrorCodeAbort(4271); + return NULL; +} + +NdbIndexOperation* +NdbTransaction::getNdbIndexOperation(const NdbDictionary::Index * index, + const NdbDictionary::Table * table) +{ + if (index && table) + return getNdbIndexOperation(& NdbIndexImpl::getImpl(*index), + & NdbTableImpl::getImpl(*table)); + + setOperationErrorCodeAbort(4271); + return NULL; +}//NdbTransaction::getNdbIndexOperation() + + +/******************************************************************************* +int receiveDIHNDBTAMPER(NdbApiSignal* aSignal) + +Return Value: Return 0 : receiveDIHNDBTAMPER was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: Sets theRestartGCI in the NDB object. +*******************************************************************************/ +int +NdbTransaction::receiveDIHNDBTAMPER(NdbApiSignal* aSignal) +{ + if (theStatus != Connecting) { + return -1; + } else { + theNdb->RestartGCI((Uint32)aSignal->readData(2)); + theStatus = Connected; + }//if + return 0; +}//NdbTransaction::receiveDIHNDBTAMPER() + +/******************************************************************************* +int receiveTCSEIZECONF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTCSEIZECONF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: Sets TC Connect pointer at reception of TCSEIZECONF. +*******************************************************************************/ +int +NdbTransaction::receiveTCSEIZECONF(NdbApiSignal* aSignal) +{ + if (theStatus != Connecting) + { + return -1; + } else + { + theTCConPtr = (Uint32)aSignal->readData(2); + theStatus = Connected; + } + return 0; +}//NdbTransaction::receiveTCSEIZECONF() + +/******************************************************************************* +int receiveTCSEIZEREF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTCSEIZEREF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: Sets TC Connect pointer. +*******************************************************************************/ +int +NdbTransaction::receiveTCSEIZEREF(NdbApiSignal* aSignal) +{ + DBUG_ENTER("NdbTransaction::receiveTCSEIZEREF"); + if (theStatus != Connecting) + { + DBUG_RETURN(-1); + } else + { + theStatus = ConnectFailure; + theNdb->theError.code = aSignal->readData(2); + DBUG_PRINT("info",("error code %d, %s", + theNdb->getNdbError().code, + theNdb->getNdbError().message)); + DBUG_RETURN(0); + } +}//NdbTransaction::receiveTCSEIZEREF() + +/******************************************************************************* +int receiveTCRELEASECONF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTCRELEASECONF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: DisConnect TC Connect pointer to NDBAPI. +*******************************************************************************/ +int +NdbTransaction::receiveTCRELEASECONF(NdbApiSignal* aSignal) +{ + if (theStatus != DisConnecting) + { + return -1; + } else + { + theStatus = NotConnected; + } + return 0; +}//NdbTransaction::receiveTCRELEASECONF() + +/******************************************************************************* +int receiveTCRELEASEREF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTCRELEASEREF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: DisConnect TC Connect pointer to NDBAPI Failure. +*******************************************************************************/ +int +NdbTransaction::receiveTCRELEASEREF(NdbApiSignal* aSignal) +{ + if (theStatus != DisConnecting) { + return -1; + } else { + theStatus = ConnectFailure; + theNdb->theError.code = aSignal->readData(2); + return 0; + }//if +}//NdbTransaction::receiveTCRELEASEREF() + +/****************************************************************************** +int receiveTC_COMMITCONF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTC_COMMITCONF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: +******************************************************************************/ +int +NdbTransaction::receiveTC_COMMITCONF(const TcCommitConf * commitConf) +{ + if(checkState_TransId(&commitConf->transId1)){ + theCommitStatus = Committed; + theCompletionStatus = CompletedSuccess; + return 0; + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + return -1; +}//NdbTransaction::receiveTC_COMMITCONF() + +/****************************************************************************** +int receiveTC_COMMITREF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTC_COMMITREF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: +******************************************************************************/ +int +NdbTransaction::receiveTC_COMMITREF(NdbApiSignal* aSignal) +{ + const TcCommitRef * ref = CAST_CONSTPTR(TcCommitRef, aSignal->getDataPtr()); + if(checkState_TransId(&ref->transId1)){ + setOperationErrorCodeAbort(ref->errorCode); + theCommitStatus = Aborted; + theCompletionStatus = CompletedFailure; + theReturnStatus = ReturnFailure; + return 0; + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + + return -1; +}//NdbTransaction::receiveTC_COMMITREF() + +/****************************************************************************** +int receiveTCROLLBACKCONF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTCROLLBACKCONF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: +******************************************************************************/ +int +NdbTransaction::receiveTCROLLBACKCONF(NdbApiSignal* aSignal) +{ + if(checkState_TransId(aSignal->getDataPtr() + 1)){ + theCommitStatus = Aborted; + theCompletionStatus = CompletedSuccess; + return 0; + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + + return -1; +}//NdbTransaction::receiveTCROLLBACKCONF() + +/******************************************************************************* +int receiveTCROLLBACKREF(NdbApiSignal* aSignal); + +Return Value: Return 0 : receiveTCROLLBACKREF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: +*******************************************************************************/ +int +NdbTransaction::receiveTCROLLBACKREF(NdbApiSignal* aSignal) +{ + if(checkState_TransId(aSignal->getDataPtr() + 1)){ + setOperationErrorCodeAbort(aSignal->readData(4)); + theCommitStatus = Aborted; + theCompletionStatus = CompletedFailure; + theReturnStatus = ReturnFailure; + return 0; + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + + return -1; +}//NdbTransaction::receiveTCROLLBACKREF() + +/***************************************************************************** +int receiveTCROLLBACKREP( NdbApiSignal* aSignal) + +Return Value: Return 0 : send was succesful. + Return -1: In all other case. +Parameters: aSignal: the signal object that contains the + TCROLLBACKREP signal from TC. +Remark: Handles the reception of the ROLLBACKREP signal. +*****************************************************************************/ +int +NdbTransaction::receiveTCROLLBACKREP( NdbApiSignal* aSignal) +{ + /**************************************************************************** +Check that we are expecting signals from this transaction and that it doesn't +belong to a transaction already completed. Simply ignore messages from other +transactions. + ****************************************************************************/ + if(checkState_TransId(aSignal->getDataPtr() + 1)){ + theError.code = aSignal->readData(4);// Override any previous errors + + /**********************************************************************/ + /* A serious error has occured. This could be due to deadlock or */ + /* lack of resources or simply a programming error in NDB. This */ + /* transaction will be aborted. Actually it has already been */ + /* and we only need to report completion and return with the */ + /* error code to the application. */ + /**********************************************************************/ + theCompletionStatus = CompletedFailure; + theCommitStatus = Aborted; + theReturnStatus = ReturnFailure; + return 0; + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + + return -1; +}//NdbTransaction::receiveTCROLLBACKREP() + +/******************************************************************************* +int receiveTCKEYCONF(NdbApiSignal* aSignal, Uint32 long_short_ind); + +Return Value: Return 0 : receiveTCKEYCONF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: +*******************************************************************************/ +int +NdbTransaction::receiveTCKEYCONF(const TcKeyConf * keyConf, Uint32 aDataLength) +{ + NdbReceiver* tOp; + const Uint32 tTemp = keyConf->confInfo; + /*************************************************************************** +Check that we are expecting signals from this transaction and that it +doesn't belong to a transaction already completed. Simply ignore messages +from other transactions. + ***************************************************************************/ + if(checkState_TransId(&keyConf->transId1)){ + + const Uint32 tNoOfOperations = TcKeyConf::getNoOfOperations(tTemp); + const Uint32 tCommitFlag = TcKeyConf::getCommitFlag(tTemp); + + const Uint32* tPtr = (Uint32 *)&keyConf->operations[0]; + Uint32 tNoComp = theNoOfOpCompleted; + for (Uint32 i = 0; i < tNoOfOperations ; i++) { + tOp = theNdb->void2rec(theNdb->int2void(*tPtr++)); + const Uint32 tAttrInfoLen = *tPtr++; + if (tOp && tOp->checkMagicNumber()) { + Uint32 done = tOp->execTCOPCONF(tAttrInfoLen); + if(tAttrInfoLen > TcKeyConf::SimpleReadBit){ + Uint32 node = tAttrInfoLen & (~TcKeyConf::SimpleReadBit); + NdbNodeBitmask::set(m_db_nodes, node); + if(NdbNodeBitmask::get(m_failed_db_nodes, node) && !done) + { + done = 1; + tOp->setErrorCode(4119); + theCompletionStatus = CompletedFailure; + theReturnStatus = NdbTransaction::ReturnFailure; + } + } + tNoComp += done; + } else { + return -1; + }//if + }//for + Uint32 tNoSent = theNoOfOpSent; + theNoOfOpCompleted = tNoComp; + Uint32 tGCI = keyConf->gci; + if (tCommitFlag == 1) { + theCommitStatus = Committed; + theGlobalCheckpointId = tGCI; + } else if ((tNoComp >= tNoSent) && + (theLastExecOpInList->theCommitIndicator == 1)){ + + + if (m_abortOption == AO_IgnoreError && theError.code != 0){ + /** + * There's always a TCKEYCONF when using IgnoreError + */ + return -1; + } +/**********************************************************************/ +// We sent the transaction with Commit flag set and received a CONF with +// no Commit flag set. This is clearly an anomaly. +/**********************************************************************/ + theError.code = 4011; + theCompletionStatus = CompletedFailure; + theReturnStatus = NdbTransaction::ReturnFailure; + theCommitStatus = Aborted; + return 0; + }//if + if (tNoComp >= tNoSent) { + return 0; // No more operations to wait for + }//if + // Not completed the reception yet. + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + + return -1; +}//NdbTransaction::receiveTCKEYCONF() + +/***************************************************************************** +int receiveTCKEY_FAILCONF( NdbApiSignal* aSignal) + +Return Value: Return 0 : receive was completed. + Return -1: In all other case. +Parameters: aSignal: the signal object that contains the + TCKEY_FAILCONF signal from TC. +Remark: Handles the reception of the TCKEY_FAILCONF signal. +*****************************************************************************/ +int +NdbTransaction::receiveTCKEY_FAILCONF(const TcKeyFailConf * failConf) +{ + NdbOperation* tOp; + /* + Check that we are expecting signals from this transaction and that it + doesn't belong to a transaction already completed. Simply ignore + messages from other transactions. + */ + if(checkState_TransId(&failConf->transId1)){ + /* + A node failure of the TC node occured. The transaction has + been committed. + */ + theCommitStatus = Committed; + tOp = theFirstExecOpInList; + while (tOp != NULL) { + /* + * Check if the transaction expected read values... + * If it did some of them might have gotten lost even if we succeeded + * in committing the transaction. + */ + switch(tOp->theOperationType){ + case NdbOperation::UpdateRequest: + case NdbOperation::InsertRequest: + case NdbOperation::DeleteRequest: + case NdbOperation::WriteRequest: + tOp = tOp->next(); + break; + case NdbOperation::ReadRequest: + case NdbOperation::ReadExclusive: + case NdbOperation::OpenScanRequest: + case NdbOperation::OpenRangeScanRequest: + theCompletionStatus = CompletedFailure; + theReturnStatus = NdbTransaction::ReturnFailure; + setOperationErrorCodeAbort(4115); + tOp = NULL; + break; + case NdbOperation::NotDefined: + case NdbOperation::NotDefined2: + assert(false); + break; + }//if + }//while + theReleaseOnClose = true; + return 0; + } else { +#ifdef VM_TRACE + ndbout_c("Recevied TCKEY_FAILCONF wo/ operation"); +#endif + } + return -1; +}//NdbTransaction::receiveTCKEY_FAILCONF() + +/************************************************************************* +int receiveTCKEY_FAILREF( NdbApiSignal* aSignal) + +Return Value: Return 0 : receive was completed. + Return -1: In all other case. +Parameters: aSignal: the signal object that contains the + TCKEY_FAILREF signal from TC. +Remark: Handles the reception of the TCKEY_FAILREF signal. +**************************************************************************/ +int +NdbTransaction::receiveTCKEY_FAILREF(NdbApiSignal* aSignal) +{ + /* + Check that we are expecting signals from this transaction and + that it doesn't belong to a transaction already + completed. Simply ignore messages from other transactions. + */ + if(checkState_TransId(aSignal->getDataPtr()+1)){ + /* + We received an indication of that this transaction was aborted due to a + node failure. + */ + if (theSendStatus == NdbTransaction::sendTC_ROLLBACK) { + /* + We were in the process of sending a rollback anyways. We will + report it as a success. + */ + theCompletionStatus = NdbTransaction::CompletedSuccess; + } else { + theReturnStatus = NdbTransaction::ReturnFailure; + theCompletionStatus = NdbTransaction::CompletedFailure; + theError.code = 4031; + }//if + theReleaseOnClose = true; + theCommitStatus = NdbTransaction::Aborted; + return 0; + } else { +#ifdef VM_TRACE + ndbout_c("Recevied TCKEY_FAILREF wo/ operation"); +#endif + } + return -1; +}//NdbTransaction::receiveTCKEY_FAILREF() + +/****************************************************************************** +int receiveTCINDXCONF(NdbApiSignal* aSignal, Uint32 long_short_ind); + +Return Value: Return 0 : receiveTCINDXCONF was successful. + Return -1: In all other case. +Parameters: aSignal: The signal object pointer. +Remark: +******************************************************************************/ +int +NdbTransaction::receiveTCINDXCONF(const TcIndxConf * indxConf, + Uint32 aDataLength) +{ + if(checkState_TransId(&indxConf->transId1)){ + const Uint32 tTemp = indxConf->confInfo; + const Uint32 tNoOfOperations = TcIndxConf::getNoOfOperations(tTemp); + const Uint32 tCommitFlag = TcKeyConf::getCommitFlag(tTemp); + + const Uint32* tPtr = (Uint32 *)&indxConf->operations[0]; + Uint32 tNoComp = theNoOfOpCompleted; + for (Uint32 i = 0; i < tNoOfOperations ; i++) { + NdbReceiver* tOp = theNdb->void2rec(theNdb->int2void(*tPtr)); + tPtr++; + const Uint32 tAttrInfoLen = *tPtr; + tPtr++; + if (tOp && tOp->checkMagicNumber()) { + tNoComp += tOp->execTCOPCONF(tAttrInfoLen); + } else { + return -1; + }//if + }//for + Uint32 tNoSent = theNoOfOpSent; + Uint32 tGCI = indxConf->gci; + theNoOfOpCompleted = tNoComp; + if (tCommitFlag == 1) { + theCommitStatus = Committed; + theGlobalCheckpointId = tGCI; + } else if ((tNoComp >= tNoSent) && + (theLastExecOpInList->theCommitIndicator == 1)){ + /**********************************************************************/ + // We sent the transaction with Commit flag set and received a CONF with + // no Commit flag set. This is clearly an anomaly. + /**********************************************************************/ + theError.code = 4011; + theCompletionStatus = NdbTransaction::CompletedFailure; + theCommitStatus = NdbTransaction::Aborted; + theReturnStatus = NdbTransaction::ReturnFailure; + return 0; + }//if + if (tNoComp >= tNoSent) { + return 0; // No more operations to wait for + }//if + // Not completed the reception yet. + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + + return -1; +}//NdbTransaction::receiveTCINDXCONF() + +/***************************************************************************** +int receiveTCINDXREF( NdbApiSignal* aSignal) + +Return Value: Return 0 : send was succesful. + Return -1: In all other case. +Parameters: aSignal: the signal object that contains the + TCINDXREF signal from TC. +Remark: Handles the reception of the TCINDXREF signal. +*****************************************************************************/ +int +NdbTransaction::receiveTCINDXREF( NdbApiSignal* aSignal) +{ + if(checkState_TransId(aSignal->getDataPtr()+1)){ + theError.code = aSignal->readData(4); // Override any previous errors + + /**********************************************************************/ + /* A serious error has occured. This could be due to deadlock or */ + /* lack of resources or simply a programming error in NDB. This */ + /* transaction will be aborted. Actually it has already been */ + /* and we only need to report completion and return with the */ + /* error code to the application. */ + /**********************************************************************/ + theCompletionStatus = NdbTransaction::CompletedFailure; + theCommitStatus = NdbTransaction::Aborted; + theReturnStatus = NdbTransaction::ReturnFailure; + return 0; + } else { +#ifdef NDB_NO_DROPPED_SIGNAL + abort(); +#endif + } + + return -1; +}//NdbTransaction::receiveTCINDXREF() + +/******************************************************************************* +int OpCompletedFailure(); + +Return Value: Return 0 : OpCompleteSuccess was successful. + Return -1: In all other case. +Parameters: aErrorCode: The error code. +Remark: An operation was completed with failure. +*******************************************************************************/ +int +NdbTransaction::OpCompleteFailure(Uint8 abortOption, bool setFailure) +{ + Uint32 tNoComp = theNoOfOpCompleted; + Uint32 tNoSent = theNoOfOpSent; + if (setFailure) + theCompletionStatus = NdbTransaction::CompletedFailure; + tNoComp++; + theNoOfOpCompleted = tNoComp; + if (tNoComp == tNoSent) { + //------------------------------------------------------------------------ + //If the transaction consists of only simple reads we can set + //Commit state Aborted. Otherwise this simple operation cannot + //decide the success of the whole transaction since a simple + //operation is not really part of that transaction. + //------------------------------------------------------------------------ + if (abortOption == AO_IgnoreError){ + /** + * There's always a TCKEYCONF when using IgnoreError + */ + return -1; + } + + return 0; // Last operation received + } else if (tNoComp > tNoSent) { + setOperationErrorCodeAbort(4113); // Too many operations, + // stop waiting for more + return 0; + } else { + return -1; // Continue waiting for more signals + }//if +}//NdbTransaction::OpCompleteFailure() + +/****************************************************************************** +int OpCompleteSuccess(); + +Return Value: Return 0 : OpCompleteSuccess was successful. + Return -1: In all other case. +Remark: An operation was completed with success. +*******************************************************************************/ +int +NdbTransaction::OpCompleteSuccess() +{ + Uint32 tNoComp = theNoOfOpCompleted; + Uint32 tNoSent = theNoOfOpSent; + tNoComp++; + theNoOfOpCompleted = tNoComp; + if (tNoComp == tNoSent) { // Last operation completed + return 0; + } else if (tNoComp < tNoSent) { + return -1; // Continue waiting for more signals + } else { + setOperationErrorCodeAbort(4113); // Too many operations, + // stop waiting for more + theCompletionStatus = NdbTransaction::CompletedFailure; + theReturnStatus = NdbTransaction::ReturnFailure; + return 0; + }//if +}//NdbTransaction::OpCompleteSuccess() + +/****************************************************************************** + int getGCI(); + +Remark: Get global checkpoint identity of the transaction +*******************************************************************************/ +int +NdbTransaction::getGCI() +{ + if (theCommitStatus == NdbTransaction::Committed) { + return theGlobalCheckpointId; + }//if + return 0; +}//NdbTransaction::getGCI() + +/******************************************************************************* +Uint64 getTransactionId(void); + +Remark: Get the transaction identity. +*******************************************************************************/ +Uint64 +NdbTransaction::getTransactionId() +{ + return theTransactionId; +}//NdbTransaction::getTransactionId() + +NdbTransaction::CommitStatusType +NdbTransaction::commitStatus() +{ + return theCommitStatus; +}//NdbTransaction::commitStatus() + +int +NdbTransaction::getNdbErrorLine() +{ + return theErrorLine; +} + +NdbOperation* +NdbTransaction::getNdbErrorOperation() +{ + return theErrorOperation; +}//NdbTransaction::getNdbErrorOperation() + +const NdbOperation * +NdbTransaction::getNextCompletedOperation(const NdbOperation * current) const { + if(current == 0) + return theCompletedFirstOp; + return current->theNext; +} + +#ifdef VM_TRACE +#define CASE(x) case x: ndbout << " " << #x; break +void +NdbTransaction::printState() +{ + ndbout << "con=" << hex << this << dec; + ndbout << " node=" << getConnectedNodeId(); + switch (theStatus) { + CASE(NotConnected); + CASE(Connecting); + CASE(Connected); + CASE(DisConnecting); + CASE(ConnectFailure); + default: ndbout << (Uint32) theStatus; + } + switch (theListState) { + CASE(NotInList); + CASE(InPreparedList); + CASE(InSendList); + CASE(InCompletedList); + default: ndbout << (Uint32) theListState; + } + switch (theSendStatus) { + CASE(NotInit); + CASE(InitState); + CASE(sendOperations); + CASE(sendCompleted); + CASE(sendCOMMITstate); + CASE(sendABORT); + CASE(sendABORTfail); + CASE(sendTC_ROLLBACK); + CASE(sendTC_COMMIT); + CASE(sendTC_OP); + default: ndbout << (Uint32) theSendStatus; + } + switch (theCommitStatus) { + CASE(NotStarted); + CASE(Started); + CASE(Committed); + CASE(Aborted); + CASE(NeedAbort); + default: ndbout << (Uint32) theCommitStatus; + } + switch (theCompletionStatus) { + CASE(NotCompleted); + CASE(CompletedSuccess); + CASE(CompletedFailure); + CASE(DefinitionFailure); + default: ndbout << (Uint32) theCompletionStatus; + } + ndbout << endl; +} +#undef CASE +#endif + +int +NdbTransaction::report_node_failure(Uint32 id){ + NdbNodeBitmask::set(m_failed_db_nodes, id); + if(!NdbNodeBitmask::get(m_db_nodes, id)) + { + return 0; + } + + /** + * Arrived + * TCKEYCONF TRANSIDAI + * 1) - - + * 2) - X + * 3) X - + * 4) X X + */ + NdbOperation* tmp = theFirstExecOpInList; + const Uint32 len = TcKeyConf::SimpleReadBit | id; + Uint32 tNoComp = theNoOfOpCompleted; + Uint32 tNoSent = theNoOfOpSent; + Uint32 count = 0; + while(tmp != 0) + { + if(tmp->theReceiver.m_expected_result_length == len && + tmp->theReceiver.m_received_result_length == 0) + { + count++; + tmp->theError.code = 4119; + } + tmp = tmp->next(); + } + tNoComp += count; + theNoOfOpCompleted = tNoComp; + if(count) + { + theReturnStatus = NdbTransaction::ReturnFailure; + if(tNoComp == tNoSent) + { + theError.code = 4119; + theCompletionStatus = NdbTransaction::CompletedFailure; + return 1; + } + } + return 0; +} |