/* 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 */ /***************************************************************************** * Name: NdbIndexOperation.cpp * Include: * Link: * Author: UABMASD Martin Sköld INN/V Alzato * Date: 2002-04-01 * Version: 0.1 * Description: Secondary index support * Documentation: * Adjust: 2002-04-01 UABMASD First version. ****************************************************************************/ #include #include #include #include #include "NdbApiSignal.hpp" #include #include #include #include #include #define CHECK_NULL(v) assert(v == NULL); v = NULL; NdbIndexOperation::NdbIndexOperation(Ndb* aNdb) : NdbOperation(aNdb), m_theIndex(NULL), m_theIndexLen(0), m_theNoOfIndexDefined(0) { m_tcReqGSN = GSN_TCINDXREQ; m_attrInfoGSN = GSN_INDXATTRINFO; m_keyInfoGSN = GSN_INDXKEYINFO; /** * Change receiver type */ theReceiver.init(NdbReceiver::NDB_INDEX_OPERATION, this); } NdbIndexOperation::~NdbIndexOperation() { } /***************************************************************************** * int indxInit(); * * Return Value: Return 0 : init was successful. * Return -1: In all other case. * Remark: Initiates operation record after allocation. *****************************************************************************/ int NdbIndexOperation::indxInit(NdbIndexImpl * anIndex, NdbTableImpl * aTable, NdbConnection* myConnection) { NdbOperation::init(aTable, myConnection); switch (anIndex->m_type) { case(NdbDictionary::Index::UniqueHashIndex): break; case(NdbDictionary::Index::Undefined): case(NdbDictionary::Index::HashIndex): case(NdbDictionary::Index::UniqueOrderedIndex): case(NdbDictionary::Index::OrderedIndex): setErrorCodeAbort(4003); return -1; } m_theIndex = anIndex; m_accessTable = anIndex->m_table; m_theIndexLen = 0; m_theNoOfIndexDefined = 0; for (Uint32 i=0; igetDataPtrSend()); tcIndxReq->scanInfo = 0; theKEYINFOptr = &tcIndxReq->keyInfo[0]; theATTRINFOptr = &tcIndxReq->attrInfo[0]; return 0; } int NdbIndexOperation::readTuple() { // First check that index is unique return NdbOperation::readTuple(); } int NdbIndexOperation::readTupleExclusive() { // First check that index is unique return NdbOperation::readTupleExclusive(); } int NdbIndexOperation::simpleRead() { // First check that index is unique return NdbOperation::simpleRead(); } int NdbIndexOperation::dirtyRead() { // First check that index is unique return NdbOperation::dirtyRead(); } int NdbIndexOperation::committedRead() { // First check that index is unique return NdbOperation::committedRead(); } int NdbIndexOperation::updateTuple() { // First check that index is unique return NdbOperation::updateTuple(); } int NdbIndexOperation::deleteTuple() { // First check that index is unique return NdbOperation::deleteTuple(); } int NdbIndexOperation::dirtyUpdate() { // First check that index is unique return NdbOperation::dirtyUpdate(); } int NdbIndexOperation::interpretedUpdateTuple() { // First check that index is unique return NdbOperation::interpretedUpdateTuple(); } int NdbIndexOperation::interpretedDeleteTuple() { // First check that index is unique return NdbOperation::interpretedDeleteTuple(); } int NdbIndexOperation::equal_impl(const NdbColumnImpl* tAttrInfo, const char* aValuePassed, Uint32 aVariableKeyLen) { register Uint32 tAttrId; Uint32 tData; Uint32 tKeyInfoPosition; const char* aValue = aValuePassed; Uint32 tempData[1024]; if ((theStatus == OperationDefined) && (aValue != NULL) && (tAttrInfo != NULL )) { /************************************************************************ * Start by checking that the attribute is an index key. * This value is also the word order in the tuple key of this * tuple key attribute. * Then check that this tuple key has not already been defined. * Finally check if all tuple key attributes have been defined. If * this is true then set Operation state to tuple key defined. ************************************************************************/ tAttrId = tAttrInfo->m_attrId; tKeyInfoPosition = tAttrInfo->m_keyInfoPos; Uint32 i = 0; // Check that the attribute is part if the index attributes // by checking if it is a primary key attribute of index table if (tAttrInfo->m_pk) { Uint32 tKeyDefined = theTupleKeyDefined[0][2]; Uint32 tKeyAttrId = theTupleKeyDefined[0][0]; do { if (tKeyDefined == false) { goto keyEntryFound; } else { if (tKeyAttrId != tAttrId) { /****************************************************************** * We read the key defined variable in advance. * It could potentially read outside its area when * i = MAXNROFTUPLEKEY - 1, * it is not a problem as long as the variable * theTupleKeyDefined is defined * in the middle of the object. * Reading wrong data and not using it causes no problems. *****************************************************************/ i++; tKeyAttrId = theTupleKeyDefined[i][0]; tKeyDefined = theTupleKeyDefined[i][2]; continue; } else { goto equal_error2; }//if }//if } while (i < MAXNROFTUPLEKEY); goto equal_error2; } else { goto equal_error1; } /************************************************************************** * Now it is time to retrieve the tuple key data from the pointer supplied * by the application. * We have to retrieve the size of the attribute in words and bits. *************************************************************************/ keyEntryFound: m_theIndexDefined[i][0] = tAttrId; m_theIndexDefined[i][1] = tKeyInfoPosition; m_theIndexDefined[i][2] = true; Uint32 sizeInBytes = tAttrInfo->m_attrSize * tAttrInfo->m_arraySize; Uint32 bitsInLastWord = 8 * (sizeInBytes & 3) ; Uint32 totalSizeInWords = (sizeInBytes + 3)/4;// Inc. bits in last word Uint32 sizeInWords = sizeInBytes / 4; // Exc. bits in last word if (true){ //tArraySize != 0) { Uint32 tIndexLen = m_theIndexLen; m_theIndexLen = tIndexLen + totalSizeInWords; if ((aVariableKeyLen == sizeInBytes) || (aVariableKeyLen == 0)) { ; } else { goto equal_error3; } } #if 0 else { /************************************************************************ * The attribute is a variable array. We need to use the length parameter * to know the size of this attribute in the key information and * variable area. A key is however not allowed to be larger than 4 * kBytes and this is checked for variable array attributes * used as keys. ***********************************************************************/ Uint32 tMaxVariableKeyLenInWord = (MAXTUPLEKEYLENOFATTERIBUTEINWORD - tKeyInfoPosition); tAttrSizeInBits = aVariableKeyLen << 3; tAttrSizeInWords = tAttrSizeInBits >> 5; tAttrBitsInLastWord = tAttrSizeInBits - (tAttrSizeInWords << 5); tAttrLenInWords = ((tAttrSizeInBits + 31) >> 5); if (tAttrLenInWords > tMaxVariableKeyLenInWord) { setErrorCodeAbort(4207); return -1; }//if m_theIndexLen = m_theIndexLen + tAttrLenInWords; }//if #endif /************************************************************************* * Check if the pointer of the value passed is aligned on a 4 byte * boundary. If so only assign the pointer to the internal variable * aValue. If it is not aligned then we start by copying the value to * tempData and use this as aValue instead. *************************************************************************/ const int attributeSize = sizeInBytes; const int slack = sizeInBytes & 3; int tDistrKey = tAttrInfo->m_distributionKey; int tDistrGroup = tAttrInfo->m_distributionGroup; if ((((UintPtr)aValue & 3) != 0) || (slack != 0)){ memcpy(&tempData[0], aValue, attributeSize); aValue = (char*)&tempData[0]; if(slack != 0) { char * tmp = (char*)&tempData[0]; memset(&tmp[attributeSize], 0, (4 - slack)); }//if }//if OperationType tOpType = theOperationType; if ((tDistrKey != 1) && (tDistrGroup != 1)) { ; } else if (tDistrKey == 1) { theDistrKeySize += totalSizeInWords; theDistrKeyIndicator = 1; } else { Uint32 TsizeInBytes = sizeInBytes; Uint32 TbyteOrderFix = 0; char* TcharByteOrderFix = (char*)&TbyteOrderFix; if (tAttrInfo->m_distributionGroupBits == 8) { char tFirstChar = aValue[TsizeInBytes - 2]; char tSecondChar = aValue[TsizeInBytes - 2]; TcharByteOrderFix[0] = tFirstChar; TcharByteOrderFix[1] = tSecondChar; TcharByteOrderFix[2] = 0x30; TcharByteOrderFix[3] = 0x30; theDistrGroupType = 0; } else { TbyteOrderFix = ((aValue[TsizeInBytes - 2] - 0x30) * 10) + (aValue[TsizeInBytes - 1] - 0x30); theDistrGroupType = 1; }//if theDistributionGroup = TbyteOrderFix; theDistrGroupIndicator = 1; }//if /************************************************************************** * If the operation is an insert request and the attribute is stored then * we also set the value in the stored part through putting the * information in the INDXATTRINFO signals. *************************************************************************/ if ((tOpType == InsertRequest) || (tOpType == WriteRequest)) { if (!tAttrInfo->m_indexOnly){ Uint32 ahValue; Uint32 sz = totalSizeInWords; AttributeHeader::init(&ahValue, tAttrId, sz); insertATTRINFO( ahValue ); insertATTRINFOloop((Uint32*)aValue, sizeInWords); if (bitsInLastWord != 0) { tData = *(Uint32*)(aValue + (sizeInWords << 2)); tData = convertEndian(tData); tData = tData & ((1 << bitsInLastWord) - 1); tData = convertEndian(tData); insertATTRINFO( tData ); }//if }//if }//if /************************************************************************** * Store the Key information in the TCINDXREQ and INDXKEYINFO signals. *************************************************************************/ if (insertKEYINFO(aValue, tKeyInfoPosition, totalSizeInWords, bitsInLastWord) != -1) { /************************************************************************ * Add one to number of tuple key attributes defined. * If all have been defined then set the operation state to indicate * that tuple key is defined. * Thereby no more search conditions are allowed in this version. ***********************************************************************/ Uint32 tNoIndexDef = m_theNoOfIndexDefined; Uint32 tErrorLine = theErrorLine; int tNoIndexAttrs = m_theIndex->m_columns.size(); unsigned char tInterpretInd = theInterpretIndicator; tNoIndexDef++; m_theNoOfIndexDefined = tNoIndexDef; tErrorLine++; theErrorLine = tErrorLine; if (int(tNoIndexDef) == tNoIndexAttrs) { if (tOpType == UpdateRequest) { if (tInterpretInd == 1) { theStatus = GetValue; } else { theStatus = SetValue; }//if return 0; } else if ((tOpType == ReadRequest) || (tOpType == DeleteRequest) || (tOpType == ReadExclusive)) { theStatus = GetValue; return 0; } else if ((tOpType == InsertRequest) || (tOpType == WriteRequest)) { theStatus = SetValue; return 0; } else { setErrorCodeAbort(4005); return -1; }//if }//if return 0; } else { return -1; }//if } else { if (theStatus != OperationDefined) { return -1; }//if if (aValue == NULL) { setErrorCodeAbort(4505); return -1; }//if if ( tAttrInfo == NULL ) { setErrorCodeAbort(4004); return -1; }//if }//if return -1; equal_error1: setErrorCodeAbort(4205); return -1; equal_error2: setErrorCodeAbort(4206); return -1; equal_error3: setErrorCodeAbort(4209); return -1; } int NdbIndexOperation::executeCursor(int aProcessorId) { printf("NdbIndexOperation::executeCursor NYI\n"); // NYI return -1; } void NdbIndexOperation::setLastFlag(NdbApiSignal* signal, Uint32 lastFlag) { TcIndxReq * const req = CAST_PTR(TcIndxReq, signal->getDataPtrSend()); TcKeyReq::setExecuteFlag(req->requestInfo, lastFlag); } int NdbIndexOperation::prepareSend(Uint32 aTC_ConnectPtr, Uint64 aTransactionId) { Uint32 tTransId1, tTransId2; Uint32 tReqInfo; Uint32 tSignalCount = 0; Uint32 tInterpretInd = theInterpretIndicator; theErrorLine = 0; if (tInterpretInd != 1) { OperationType tOpType = theOperationType; OperationStatus tStatus = theStatus; if ((tOpType == UpdateRequest) || (tOpType == InsertRequest) || (tOpType == WriteRequest)) { if (tStatus != SetValue) { setErrorCodeAbort(4506); return -1; }//if } else if ((tOpType == ReadRequest) || (tOpType == ReadExclusive) || (tOpType == DeleteRequest)) { if (tStatus != GetValue) { setErrorCodeAbort(4506); return -1; }//if } else { setErrorCodeAbort(4507); return -1; }//if } else { if (prepareSendInterpreted() == -1) { return -1; }//if }//if //------------------------------------------------------------- // We start by filling in the first 8 unconditional words of the // TCINDXREQ signal. //------------------------------------------------------------- TcIndxReq * const tcIndxReq = CAST_PTR(TcIndxReq, theTCREQ->getDataPtrSend()); Uint32 tTotalCurrAI_Len = theTotalCurrAI_Len; Uint32 tIndexId = m_theIndex->m_indexId; Uint32 tSchemaVersion = m_theIndex->m_version; tcIndxReq->apiConnectPtr = aTC_ConnectPtr; tcIndxReq->senderData = ptr2int(); tcIndxReq->attrLen = tTotalCurrAI_Len; tcIndxReq->indexId = tIndexId; tcIndxReq->indexSchemaVersion = tSchemaVersion; tTransId1 = (Uint32) aTransactionId; tTransId2 = (Uint32) (aTransactionId >> 32); //------------------------------------------------------------- // Simple is simple if simple or both start and commit is set. //------------------------------------------------------------- // Temporarily disable simple stuff Uint8 tSimpleIndicator = 0; // Uint8 tSimpleIndicator = theSimpleIndicator; Uint8 tCommitIndicator = theCommitIndicator; Uint8 tStartIndicator = theStartIndicator; // if ((theNdbCon->theLastOpInList == this) && (theCommitIndicator == 0)) // abort(); // Temporarily disable simple stuff Uint8 tSimpleAlt = 0; // Uint8 tSimpleAlt = tStartIndicator & tCommitIndicator; tSimpleIndicator = tSimpleIndicator | tSimpleAlt; //------------------------------------------------------------- // Simple state is set if start and commit is set and it is // a read request. Otherwise it is set to zero. //------------------------------------------------------------- Uint8 tReadInd = (theOperationType == ReadRequest); Uint8 tSimpleState = tReadInd & tSimpleAlt; theNdbCon->theSimpleState = tSimpleState; tcIndxReq->transId1 = tTransId1; tcIndxReq->transId2 = tTransId2; tReqInfo = 0; if (tTotalCurrAI_Len <= TcIndxReq::MaxAttrInfo) { tcIndxReq->setAIInTcIndxReq(tReqInfo, tTotalCurrAI_Len); } else { tcIndxReq->setAIInTcIndxReq(tReqInfo, TcIndxReq::MaxAttrInfo); }//if tcIndxReq->setSimpleFlag(tReqInfo, tSimpleIndicator); tcIndxReq->setCommitFlag(tReqInfo, tCommitIndicator); tcIndxReq->setStartFlag(tReqInfo, tStartIndicator); const Uint8 tInterpretIndicator = theInterpretIndicator; tcIndxReq->setInterpretedFlag(tReqInfo, tInterpretIndicator); Uint8 tDirtyIndicator = theDirtyIndicator; OperationType tOperationType = theOperationType; Uint32 tIndexLen = m_theIndexLen; Uint8 abortOption = theNdbCon->m_abortOption; tcIndxReq->setDirtyFlag(tReqInfo, tDirtyIndicator); tcIndxReq->setOperationType(tReqInfo, tOperationType); tcIndxReq->setIndexLength(tReqInfo, tIndexLen); tcIndxReq->setCommitType(tReqInfo, abortOption); Uint8 tDistrKeyIndicator = theDistrKeyIndicator; Uint8 tDistrGroupIndicator = theDistrGroupIndicator; Uint8 tDistrGroupType = theDistrGroupType; Uint8 tScanIndicator = theScanInfo & 1; tcIndxReq->setDistributionGroupFlag(tReqInfo, tDistrGroupIndicator); tcIndxReq->setDistributionGroupTypeFlag(tReqInfo, tDistrGroupType); tcIndxReq->setDistributionKeyFlag(tReqInfo, tDistrKeyIndicator); tcIndxReq->setScanIndFlag(tReqInfo, tScanIndicator); tcIndxReq->requestInfo = tReqInfo; //------------------------------------------------------------- // The next step is to fill in the upto three conditional words. //------------------------------------------------------------- Uint32* tOptionalDataPtr = &tcIndxReq->scanInfo; Uint32 tDistrGHIndex = tScanIndicator; Uint32 tDistrKeyIndex = tDistrGHIndex + tDistrGroupIndicator; Uint32 tScanInfo = theScanInfo; Uint32 tDistributionGroup = theDistributionGroup; Uint32 tDistrKeySize = theDistrKeySize; tOptionalDataPtr[0] = tScanInfo; tOptionalDataPtr[tDistrGHIndex] = tDistributionGroup; tOptionalDataPtr[tDistrKeyIndex] = tDistrKeySize; //------------------------------------------------------------- // The next is step is to compress the key data part of the // TCKEYREQ signal. //------------------------------------------------------------- Uint32 tKeyIndex = tDistrKeyIndex + tDistrKeyIndicator; Uint32* tKeyDataPtr = &tOptionalDataPtr[tKeyIndex]; Uint32 Tdata1 = tcIndxReq->keyInfo[0]; Uint32 Tdata2 = tcIndxReq->keyInfo[1]; Uint32 Tdata3 = tcIndxReq->keyInfo[2]; Uint32 Tdata4 = tcIndxReq->keyInfo[3]; Uint32 Tdata5; tKeyDataPtr[0] = Tdata1; tKeyDataPtr[1] = Tdata2; tKeyDataPtr[2] = Tdata3; tKeyDataPtr[3] = Tdata4; if (tIndexLen > 4) { Tdata1 = tcIndxReq->keyInfo[4]; Tdata2 = tcIndxReq->keyInfo[5]; Tdata3 = tcIndxReq->keyInfo[6]; Tdata4 = tcIndxReq->keyInfo[7]; tKeyDataPtr[4] = Tdata1; tKeyDataPtr[5] = Tdata2; tKeyDataPtr[6] = Tdata3; tKeyDataPtr[7] = Tdata4; }//if //------------------------------------------------------------- // Finally we also compress the INDXATTRINFO part of the signal. // We optimise by using the if-statement for sending INDXKEYINFO // signals to calculating the new Attrinfo Index. //------------------------------------------------------------- Uint32 tAttrInfoIndex; if (tIndexLen > TcIndxReq::MaxKeyInfo) { /** * Set transid and TC connect ptr in the INDXKEYINFO signals */ NdbApiSignal* tSignal = theFirstKEYINFO; Uint32 remainingKey = tIndexLen - TcIndxReq::MaxKeyInfo; do { Uint32* tSigDataPtr = tSignal->getDataPtrSend(); NdbApiSignal* tnextSignal = tSignal->next(); tSignalCount++; tSigDataPtr[0] = aTC_ConnectPtr; tSigDataPtr[1] = tTransId1; tSigDataPtr[2] = tTransId2; if (remainingKey > IndxKeyInfo::DataLength) { // The signal is full tSignal->setLength(IndxKeyInfo::MaxSignalLength); remainingKey -= IndxKeyInfo::DataLength; } else { // Last signal tSignal->setLength(IndxKeyInfo::HeaderLength + remainingKey); remainingKey = 0; } tSignal = tnextSignal; } while (tSignal != NULL); tAttrInfoIndex = tKeyIndex + TcIndxReq::MaxKeyInfo; } else { tAttrInfoIndex = tKeyIndex + tIndexLen; }//if //------------------------------------------------------------- // Perform the Attrinfo packing in the TCKEYREQ signal started // above. //------------------------------------------------------------- Uint32* tAIDataPtr = &tOptionalDataPtr[tAttrInfoIndex]; Tdata1 = tcIndxReq->attrInfo[0]; Tdata2 = tcIndxReq->attrInfo[1]; Tdata3 = tcIndxReq->attrInfo[2]; Tdata4 = tcIndxReq->attrInfo[3]; Tdata5 = tcIndxReq->attrInfo[4]; theTCREQ->setLength(tcIndxReq->getAIInTcIndxReq(tReqInfo) + tAttrInfoIndex + TcIndxReq::StaticLength); tAIDataPtr[0] = Tdata1; tAIDataPtr[1] = Tdata2; tAIDataPtr[2] = Tdata3; tAIDataPtr[3] = Tdata4; tAIDataPtr[4] = Tdata5; /*************************************************** * Send the INDXATTRINFO signals. ***************************************************/ if (tTotalCurrAI_Len > 5) { // Set the last signal's length. NdbApiSignal* tSignal = theFirstATTRINFO; theCurrentATTRINFO->setLength(theAI_LenInCurrAI); do { Uint32* tSigDataPtr = tSignal->getDataPtrSend(); NdbApiSignal* tnextSignal = tSignal->next(); tSignalCount++; tSigDataPtr[0] = aTC_ConnectPtr; tSigDataPtr[1] = tTransId1; tSigDataPtr[2] = tTransId2; tSignal = tnextSignal; } while (tSignal != NULL); }//if NdbRecAttr* tRecAttrObject = theFirstRecAttr; theStatus = WaitResponse; theCurrentRecAttr = tRecAttrObject; return 0; } void NdbIndexOperation::closeScan() { printf("NdbIndexOperation::closeScan NYI\n"); } /*************************************************************************** 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 TCKEYREF signal. ***************************************************************************/ int NdbIndexOperation::receiveTCINDXREF( NdbApiSignal* aSignal) { const TcIndxRef * const tcIndxRef = CAST_CONSTPTR(TcIndxRef, aSignal->getDataPtr()); if (checkState_TransId(aSignal) == -1) { return -1; }//if theStatus = Finished; theNdbCon->theReturnStatus = ReturnFailure; //--------------------------------------------------------------------------// // If the transaction this operation belongs to consists only of simple reads // we set the error code on the transaction object. // If the transaction consists of other types of operations we set // the error code only on the operation since the simple read is not really // part of this transaction and we can not decide the status of the whole // transaction based on this operation. //--------------------------------------------------------------------------// Uint32 errorCode = tcIndxRef->errorCode; if (theNdbCon->theSimpleState == 0) { theError.code = errorCode; theNdbCon->setOperationErrorCodeAbort(errorCode); return theNdbCon->OpCompleteFailure(); } else { theError.code = errorCode; return theNdbCon->OpCompleteSuccess(); } }//NdbIndexOperation::receiveTCINDXREF()