mirror of
https://github.com/MariaDB/server.git
synced 2025-09-03 20:43:11 +03:00
2069 lines
67 KiB
C++
2069 lines
67 KiB
C++
/* 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);
|
|
NdbTableImpl* table = theNdb->theDictionary->getTable(aTableName);
|
|
|
|
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(theNdb->theError.code);
|
|
return NULL;
|
|
}//if
|
|
}
|
|
|
|
setOperationErrorCodeAbort(4114);
|
|
return NULL;
|
|
}//NdbTransaction::getNdbIndexScanOperation()
|
|
|
|
NdbIndexScanOperation*
|
|
NdbTransaction::getNdbIndexScanOperation(const NdbDictionary::Index * index,
|
|
const NdbDictionary::Table * table)
|
|
{
|
|
if (index && table)
|
|
return getNdbIndexScanOperation(& NdbIndexImpl::getImpl(*index),
|
|
& NdbTableImpl::getImpl(*table));
|
|
else
|
|
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->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;
|
|
}
|
|
|
|
// table == 0
|
|
setOperationErrorCodeAbort(theNdb->theError.code);
|
|
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,
|
|
const NdbDictionary::Table * table)
|
|
{
|
|
if (index && table)
|
|
return getNdbIndexOperation(& NdbIndexImpl::getImpl(*index),
|
|
& NdbTableImpl::getImpl(*table));
|
|
else
|
|
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)
|
|
{
|
|
if (theStatus != Connecting)
|
|
{
|
|
return -1;
|
|
} else
|
|
{
|
|
theStatus = ConnectFailure;
|
|
theNdb->theError.code = aSignal->readData(2);
|
|
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;
|
|
}
|