/* xbixmdx.cpp XBase64 Software Library Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. Email Contact: XDB-devel@lists.sourceforge.net XDB-users@lists.sourceforge.net MDX indices are comprised of blocks and pages. A page is 512 bytes. A Block is one or more pages. The default block size is two 512 byte pages per block. Nodes are used for storing block images in memory */ #include "xbase.h" #ifdef XB_MDX_SUPPORT namespace xb{ /***********************************************************************/ xbIxMdx::xbIxMdx( xbDbf *dbf ) : xbIx( dbf ){ Init(); } /***********************************************************************/ //void xbIxMdx::Init( xbInt16 iOpt ){ void xbIxMdx::Init( xbInt16 ){ cVersion = 0; cCreateYY = 0; cCreateMM = 0; cCreateDD = 0; sFileName = ""; iBlockFactor = 0; cProdIxFlag = 0; cTagEntryCnt = 0; iTagLen = 0; iTagUseCnt = 0; cNextTag = 0; c1B = 0x0b; ulPageCnt = 0; ulFirstFreePage = 0; ulNoOfBlockAvail = 0; cUpdateYY = 0; cUpdateMM = 0; cUpdateDD = 0; mdxTagTbl = NULL; cNodeBuf = NULL; bReuseEmptyNodes = xbTrue; } /***********************************************************************/ xbIxMdx::~xbIxMdx(){ if( cNodeBuf ) free( cNodeBuf ); if( FileIsOpen()) Close(); } /***********************************************************************/ //! @brief Add key. /*! Add key. If this is a unique index, this logic assumes the duplicate check logic was already done. \param vpTag Tag to update. \param ulRecNo Record number to add key for. \returns Return Codes */ xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ xbMdxTag * npTag = (xbMdxTag *) vpTag; if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts ) return XB_NO_ERROR; xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbInt16 iHeadNodeUpdateOpt = 2; xbIxNode * npRightNode = NULL; xbUInt32 ulNewRightChild = 0; try{ if(( iRc = xbIxMdx::KeySetPosAdd( npTag, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode ); if( lKeyCnt < npTag->iKeysPerBlock ){ // Section A - add key to appropriate position if space available // std::cout << "AddKey Section A begin\n"; if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } else { // land here with a full leaf node iHeadNodeUpdateOpt = 1; npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ); if( !npRightNode ){ iErrorStop = 120; iRc = XB_NO_MEMORY; throw iRc; } if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRightChild ){ ulNewRightChild = npRightNode->ulBlockNo * (xbUInt32) iBlockFactor; } if(( iRc = xbIxMdx::SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } xbUInt32 ulTempBlockNo = npRightNode->ulBlockNo; // section C - go up the tree, splitting nodes as necessary xbIxNode * npParent = npTag->npCurNode->npPrev; while( npParent && GetKeyCount( npParent ) >= npTag->iKeysPerBlock ){ //std::cout << "Section C begin interior node is full\n"; npRightNode = FreeNodeChain( npRightNode ); npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npParent->ulBlockNo * (xbUInt32) iBlockFactor ); //std::cout << "Section C - B new right node block number for interior node split= " << npRightNode->ulBlockNo << "\n"; if( !npRightNode ){ iErrorStop = 140; iRc = XB_NO_MEMORY; throw iRc; } //std::cout << "Section C - going to split interior node C\n"; if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, BlockToPage( ulTempBlockNo ))) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } // std::cout << "Section C - interior node split \n"; ulTempBlockNo = npRightNode->ulBlockNo; npTag->npCurNode = npParent; npParent = npParent->npPrev; } // section D - if cur node is split root, create new root if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRootPage ){ if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } if( npRightNode ) npRightNode = FreeNodeChain( npRightNode ); } else { // std::cout << "Section E, put key in parent\n"; if(( iRc = InsertNodeI( (void *) vpTag, (xbIxNode *) npParent, (xbInt16) npParent->iCurKeyNo, BlockToPage( npRightNode->ulBlockNo ))) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } } } // update the header if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){ iErrorStop = 180; throw iRc; } // if adding the first key, set the cHasKeys field if( !npTag->cHasKeys ){ npTag->cHasKeys = 0x01; if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 190; throw iRc; } if(( iRc = xbFwrite( &npTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } } if( ulNewRightChild > 0 ){ char cBuf[4]; ePutUInt32( cBuf, ulNewRightChild ); if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 252), SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 210; throw iRc; } if(( iRc = xbFwrite( &cBuf, 4, 1 )) != XB_NO_ERROR ){ iErrorStop = 220; throw iRc; } npTag->ulRightChild = ulNewRightChild; } if( npRightNode ) npRightNode = FreeNodeChain( npRightNode ); npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); npTag->npCurNode = NULL; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::AddKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ void xbIxMdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){ xbMdxTag * mdxTag = (xbMdxTag *) vpTag; if( mdxTag->npNodeChain == NULL ){ mdxTag->npNodeChain = npNode; mdxTag->npCurNode = npNode; } else { npNode->npPrev = mdxTag->npCurNode; mdxTag->npCurNode->npNext = npNode; mdxTag->npCurNode = npNode; } // time stamp the node chain GetFileMtime( mdxTag->tNodeChainTs ); } /***********************************************************************/ //! @brief Add new root node. /*! \param mpTag Tag to update. \param npLeft Left node. \param npRight Right node. \returns Return Codes */ xbInt16 xbIxMdx::AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char *pLastKey = NULL; try{ xbIxNode *npRoot = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npRight->ulBlockNo * (xbUInt32) iBlockFactor ); if( !npRoot ){ iErrorStop = 100; iRc = XB_NO_MEMORY; throw iRc; } npTag->ulRootPage = npRoot->ulBlockNo; pLastKey = (char *) malloc( (size_t) npTag->iKeyLen ); if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } char * pTrg = npRoot->cpBlockData; // set no of keys to 1 ePutUInt32( pTrg, 1 ); // set the left node number pTrg += 8; ePutUInt32( pTrg, npLeft->ulBlockNo * (xbUInt32) iBlockFactor ); // set the key pTrg+= 4; memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen ); pTrg+= npTag->iKeyItemLen - 4; ePutUInt32( pTrg, npRight->ulBlockNo * (xbUInt32) iBlockFactor ); // write out the new block if(( iRc = WriteBlock( npRoot->ulBlockNo, GetBlockSize(), npRoot->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } // write out the new root node number in the tag header // position the file xbUInt32 ulPagePos = npTag->ulTagHdrPageNo * 512; // save the number to a buffer char cBuf[4]; ePutUInt32( cBuf, npRoot->ulBlockNo * ((xbUInt32) iBlockFactor )); if(( iRc = xbFseek( ulPagePos, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if(( iRc = xbFwrite( &cBuf, 4, 1 )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } if( pLastKey ) free( pLastKey ); npRoot = FreeNodeChain( npRoot ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::AddKeyNewRoot() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( pLastKey ) free( pLastKey ); } return iRc; } /***********************************************************************/ //! @brief Allocate a node. /*! \param mpTag Pointer to mdx tag \param ulBufSize Buffer size. \param ulBlock2 Value to load in ulBlock2 field, bytes 4-7 in the first page of the block \returns Pointer to new node. */ xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbIxNode *n = NULL; try{ if(( n = xbIx::AllocateIxNode( ulBufSize )) == NULL ){ iRc = XB_NO_MEMORY; iErrorStop = 100; throw iRc; } char *p = n->cpBlockData; p += 4; if( ulFirstFreePage > 0 && bReuseEmptyNodes ){ // have an empty node we can reuse n->ulBlockNo = PageToBlock( ulFirstFreePage ); if(( iRc = ReadBlock( n->ulBlockNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){ iRc = 110; throw iRc; } // update ulFirstFreePage ulFirstFreePage = eGetUInt32( p ); if(( iRc = xbFseek( 36, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if(( iRc = xbFwrite( p, 4, 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } // memset cpBlockData to zeroes memset( n->cpBlockData, 0x00, GetBlockSize()); } else { n->ulBlockNo = ulPageCnt / (xbUInt32) iBlockFactor; ulPageCnt += (xbUInt32) iBlockFactor; } mpTag->ulTagSize += (xbUInt32) iBlockFactor; if( ulBlock2 > 0 ){ ePutUInt32( p, ulBlock2 ); } } catch( xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::AllocateIxNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( n ) n = FreeNodeChain( n ); } return n; } /***********************************************************************/ //! @brief Calculate B-tree pointers. /*! Set binary tree pointer value. The MDX tags are stored with binary tree positions. This routine calculates the value in memory. \returns void */ void xbIxMdx::CalcBtreePointers(){ xbInt16 iaLeftChild[48]; xbInt16 iaRightChild[48]; xbInt16 iaParent[48]; for( xbInt16 i = 0; i < 48; i++ ){ iaLeftChild[i] = 0; iaRightChild[i] = 0; iaParent[i] = 0; } // anything to do? if( iTagUseCnt > 1 ){ xbString sBaseTag; xbString sThisTag; xbString sWorkTag; xbInt16 iWorkTagNo; xbBool bDone; sBaseTag = GetTagName( GetTag( 0 )); for( xbInt16 iThisTagNo = 1; iThisTagNo < iTagUseCnt; iThisTagNo++ ){ iWorkTagNo = 0; sWorkTag.Set( sBaseTag ); sThisTag = GetTagName( GetTag( iThisTagNo )); bDone = xbFalse; while( !bDone ){ if( sThisTag < sWorkTag ){ if( iaLeftChild[iWorkTagNo] == 0 ) { iaLeftChild[iWorkTagNo] = iThisTagNo + 1; iaParent[iThisTagNo] = iWorkTagNo + 1; bDone = xbTrue; } else { iWorkTagNo = iaLeftChild[iWorkTagNo]-1; sWorkTag = GetTagName( GetTag( iWorkTagNo)); } } else { if( iaRightChild[iWorkTagNo] == 0 ) { iaRightChild[iWorkTagNo] = iThisTagNo + 1; iaParent[iThisTagNo] = iWorkTagNo + 1; bDone = xbTrue; } else { iWorkTagNo = iaRightChild[iWorkTagNo]-1; sWorkTag = GetTagName( GetTag( iWorkTagNo )); } } } } } xbString s; xbMdxTag *mpTag = mdxTagTbl; for( xbInt16 i = 0; i < iTagUseCnt; i++ ){ mpTag->cLeftChild = (char ) iaLeftChild[i]; mpTag->cRightChild = (char ) iaRightChild[i]; mpTag->cParent = (char ) iaParent[i]; mpTag = mpTag->next; } } /**************************************************************************************************************/ //! @brief Calculate the page number for a given block /*! This routine is called by any function needing to calculate the page number for a given block. Page numbers are stored internally in the physical file, and the library reads and writes in blocks of one or more pages. Assumes valid data input \param ulBlockNo Block Number \returns Calculated page number. */ inline xbUInt32 xbIxMdx::BlockToPage( xbUInt32 ulBlockNo ){ return ulBlockNo * (xbUInt32) iBlockFactor; } /***********************************************************************/ char xbIxMdx::CalcTagKeyFmt( xbExp &exp ){ xbExpNode *n = exp.GetTreeHandle(); if( n->GetChildCnt() == 0 && n->GetNodeType() == XB_EXP_FIELD ) return 0x01; else return 0; } /***********************************************************************/ //! @brief Check for duplicate key. /*! \param vpTag Tag to check. \returns XB_KEY_NOT_UNIQUE
XB_NO_ERROR */ xbInt16 xbIxMdx::CheckForDupKey( void *vpTag ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag *mpTag = (xbMdxTag *) vpTag; mpTag->bFoundSts = xbFalse; try{ if( GetUnique( mpTag )){ if( mpTag->iKeySts == XB_ADD_KEY || mpTag->iKeySts == XB_UPD_KEY ) if( KeyExists( mpTag )){ if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){ mpTag->bFoundSts = xbTrue; return 0; } else { return XB_KEY_NOT_UNIQUE; } } } return 0; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Check tag integrity. /*! Check a tag for accuracy. \param vpTag Tag to create key for. \param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\returns Return Codes */ xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iRc2; xbInt16 iRc3; xbInt16 iErrorStop = 0; xbUInt32 ulIxCnt = 0; xbUInt32 ulThisRecNo = 0; xbUInt32 ulPrevRecNo = 0; xbBool bDone = false; xbString sMsg; char cKeyType; char *pPrevKeyBuf = NULL; xbMdxTag *npTag = (xbMdxTag *) vpTag; xbBool bDescending = npTag->cKeyFmt2 & 0x08; #ifdef XB_LOCKING_SUPPORT xbBool bLocked = xbFalse; #endif try{ #ifdef XB_LOCKING_SUPPORT if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } bLocked = xbTrue; } #endif memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyLen ); cKeyType = GetKeyType( vpTag ); pPrevKeyBuf = (char *) calloc( 1, (size_t) npTag->iKeyLen ); iRc = GetFirstKey( vpTag, 0 ); memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); // for each key in the index, make sure it is trending in the right direction while( iRc == XB_NO_ERROR && !bDone ){ ulIxCnt++; iRc = GetNextKey( vpTag, 0 ); if( iRc == XB_NO_ERROR ){ // compare this key to prev key iRc2 = CompareKey( cKeyType, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), pPrevKeyBuf, (size_t) npTag->iKeyLen ); if(( iRc2 < 0 && !bDescending ) || ( iRc2 > 0 && bDescending )){ sMsg.Sprintf( "Key sequence error at key number [%ld].", ulIxCnt ); xbase->WriteLogMessage( sMsg, iOpt ); iErrorStop = 110; iRc = XB_INVALID_INDEX; throw iRc; } ulThisRecNo = 0; if(( iRc3 = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulThisRecNo )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc3; } if( iRc2 == 0 && (ulThisRecNo <= ulPrevRecNo )){ sMsg.Sprintf( "Dbf record number sequence error at key number [%ld].", iOpt ); xbase->WriteLogMessage( sMsg, iOpt ); iErrorStop = 130; iRc = XB_INVALID_INDEX; throw iRc; } // save this key info to prev key memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); ulPrevRecNo = ulThisRecNo; } } xbUInt32 ulDbfCnt = 0; if(( iRc = dbf->GetRecordCnt( ulDbfCnt )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } xbUInt32 ulFiltCnt = 0; xbBool bFiltered = xbTrue; // verify each record in the dbf file has a corresponding index entry xbUInt32 j = 0; while( j < ulDbfCnt ){ if(( iRc = dbf->GetRecord( ++j )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } if( npTag->cHasFilter ){ if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } if(( iRc = npTag->filter->GetBoolResult( bFiltered )) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } } if( bFiltered ){ if(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){ ulThisRecNo = j; iErrorStop = 180; throw iRc; } ulFiltCnt++; } } if((GetUniqueKeyOpt() == XB_EMULATE_DBASE) && (GetUnique( vpTag ))){ // can't compare counts if using XB_EMULATE_DBASE and it's a unique index } else { if( ulIxCnt != ulFiltCnt && GetUniqueKeyOpt() == XB_HALT_ON_DUPKEY ){ if( npTag->cHasFilter ) sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Filtered index entry count [%ld] does not match dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); else sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Index entry count [%ld] does not match dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); xbase->WriteLogMessage( sMsg, iOpt ); iErrorStop = 190; iRc = XB_INVALID_INDEX; throw iRc; } if( npTag->cHasFilter ) sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Filtered index entry count [%ld] matches dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); else sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName ); xbase->WriteLogMessage( sMsg, iOpt ); } if( pPrevKeyBuf ) free( pPrevKeyBuf ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d] Tag = [%s]", iErrorStop, iRc, npTag->cTagName ); xbase->WriteLogMessage( sMsg, iOpt ); xbase->WriteLogMessage( GetErrorMessage( iRc ), iOpt ); if( pPrevKeyBuf ) free( pPrevKeyBuf ); if( iErrorStop == 160 ){ sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo ); xbase->WriteLogMessage( sMsg, iOpt ); } } #ifdef XB_LOCKING_SUPPORT if( bLocked ){ dbf->LockTable( XB_UNLOCK ); } #endif return iRc; } /***********************************************************************/ xbMdxTag *xbIxMdx::ClearTagTable(){ // clear the list of tags xbMdxTag *tt = mdxTagTbl; xbMdxTag *tt2; while( tt ){ tt2 = tt; tt = tt->next; tt2->npNodeChain = FreeNodeChain( tt2->npNodeChain ); tt2->npCurNode = NULL; if( tt2->cpKeyBuf ) free( tt2->cpKeyBuf ); if( tt2->cpKeyBuf2 ) free( tt2->cpKeyBuf2 ); if( tt2->exp ) delete tt2->exp; if( tt2->filter ) delete tt2->filter; if( tt2->sKeyExp ) delete tt2->sKeyExp; if( tt2->sTagName ) delete tt2->sTagName; if( tt2->sFiltExp ) delete tt2->sFiltExp; free( tt2 ); } return NULL; } /***********************************************************************/ xbInt16 xbIxMdx::Close(){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ mdxTagTbl = ClearTagTable(); if(( iRc = xbIx::Close()) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Create key. /*! \param vpTag Tag \param iOpt 1 = Append, 2 = Update \returns XB_KEY_NOT_UNIQUE
XB_NO_ERROR iKeySts 0 - No Updates 1 - Add Key XB_ADD_KEY 2 - Update Key XB_UPD_KEY 3 - Delete Key XB_DEL_KEY bKeyFiltered xbFalse - Key filtered out xbTrue - Key filtered in cpKeyBuf - Key buffer for add cpKeyBuf2 - Key buffer for delete */ xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbBool bFilter0 = xbFalse; // filter against RecBuf, updated record buffer xbBool bFilter1 = xbFalse; // filter against recBuf2, original record buffer try{ xbMdxTag *npTag = (xbMdxTag *) vpTag; npTag->iKeySts = 0; // do tag filter logic if( npTag->cHasFilter ){ if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if(( iRc = npTag->filter->GetBoolResult( bFilter0 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } else { bFilter0 = xbTrue; } // if add request and filtered out, we're done if( iOpt == 1 && !bFilter0 ) return XB_NO_ERROR; if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if( npTag->exp->GetReturnType() == XB_EXP_CHAR ) npTag->exp->GetStringResult( npTag->cpKeyBuf, (xbUInt32) npTag->iKeyLen ); else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){ xbDouble d; npTag->exp->GetNumericResult( d ); xbBcd bcd( d ); bcd.ToChar( npTag->cpKeyBuf ); } else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ xbDouble d; npTag->exp->GetNumericResult( d ); memcpy( npTag->cpKeyBuf, &d, 8 ); } if( iOpt == 1 ) // Append npTag->iKeySts = XB_ADD_KEY; else if( iOpt == 2 ){ // Update if( npTag->cHasFilter ){ if(( iRc = npTag->filter->ProcessExpression( 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if(( iRc = npTag->filter->GetBoolResult( bFilter1 )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } } else { bFilter1 = xbTrue; } if(( iRc = npTag->exp->ProcessExpression( 1 )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){ npTag->exp->GetStringResult( npTag->cpKeyBuf2, (xbUInt32) npTag->iKeyLen ); } else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){ xbDouble d; npTag->exp->GetNumericResult( d ); xbBcd bcd( d ); bcd.ToChar( npTag->cpKeyBuf2 ); } else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ xbDouble d; npTag->exp->GetNumericResult( d ); memcpy( npTag->cpKeyBuf2, &d, 8 ); } if( bFilter1 ){ // original key was indexed if( bFilter0 ){ // new key s/b indexed, update it if changed if( memcmp( npTag->cpKeyBuf, npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){ npTag->iKeySts = XB_UPD_KEY; } } else { // original key indexed, new key not indexed, delete it npTag->iKeySts = XB_DEL_KEY; } } else { // original key not indexed if( bFilter0 ) npTag->iKeySts = XB_ADD_KEY; } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::CreateKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Create new tag. /*! This routine creates a new tag. When complete, sets the cur tag pointer to the newly created tag. \param sName Tag Name, including .MDX suffix \param sKey Key Expression \param sFilter Filter expression. \param iDescending \param iUnique xbtrue - Unique.
xbFalse - Not unique. \param iOverLay xbTrue - Overlay if file already exists.
xbFalse - Don't overlay. \param vpTag Output from method Pointer to vptag pointer. \returns Return Codes */ xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag *tte = NULL; try{ // verify room for new tag if( !( iTagUseCnt < 47 )){ iErrorStop = 100; iRc = XB_LIMIT_REACHED; throw iRc; } // verify valid tag name xbString sWorker = sName; sWorker.Trim(); if( sWorker.Len() > 10 ){ iErrorStop = 110; iRc = XB_INVALID_TAG; throw iRc; } // verify tag not already defined if( iTagUseCnt > 0 ){ if( GetTag( sWorker )){ iErrorStop = 120; iRc = XB_INVALID_TAG; throw iRc; } } // allocate a tag structure here if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){ iErrorStop = 130; iRc = XB_NO_MEMORY; throw iRc; } *vpTag = tte; tte->sTagName = new xbString( sWorker ); //set up the key expression sWorker = sFilter; sWorker.Trim(); if( sWorker.Len() > 0 ){ if( sWorker.Len() == 0 || sWorker.Len() > 220 ){ iRc = XB_INVALID_TAG; iErrorStop = 140; throw iRc; } tte->sFiltExp = new xbString( sWorker ); tte->filter = new xbExp( dbf->GetXbasePtr()); if(( iRc = tte->filter->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } if((tte->filter->GetReturnType()) != 'L' ){ iRc = XB_INVALID_TAG; iErrorStop = 160; throw iRc; } tte->cHasFilter = 0x01; } //set up the key expression sWorker = sKey; sWorker.Trim(); if( sWorker.Len() == 0 || sWorker.Len() > 100 ){ iRc = XB_INVALID_TAG; iErrorStop = 170; throw iRc; } tte->sKeyExp = new xbString( sWorker ); tte->exp = new xbExp( dbf->GetXbasePtr()); if(( iRc = tte->exp->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){ iErrorStop = 180; throw iRc; } xbDate d; d.Sysdate(); if( iTagUseCnt == 0 ){ // first tag, new mdx file // create the file name xbString sIxFileName = dbf->GetFqFileName(); sIxFileName.Trim(); xbUInt32 lLen = sIxFileName.Len(); sIxFileName.PutAt( lLen-2, 'M' ); sIxFileName.PutAt( lLen-1, 'D' ); sIxFileName.PutAt( lLen, 'X' ); // copy the file name to the class variable this->SetFileName( sIxFileName ); if( FileExists() && !iOverlay ){ iErrorStop = 190; iRc = XB_FILE_EXISTS; throw iRc; } // first tag, need to create the file if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } cVersion = 2; cCreateYY = (char) d.YearOf() - 1900; cCreateMM = (char) d.MonthOf(); cCreateDD = (char) d.DayOf( XB_FMT_MONTH ); GetFileNamePart( sFileName ); sFileName.ToUpperCase(); SetBlockSize( (xbUInt32) dbf->GetCreateMdxBlockSize()); iBlockFactor = GetBlockSize() / 512; cProdIxFlag = 1; cTagEntryCnt = 48; iTagLen = 32; ulPageCnt = 4; ulFirstFreePage = 0; ulNoOfBlockAvail = 0; cNextTag = 1; c1B = 0x1B; cUpdateYY = cCreateYY; cUpdateMM = cCreateMM; cUpdateDD = cCreateDD; if(( iRc = WriteHeadBlock( 0 )) != XB_NO_ERROR ){ iErrorStop = 210; throw iRc; } } // populate the tag table entry structure tte->ulTagHdrPageNo = ulPageCnt; ulPageCnt += (xbUInt32) iBlockFactor; tte->sTagName->strncpy( tte->cTagName, 10 ); // cKeyFmt is always 0x10; // tested 2+ZIPCD CITY+STATE or just standalone field - always 0x10 tte->cKeyFmt = 0x10; // = CalcTagKeyFmt( *tte->exp ); switch( tte->exp->GetReturnType()){ case XB_EXP_CHAR: tte->cKeyType = 'C'; tte->iKeyLen = tte->exp->GetResultLen(); tte->iSecKeyType = 0; break; case XB_EXP_NUMERIC: tte->cKeyType = 'N'; tte->iKeyLen = 12; tte->iSecKeyType = 0; break; case XB_EXP_DATE: tte->cKeyType = 'D'; tte->iKeyLen = 8; tte->iSecKeyType = 1; break; default: iErrorStop = 200; iRc = XB_INVALID_INDEX; throw iRc; } tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 ); tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 ); // write the new tte entry here char tteBuf[21]; memset( tteBuf, 0x00, 21 ); ePutUInt32( &tteBuf[0], tte->ulTagHdrPageNo ); for( xbUInt32 l = 0; l < tte->sTagName->Len() && l < 10; l++ ){ tteBuf[l+4] = tte->sTagName->GetCharacter(l+1); } tteBuf[15] = tte->cKeyFmt; tteBuf[19] = 0x02; // appears to always be a 0x02 tteBuf[20] = tte->cKeyType; if(( iRc = xbFseek( (iTagUseCnt * 32) + 544, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 210; throw iRc; } if(( iRc = xbFwrite( tteBuf, 21, 1 )) != XB_NO_ERROR ){ iErrorStop = 220; throw iRc; } // Begin Tag Header tte->ulRootPage = ulPageCnt; tte->ulTagSize = (xbUInt32) iBlockFactor; ulPageCnt += 2; tte->cKeyFmt2 = 0x10; if( iDescending ) tte->cKeyFmt2 += 0x08; if( iUnique ){ tte->cKeyFmt2 += 0x40; tte->cUnique = 0x01; } tte->cTag11 = 0x1B; // always 0x1b ? tte->cSerialNo = 0x01; // version incremented with each tag update tte->ulLeftChild = tte->ulRootPage; tte->ulRightChild = tte->ulRootPage; tte->cTagYY = (char) d.YearOf() - 1900; tte->cTagMM = (char) d.MonthOf(); tte->cTagDD = (char) d.DayOf( XB_FMT_MONTH ); tte->cKeyType2 = tte->cKeyType; tte->iKeyItemLen = tte->iKeyLen + 4; while(( tte->iKeyItemLen % 4 ) != 0 ) tte->iKeyItemLen++; tte->iKeysPerBlock = (xbInt16) (GetBlockSize() - 12) / tte->iKeyItemLen; tte->cKeyFmt3 = CalcTagKeyFmt( *tte->exp ); char *pBuf; if(( pBuf = (char *) calloc( 1, (size_t) GetBlockSize())) == NULL ){ iErrorStop = 230; iRc = XB_NO_MEMORY; throw iRc; } char *wPtr; wPtr = pBuf; ePutUInt32( wPtr, tte->ulRootPage ); wPtr += 4; ePutUInt32( wPtr, tte->ulTagSize ); wPtr += 4; *wPtr = tte->cKeyFmt2; wPtr++; *wPtr = tte->cKeyType2; wPtr += 2; *wPtr = tte->cTag11; wPtr += 1; ePutInt16( wPtr, tte->iKeyLen ); wPtr += 2; ePutInt16( wPtr, tte->iKeysPerBlock ); wPtr += 2; ePutInt16( wPtr, tte->iSecKeyType ); wPtr += 2; ePutInt16( wPtr, tte->iKeyItemLen ); wPtr += 2; *wPtr = tte->cSerialNo; wPtr += 3; *wPtr = tte->cUnique; wPtr++; for( xbUInt32 l = 0; l < tte->sKeyExp->Len(); l++ ) *wPtr++ = tte->sKeyExp->GetCharacter(l+1); wPtr = pBuf; tte->cHasKeys = 0x00; pBuf[246] = tte->cHasKeys; wPtr += 248; ePutUInt32( wPtr, tte->ulLeftChild ); wPtr += 4; ePutUInt32( wPtr, tte->ulRightChild ); pBuf[257] = tte->cTagYY; pBuf[258] = tte->cTagMM; pBuf[259] = tte->cTagDD; pBuf[480] = tte->cKeyFmt3; if( sFilter.Len() > 0 ){ pBuf[245] = tte->cHasFilter; wPtr = pBuf; wPtr += 762; for( xbUInt32 l = 0; l < sFilter.Len(); l++ ) *wPtr++ = sFilter.GetCharacter(l+1); } if(( iRc = xbFseek( tte->ulTagHdrPageNo * 512, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 240; throw iRc; } if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){ iErrorStop = 250; throw iRc; } memset( pBuf, 0x00, GetBlockSize() ); if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){ iErrorStop = 260; throw iRc; } iTagUseCnt++; cNextTag++; if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ iErrorStop = 270; throw iRc; } // update the dbf file if needed - discreet field, has no filter // 10/15/22 - dbase 7 does not update this field on index creation if( tte->cKeyFmt3 == 0x01 && !tte->cHasFilter ){ // printf( "cKeyFmt3 = [%x]\n", tte->cKeyFmt3 ); xbInt16 iFldNo; if(( iRc = dbf->GetFieldNo( sKey, iFldNo )) != XB_NO_ERROR ){ iErrorStop = 280; throw iRc; } xbInt64 lOffset = 31 + ((iFldNo + 1) * 32 ); if(( iRc = dbf->xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 290; iRc = XB_SEEK_ERROR; throw iRc; } char cBuf[2]; cBuf[0] = 0x01; cBuf[1] = 0x00; if(( iRc = dbf->xbFwrite( cBuf, 1, 1 )) != XB_NO_ERROR ){ iErrorStop = 300; throw iRc; } dbf->UpdateSchemaIxFlag( iFldNo, 0x01 ); } // add the new entry to the end of the list of tags if( mdxTagTbl == NULL ){ mdxTagTbl = tte; } else { xbMdxTag *tteL = mdxTagTbl; while( tteL->next ) tteL = tteL->next; tteL->next = tte; } /* update the btree pointers */ CalcBtreePointers(); char bBuf[3]; xbMdxTag *tteWork = mdxTagTbl; if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 310; throw iRc; } while( tteWork ){ bBuf[0] = tteWork->cLeftChild; bBuf[1] = tteWork->cRightChild; bBuf[2] = tteWork->cParent; if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){ iErrorStop = 320; throw iRc; } if( tteWork->next ){ if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){ iErrorStop = 330; throw iRc; } } tteWork = tteWork->next; } free( pBuf ); } catch (xbInt16 iRc ){ if( tte ){ if( tte->cpKeyBuf ) free( tte->cpKeyBuf ); if( tte->cpKeyBuf2 ) free( tte->cpKeyBuf2 ); if( tte->exp ) delete tte->exp; if( tte->filter ) delete tte->filter; if( tte->sKeyExp ) delete tte->sKeyExp; if( tte->sFiltExp ) delete tte->sFiltExp; if( tte->sTagName ) delete tte->sTagName; free( tte ); } xbString sMsg; sMsg.Sprintf( "xbIxMdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; }; /***********************************************************************/ //! @brief Delete a key from a node. /*! This routine deletes a key from a supplied node. \param vpTag Tag to delete key on. \param npNode Node to delete key on. \param iSlotNo Slot number of key to delete. \returns Return Codes */ xbInt16 xbIxMdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag * npTag = (xbMdxTag *) vpTag; try{ xbInt32 lKeyCnt = GetKeyCount( npNode ); xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen; xbBool bLeaf = IsLeaf( vpTag, npNode ); if( !bLeaf ) iLen += 4; char *pTrg = npNode->cpBlockData; if( iLen > 0 ){ pTrg += (8 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos; // std::cout << "TrgSpot = " << (8 + (npTag->iKeyItemLen * (iSlotNo)) ) << "\n"; char *pSrc = pTrg; pSrc += npTag->iKeyItemLen; memmove( pTrg, pSrc, (size_t) iLen ); } // zap out the right most key pTrg = npNode->cpBlockData; if( bLeaf ){ pTrg += (8 + (npTag->iKeyItemLen * ( lKeyCnt-1 ))); } else { pTrg += (12 + (npTag->iKeyItemLen * ( lKeyCnt-1 ))); } for( xbInt16 i = 0; i < npTag->iKeyItemLen; i++ ) *pTrg++ = 0x00; // set the new number of keys ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 ); // if node empty, add it to the free node chain if( lKeyCnt < 2 ){ if( bReuseEmptyNodes ){ if( bLeaf && lKeyCnt == 1 ){ if(( iRc = HarvestEmptyNode( npTag, npNode, 0 )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } } } // write out the block if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::DeleteFromNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return XB_NO_ERROR; } /***********************************************************************/ //! @brief Delete a key. /*! This routine deletes a key. It assumes the key to delete is the current key in the node chain. \param vpTag Tag to delete key on. \returns Return Codes */ xbInt16 xbIxMdx::DeleteKey( void *vpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag * npTag = (xbMdxTag *) vpTag; // save copy of node chain to reset to after delete completed xbIxNode *npSaveNodeChain = npTag->npNodeChain; npTag->npNodeChain = NULL; xbIxNode * npSaveCurNode = npTag->npCurNode; try{ xbString sMsg; if(( iRc = xbIxMdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } // Delete key needs to handle two scenarios // 1 - if delete is on the only key of leaf, // traverse up tree, trim as needed // 2 - if last key on node is deleted, and key value is not the same // as prev key, ascend tree looking for an interior node needing // updated key value xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode ); if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } if( lOrigKeyCnt == 1 ){ // scenario 1 xbBool bDone = xbFalse; xbBool bIsLeaf = xbFalse; xbInt32 lKeyCnt; npTag->npCurNode = npTag->npCurNode->npPrev; while( npTag->npCurNode && !bDone ){ lKeyCnt = GetKeyCount( npTag->npCurNode ); bIsLeaf = IsLeaf( npTag, npTag->npCurNode ); if( lKeyCnt > 0 ){ if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } else if(( iRc = HarvestEmptyNode( npTag, npTag->npCurNode, 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if( (bIsLeaf && lKeyCnt > 1) || (!bIsLeaf && lKeyCnt > 0) ) bDone = xbTrue; else npTag->npCurNode = npTag->npCurNode->npPrev; } } else if( npTag->npCurNode->iCurKeyNo == (xbUInt32) lOrigKeyCnt - 1 ){ // scenario 2 // if last two keys identical, then nothing to do, else go up looking for a key to change if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen )){ xbIxNode *pNode = npTag->npCurNode->npPrev; char *pSrc = GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ); while( pNode && pNode->ulBlockNo != npTag->ulRootPage && pNode->iCurKeyNo == (xbUInt32) GetKeyCount( pNode ) ) pNode = pNode->npPrev; if( pNode ){ if( pNode->iCurKeyNo < (xbUInt32) GetKeyCount( pNode )){ char *pTrg = pNode->cpBlockData; pTrg += 12 + (pNode->iCurKeyNo * (xbUInt32) npTag->iKeyItemLen); for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) *pTrg++ = *pSrc++; // write out the block if(( iRc = WriteBlock( pNode->ulBlockNo, GetBlockSize(), pNode->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } } } } } // restore node chain to pre delete status (which should be post add status) npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); npTag->npNodeChain = npSaveNodeChain; npTag->npCurNode = npSaveCurNode; // update the serial number if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::DeleteKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( npSaveNodeChain ){ npTag->npNodeChain = npSaveNodeChain; npSaveNodeChain = FreeNodeChain( npSaveNodeChain ); npTag->npCurNode = npSaveCurNode; } } return iRc; } /***********************************************************************/ //! @brief Delete a given tag /*! \param vpTag Input tag ptr for tag to be deleted
\returns Return Codes
1 = Deleted entire MDX file, only had one tag */ xbInt16 xbIxMdx::DeleteTag( void *vpTag ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; xbMdxTag * mpTag = (xbMdxTag *) vpTag; xbIxNode *n = NULL; xbBool bLoneTag = xbFalse; try{ if( !vpTag ){ iErrorStop = 100; iRc = XB_INVALID_TAG; throw iRc; } char cSaveHasFilter = mpTag->cHasFilter; char cSaveKeyFmt3 = mpTag->cKeyFmt3; xbString sSaveKey = mpTag->sKeyExp->Str(); if( iTagUseCnt == 1 ){ // std::cout << "xbIxMdx::DeleteTag - one tag found, delete the mdx file\n"; // close the mdx file if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // delete the file xbRemove(); // init variables - needed? // Init(); // iRc > 0 defines this as the only tag in an MDX file, MDX file deleted. // signals to the calling process to drop the MDX file from the // list of updateable indices. bLoneTag = xbTrue; } else { // harvest tag nodes if(( iRc = HarvestTagNodes( mpTag, xbTrue )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } // remove an entry from tag table // which tag is this? xbInt16 iTagNo = 0; xbMdxTag *mp = mdxTagTbl; xbMdxTag *mpPrev = NULL; while( mp && mp->ulTagHdrPageNo != mpTag->ulTagHdrPageNo ){ iTagNo++; mpPrev = mp; mp = mp->next; } // remove it from the linked list of tags if( !mpPrev ){ mdxTagTbl = mp->next; } else { mpPrev->next = mp->next; } if( mp ){ if( mp->cpKeyBuf ) free( mp->cpKeyBuf ); if( mp->cpKeyBuf2 ) free( mp->cpKeyBuf2 ); if( mp->exp ) delete mp->exp; if( mp->filter ) delete mp->filter; if( mp->sKeyExp ) delete mp->sKeyExp; if( mp->sFiltExp ) delete mp->sFiltExp; if( mp->sTagName ) delete mp->sTagName; free( mp ); } xbInt32 iTarg = iTagNo * 32; xbInt32 iSrc = iTarg + 32; xbInt32 iLen = (iTagUseCnt - iTagNo) * 32; if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } char Buf[1536]; // 47 tags + 1 in case tag #47 is deleted memset( Buf, 0x00, 1536 ); if(( iRc = xbFread( Buf, 1504, 1 )) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } char *pTrg = Buf; pTrg += iTarg; char *pSrc = Buf; pSrc += iSrc; for( xbInt32 i = 0; i < iLen; i++ ) *pTrg++ = *pSrc++; if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 180; throw iRc; } if(( iRc = xbFwrite( Buf, 1504, 1 )) != XB_NO_ERROR ){ iErrorStop = 190; throw iRc; } iTagUseCnt--; if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } /* update the btree pointers */ CalcBtreePointers(); char bBuf[3]; xbMdxTag *tteWork = mdxTagTbl; if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 210; throw iRc; } while( tteWork ){ bBuf[0] = tteWork->cLeftChild; bBuf[1] = tteWork->cRightChild; bBuf[2] = tteWork->cParent; if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){ iErrorStop = 320; throw iRc; } if( tteWork->next ){ if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){ iErrorStop = 330; throw iRc; } } tteWork = tteWork->next; } } // update the dbf file if needed, if discreet field with no filter // printf( "cSaveKeyFmt3 = [%x] cSaveHasFilter=[%x] SaveKey = [%s]\n", cSaveKeyFmt3, cSaveHasFilter, sSaveKey.Str()); if( cSaveKeyFmt3 == 0x01 && !cSaveHasFilter ){ xbInt16 iFldNo; if(( iRc = dbf->GetFieldNo( sSaveKey, iFldNo )) != XB_NO_ERROR ){ iErrorStop = 340; throw iRc; } xbInt64 lOffset = 31 + ((iFldNo + 1) * 32 ); if(( iRc = dbf->xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 350; iRc = XB_SEEK_ERROR; throw iRc; } char cBuf[2]; cBuf[0] = 0x00; cBuf[1] = 0x00; if(( iRc = dbf->xbFwrite( cBuf, 1, 1 )) != XB_NO_ERROR ){ iErrorStop = 360; throw iRc; } dbf->UpdateSchemaIxFlag( iFldNo, 0x00 ); } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( n ) free( n ); } if( bLoneTag && !iRc ) return 1; else return iRc; } /***********************************************************************/ #ifdef XB_DEBUG_SUPPORT //! @brief Dump a given block for a tag /*! \param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\param ulBlockNo Block number to dump \param mpTag Index tag pointer \returns Return Codes */ xbInt16 xbIxMdx::DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag *mpTag ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; xbString s, s2; xbBool bLeaf; char *p; try{ if(( iRc = GetBlock( mpTag, ulBlockNo, 0 )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } p = cNodeBuf; xbInt32 lNoOfKeys = eGetInt32( p ); p+=4; xbUInt32 ulNode2 = eGetUInt32( p ); if( !mpTag ){ // if no tag info, print what is available without tag info and exit s.Sprintf( "--- BlkNo = %ld Page = %ld NoOfKeys = %ld Node2 (opt NextFreePage) = %ld", ulBlockNo, BlockToPage( ulBlockNo ), lNoOfKeys, ulNode2 ); xbase->WriteLogMessage( s, iOpt ); return XB_NO_ERROR; } p+=4; p+= mpTag->iKeyItemLen * lNoOfKeys; if( eGetUInt32( p ) == 0 ){ bLeaf = xbTrue; // std::cout << "No of keys = " << lNoOfKeys << "\n"; s.Sprintf( "--- Leaf Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo ); } else { bLeaf = xbFalse; s.Sprintf( "--- Interior Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo ); } if( ulNode2 > 0 ) s.Sprintf( "%s Node2 (opt NextFreePage) = %d", s.Str(), ulNode2 ); xbase->WriteLogMessage( s, iOpt ); xbInt32 l; for( l = 0; l < lNoOfKeys; l++ ){ p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen )); s.Sprintf( "%08ld\t", eGetUInt32( p )); p+=4; if( mpTag->cKeyType2 == 'C' ){ //CHAR for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ ) s += *p++; s.Trim(); } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC xbBcd bcd( p ); bcd.ToString( s2 ); s += s2; } else if( mpTag->cKeyType2 == 'D' ){ // DATE xbInt32 lDate = (xbInt32) eGetDouble( p ); xbDate d( lDate ); s.Sprintf( "%s\t%ld\t(%s)", s.Str(), lDate, d.Str()); } else { s.Sprintf( "Unknown key type [%c]", mpTag->cKeyType2 ); } xbase->WriteLogMessage( s, iOpt ); } if( !bLeaf ){ // interior node has one extra key at the right most position p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen )); s.Sprintf( "\t%08ld", eGetUInt32( p )); xbase->WriteLogMessage( s, iOpt ); } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::DumpBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /**************************************************************************************************************/ //! @brief Dump free blocks. /*! Dump free blocks for index debugging purposes. \param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\returns Return Codes */ xbInt16 xbIxMdx::DumpFreeBlocks( xbInt16 iOpt ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbString s; char *pBuf = NULL; char *pNextPage; xbUInt32 ulNextPage; try{ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } xbUInt32 ulLastBlock = PageToBlock( ulPageCnt ); pBuf = (char *) malloc( (size_t) GetBlockSize()); if( !pBuf ){ iErrorStop = 110; iRc = XB_NO_MEMORY; throw iRc; } if( ulFirstFreePage > 0 ){ xbUInt32 ulThisFreePage = ulFirstFreePage; xbUInt32 ulNextFreePage = 0; xbUInt32 ulCnt = 0; xbase->WriteLogMessage( "*** Free Blocks ***", iOpt ); s.Sprintf( "File Header - FirstFreePage = %ld Block = %ld", ulFirstFreePage, PageToBlock( ulFirstFreePage )); xbase->WriteLogMessage( s, iOpt ); while( ulThisFreePage > 0 ){ if(( iRc = ReadBlock( PageToBlock( ulThisFreePage ), GetBlockSize(), pBuf )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } pNextPage = pBuf; pNextPage+=4; ulNextFreePage = eGetUInt32( pNextPage ); s.Sprintf( "Free Page# = %ld\t(Block# = %ld)\tNext Free Page = %ld\t(Block = %ld)", ulThisFreePage, PageToBlock( ulThisFreePage ), ulNextFreePage, PageToBlock( ulNextFreePage )); xbase->WriteLogMessage( s, iOpt ); ulThisFreePage = ulNextFreePage; ulCnt++; } s.Sprintf( "%ld free blocks (%ld pages)", ulCnt, BlockToPage( ulCnt )); xbase->WriteLogMessage( s, iOpt ); xbase->WriteLogMessage( "*** End Of Free Blocks ***", iOpt ); } pNextPage = pBuf; pNextPage+=4; s = "*** Beginning of Block2 Info ***"; xbase->WriteLogMessage( s, iOpt ); s = "ulBlock2 info. ulBlock2 is either one of a linked list of free nodes, or the id of the original node that this node was split from."; xbase->WriteLogMessage( s, iOpt ); s = "Stored in physical file as pages, processed in blocks"; xbase->WriteLogMessage( s, iOpt ); xbUInt32 ulFirstBlock = 3; for( xbUInt32 ul = ulFirstBlock; ul < ulLastBlock; ul++ ){ if(( iRc = ReadBlock( ul, GetBlockSize(), pBuf )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } ulNextPage = eGetUInt32( pNextPage ); if( ulNextPage > 0 ){ s.Sprintf( " Block# = %ld\tPage# = %ld\tulBlock2 = %ld\tulBlock2(Page) = %ld", ul, BlockToPage( ul ), PageToBlock( ulNextPage ), ulNextPage ); xbase->WriteLogMessage( s, iOpt ); } } s = "*** End of Block2 Info ***"; xbase->WriteLogMessage( s, iOpt ); if( pBuf ) free( pBuf ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::DumpFreeBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( pBuf ) free( pBuf ); } return iRc; } /**************************************************************************************************************/ //! @brief Dump interior and leaf blocks for a given tag. /*! Dump blocks for given tag for index debugging purposes. A page is 512 bytes
A block is one or more pages
The default mdx block size is 2 pages, or 1024 bytes
The first four pages or header pages
\param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\param vpTag Index tag pointer, defaults to all tags if null. \returns Return Codes */ xbInt16 xbIxMdx::DumpTagBlocks( xbInt16 iOpt, void * vpTag ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; xbInt16 iCurTag = 0; xbString s; xbInt16 iBlockCtr = 0; try{ xbMdxTag * mpTag; if( vpTag == NULL ) mpTag = (xbMdxTag *) GetTag( iCurTag++ ); else mpTag = (xbMdxTag *) vpTag; if( mpTag == NULL ){ iErrorStop = 100; iRc = XB_INVALID_TAG; throw iRc; } xbIxNode *n; xbString s; xbString s2; xbBool bDone = xbFalse; xbUInt32 ulBlkNo; xbLinkListOrd ll; xbLinkListNode * llN; ll.SetDupKeys( xbFalse ); s.Sprintf( "%s Root Page %ld (Block %ld)", mpTag->cTagName, mpTag->ulRootPage, PageToBlock( mpTag->ulRootPage ) ); xbase->WriteLogMessage( s, iOpt ); // for each tag while( !bDone ){ // clear out any history ll.Clear(); if( mpTag->npNodeChain ){ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } while( GetNextKey( mpTag, 0 ) == 0 ){ n = mpTag->npNodeChain; while(n){ ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo ); n = n->npNext; } } llN = ll.GetHeadNode(); while( llN ){ ulBlkNo = llN->GetKey(); xbIxMdx::DumpBlock( iOpt, ulBlkNo, mpTag ); llN = llN->GetNextNode(); iBlockCtr++; } if( vpTag || iCurTag >= GetTagCount()) bDone = xbTrue; else mpTag = (xbMdxTag *) GetTag( iCurTag++ ); } s.Sprintf( "\nTotal Blocks: %d", iBlockCtr ); xbase->WriteLogMessage( s, iOpt ); if( mpTag->npNodeChain ){ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::DumpTagBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Dump index file header. /*! Dump a index file header for debugging purposes. \param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\param iFmtOpt Output Format
0, 1 = Header info only
2 = Tag info
3 = Header && Tag info
\returns Return Codes */ xbInt16 xbIxMdx::DumpHeader( xbInt16 iOpt, xbInt16 iFmtOpt ) { xbInt16 iRc = XB_NO_ERROR; xbString s; if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ) return iRc; char c, tfv, cDisplayMask = 1; cDisplayMask = cDisplayMask << 7; if( iFmtOpt != 2 && iFmtOpt != 4 ){ s = "*** MDX Index Header ***"; xbase->WriteLogMessage( s, iOpt ); s = "Version = "; tfv = cVersion; for( c = 1; c<= 8; c++ ){ //std::cout << (tfv & cDisplayMask ? '1' : '0'); s+= (tfv & cDisplayMask ? '1' : '0'); tfv <<= 1; } xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Create Date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "File Name = %s", sFileName.Str() ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Block Factor = %d", iBlockFactor ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Block Size = %d", GetBlockSize() ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Prod Ix Flag = %d", (xbInt16) cProdIxFlag ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Tag Entry Cnt = %d", (xbInt16) cTagEntryCnt ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Tag Len = %d", iTagLen ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Next Tag = %d", (xbInt16) cNextTag ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Tag Use Cnt = %d", iTagUseCnt ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Page Cnt = %d", ulPageCnt ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "First Free Page = %d", ulFirstFreePage ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "No Of Block Avail = %d\n", ulNoOfBlockAvail ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Last update date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 ); xbase->WriteLogMessage( s, iOpt ); if( ulFirstFreePage > 0 ){ xbString s; xbUInt32 ulNfp = ulFirstFreePage; // next free page xbInt16 lc = 0; while( ulNfp && lc++ < 5 ){ if( s.Len() > 0 ) s += ","; s.Sprintf( "%s%ld", s.Str(), ulNfp ); if(( iRc = GetBlock( NULL, (xbUInt32) (ulNfp / (xbUInt32) iBlockFactor), 0 )) != 0 ) return iRc; ulNfp = eGetUInt32( cNodeBuf+4 ); } xbase->WriteLogMessage( s, iOpt ); } } if( iFmtOpt > 1 ){ xbMdxTag *tt = mdxTagTbl; xbString s; xbInt16 i = 0; if( tt ){ while( tt ){ i++; if(( iRc = LoadTagDetail( 2, tt )) != XB_NO_ERROR ) return iRc; s.Sprintf( "TTE (%d)\tName HdrPage\tFormat\tLeftChild\tRightChild\tParent\tKeyType", i ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "TTE (%d)\t%-12s %d\t\t%d\t%d\t\t%d\t\t%d\t%c\n", i, tt->cTagName, tt->ulTagHdrPageNo, tt->cKeyFmt, tt->cLeftChild, tt->cRightChild, tt->cParent, tt->cKeyType ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "TTH (%d)\tRoot\tTagSize\tKeyFmt2\tType2\tKeyLen\tKeysPerBlock\tSecType\tKeyItemLen\tSerial#\tHasKeys\tFilter\tDesc\tUnique\tLchild\tRchild\tKeyFmt3\tTagDate", i ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "TTH (%d)\t%d\t%d\t%d\t%c\t%d\t%d\t\t%d\t%d\t\t%x\t%x\t%d\t%d\t%d\t%d\t%d\t%d\t%d/%d/%d", i, tt->ulRootPage, tt->ulTagSize, tt->cKeyFmt2, tt->cKeyType2, tt->iKeyLen, tt->iKeysPerBlock, tt->iSecKeyType, tt->iKeyItemLen, tt->cSerialNo, tt->cHasKeys, tt->cHasFilter, (((tt->cKeyFmt2 & 0x08) > 0) ? 1 : 0), // descending? tt->cUnique, tt->ulLeftChild, tt->ulRightChild, tt->cKeyFmt3, (int) tt->cTagMM, (int) tt->cTagDD, (int) tt->cTagYY % 100 ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Key (%d) %s", i, tt->sKeyExp->Str()); xbase->WriteLogMessage( s, iOpt ); if( tt->cHasFilter ){ s.Sprintf( "Flt (%d) %s", i, tt->sFiltExp->Str()); xbase->WriteLogMessage( s, iOpt ); } xbase->WriteLogMessage( "", iOpt ); tt = tt->next; } } } return iRc; } /***********************************************************************/ xbInt16 xbIxMdx::DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbInt16 iDepth = 0; xbUInt32 lKeyCtr = 0; xbInt32 iMinDepth = 999999; xbInt32 iMaxDepth = 0; try{ /* get first node while interior node print the left key level++ go down one on the left */ xbMdxTag * mpTag = (xbMdxTag *) vpTag; if( mpTag->npNodeChain ){ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } // Get the root if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } // lRootBlock is now available if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // if no keys on this node, then the index is empty xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData ); if( ulKeyPtr == 0 ){ iErrorStop = 120; iRc = XB_EMPTY; throw iRc; } while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt ); if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } } if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } // loop through the leaf entries of left most leaf if( iDepth < iMinDepth ) iMinDepth = iDepth; if( iDepth > iMaxDepth ) iMaxDepth = iDepth; xbUInt32 ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){ PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt ); lKeyCtr++; } // if head node = start node, return if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) return XB_NO_ERROR; xbBool bEof = false; while( !bEof ){ // go up the chain, looking for an interior node with more keys on it xbIxNode * TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; mpTag->npCurNode->npNext = NULL; TempIxNode->npPrev = NULL; TempIxNode = FreeNodeChain( TempIxNode ); iDepth--; while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) && mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; mpTag->npCurNode->npNext = NULL; TempIxNode->npPrev = NULL; TempIxNode = FreeNodeChain( TempIxNode ); iDepth--; } // if head node && right most key, return if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) && mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData )) bEof = true; if( !bEof ){ mpTag->npCurNode->iCurKeyNo++; if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } PrintKey( vpTag, mpTag->npCurNode , mpTag->npCurNode->iCurKeyNo, iDepth++, 'I', iOutputOpt ); if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } // traverse down the left side of the tree while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node { PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt ); if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 180; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 190; throw iRc; } } if( iDepth < iMinDepth ) iMinDepth = iDepth; if( iDepth > iMaxDepth ) iMaxDepth = iDepth; ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){ PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt ); lKeyCtr++; } } } xbString s; s.Sprintf( "Total keys = [%ld] Min Depth = [%d] Max Depth = [%d]", lKeyCtr, iMinDepth, iMaxDepth ); xbase->WriteLogMessage( s.Str(), 2 ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::DumpIxForTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /**************************************************************************************************************/ void xbIxMdx::DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const { xbMdxTag * mpTag = (xbMdxTag *) vpTag; xbString s( "Dump Node Chain" ); xbase->WriteLogMessage( s, 2 ); if( mpTag->npNodeChain ){ xbIxNode *n = mpTag->npNodeChain; xbInt16 iCtr = 0; char cLeaf; s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo Page NoOfKeys Type" ); xbase->WriteLogMessage( s, iOutputOpt ); while( n ){ IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I'; s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %08ld %c", iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo, n->ulBlockNo, n->ulBlockNo * (xbUInt32) iBlockFactor, eGetUInt32( n->cpBlockData ), cLeaf ); xbase->WriteLogMessage( s, 2 ); n = n->npNext; } } else { s = "Empty Node Chain"; xbase->WriteLogMessage( s, 2 ); } } #endif /***********************************************************************************************/ xbInt16 xbIxMdx::FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ){ xbMdxTag * mpTag = (xbMdxTag *) vpTag; if( mpTag->cKeyType2 == 'N' ){ // mdx indices store numeric keys as bcd values xbBcd bcd( dKey ); return xbIx::FindKey( vpTag, bcd, iRetrieveSw ); } else // this would be a julian date inquiry return FindKey( vpTag, &dKey, 8, iRetrieveSw ); } /***********************************************************************************************/ // iRetrieveSw = 1 - position db file to index position // 0 - do not position dbf file xbInt16 xbIxMdx::FindKey( void *vpTag, const void * vpKey, xbInt32 lSearchKeyLen, xbInt16 iRetrieveSw ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; try{ // clean up any previous table updates before moving on if( iRetrieveSw ){ if( dbf->GetDbfStatus() == XB_UPDATED ){ if( dbf->GetAutoCommit() == 1 ){ if(( iRc = dbf->Commit()) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } else { if(( iRc = dbf->Abort()) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } } } xbUInt32 ulNoOfKeys; xbMdxTag * mpTag = (xbMdxTag *) vpTag; char cKeyType = GetKeyType( vpTag ); xbBool bDescending = mpTag->cKeyFmt2 & 0x08; if( mpTag->npNodeChain ){ // determine if the index has been updated since the last time it was used time_t tFileTs; if(( iRc = GetFileMtime( tFileTs )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if( mpTag->tNodeChainTs < tFileTs ){ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } else { // pop up the chain looking for appropriate starting point xbBool bDone = false; xbIxNode * TempIxNode; while( mpTag->npCurNode && !bDone && (mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor ))){ // not root node //if no keys on the node, pop up one ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); if( ulNoOfKeys == 0 ){ TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } else { iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, 0, mpTag->iKeyItemLen ), (size_t) lSearchKeyLen ); if( (!bDescending && iRc <= 0) || (bDescending && iRc >= 0 )){ TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } else { // get the number of keys on the block and compare the key to the rightmost key xbUInt32 ulKeyCtr = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1; // IsLeaf( vpTag, mpTag->npCurNode ); iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulKeyCtr, mpTag->iKeyItemLen), (size_t) lSearchKeyLen ); if( (!bDescending && iRc > 0) || (bDescending && iRc < 0 )){ TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } else { bDone = true; } } } } } } // either started empty, or cleared due to file time diff if( !mpTag->npNodeChain ){ // Get the root if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } // lRootBlock is now available if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } // if this is a leaf node and no keys on this node, then the index is empty ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ // iRc = XB_EMPTY; iRc = XB_NOT_FOUND; return iRc; } } // should be in the appropriate position in the node chain to continue the search from here // run down through the interior nodes xbInt16 iSearchRc = 0; xbUInt32 ulKeyPtr = 0; while( vpTag && !IsLeaf( vpTag, mpTag->npCurNode ) ){ // get the number of keys on the block and compare the key to the rightmost key ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); if( ulNoOfKeys == 0 ){ mpTag->npCurNode->iCurKeyNo = 0; } else { iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulNoOfKeys - 1, mpTag->iKeyItemLen), (size_t) lSearchKeyLen ); if( (!bDescending && iRc > 0) || (bDescending && iRc < 0)){ mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; } else { mpTag->npCurNode->iCurKeyNo = (xbUInt32) BSearchBlock( cKeyType, mpTag->npCurNode, (xbInt32) mpTag->iKeyItemLen, vpKey, (xbInt32) lSearchKeyLen, iSearchRc, bDescending ); } } if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32)iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } } ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); xbInt16 iCompRc = 0; if( ulNoOfKeys == 0 ){ // iCompRc = -1; // iRc = XB_EMPTY; iRc = XB_NOT_FOUND; return iRc; } else { iRc = BSearchBlock( cKeyType, mpTag->npCurNode, mpTag->iKeyItemLen, vpKey, lSearchKeyLen, iCompRc, bDescending ); // iCompRc // 0 found // < 0 eof encountered, search key > last key in file // > 0 not found, positioned to next key if( iCompRc >= 0 ){ mpTag->npCurNode->iCurKeyNo = (xbUInt32) iRc; if( iRetrieveSw ){ xbUInt32 ulKey = mpTag->npCurNode->iCurKeyNo; if( ulKey >= ulNoOfKeys ) // if position past end of keys, need to go back one and position to last key ulKey--; // if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ if(( iRc = GetKeyPtr( vpTag, ulKey, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 180; throw iRc; } } } } if( iCompRc == 0 ) return XB_NO_ERROR; else if( iCompRc > 0 ) return XB_NOT_FOUND; else return XB_EOF; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::FindKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ xbInt16 xbIxMdx::FindKeyForCurRec( void * vpTag ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbInt16 i = 0; try{ if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::FindKeyForCurRec() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return XB_NO_ERROR; } /***********************************************************************/ //! @brief Get dbf record number for given key number. /*! \param vpTag Tag to retrieve dbf rec number on. \param iKeyNo Key number for retrieval \param np Pointer to node \param ulDbfPtr- Output dbf record number \returns Return Codes */ xbInt16 xbIxMdx::GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulDbfPtr ) const { xbInt16 iRc = 0; xbInt16 iErrorStop = 0; try{ #ifdef XB_DEBUG_SUPPORT // turn this off in production mode for better performance xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData ); if( iKeyNo < 0 || iKeyNo > (xbInt16) --ulNoOfKeys ){ iErrorStop = 100; throw XB_INVALID_KEYNO; } #endif xbMdxTag * mpTag = (xbMdxTag *) vpTag; char *p = ( np->cpBlockData); p += (8 + (iKeyNo * mpTag->iKeyItemLen)); ulDbfPtr = eGetUInt32 ( p ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::GetDbfPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ /*! \param vpTag Tag to retrieve first key on. \param iRetrieveSw xbTrue - Retrieve the record on success.
xbFalse - Don't retrieve record. \returns Return Codes */ xbInt16 xbIxMdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw = 0 ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; // convert the tag pointer to mdx tag pointer xbMdxTag * mpTag = (xbMdxTag *) vpTag; try{ // clear out any history if( mpTag->npNodeChain ){ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } // Get the root if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } // lRootPage is available if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // if no keys on this node, then the index is empty // this is not true xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData ); if( ulKeyPtr == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ iRc = XB_EMPTY; return iRc; } while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } } // retrieve record to data buf if( iRetrieveSw ){ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::GetFirstKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //xbBool xbIxMdx::GetIndexUpdated() const { // std::cout << "xbIxMdx::GetIndexUpdate() FIX ME \n"; // return xbFalse; //} /***********************************************************************/ //! @brief Get the key expression for the given tag. /*! \param vpTag Tag to retrieve key expression from tag. \returns Key expression. */ xbString &xbIxMdx::GetKeyExpression( const void * vpTag ) const{ xbMdxTag *mpTag = (xbMdxTag *) vpTag; return *mpTag->sKeyExp; } /***********************************************************************/ //! @brief Get the key expression for the given tag. /*! \param vpTag Tag to retrieve filter expression from tag (if it exists). \returns Key filter. */ xbString &xbIxMdx::GetKeyFilter( const void * vpTag ) const{ xbMdxTag * mpTag = (xbMdxTag *) vpTag; if( mpTag->sFiltExp ) return *mpTag->sFiltExp; else return sNullString; } /**************************************************************************************************/ xbInt16 xbIxMdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ xbMdxTag *mdxTag = (xbMdxTag *) vpTag; char *p = np->cpBlockData; xbUInt32 ulKeyCnt = eGetUInt32( p ); if( iKeyNo < 0 || iKeyNo > (xbInt16) ulKeyCnt ){ iErrorStop = 100; iRc = XB_INVALID_KEYNO; throw iRc; } p+=8; // skip past first two four byte numeric fields p+= (iKeyNo * mdxTag->iKeyItemLen); ulKeyPtr = eGetUInt32( p ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::GetKeyPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Returns key update status. /*! \param vpTag Tag to check status on. \returns XB_UPD_KEY Key updated.
XB_DEL_KEY Key deleted.
XB_ADD_KEY Key added.
0 No key updates */ inline xbInt16 xbIxMdx::GetKeySts( void *vpTag ) const{ xbMdxTag *mpTag = (xbMdxTag *) vpTag; return mpTag->iKeySts; } /***********************************************************************/ char xbIxMdx::GetKeyType( const void *vpTag ) const { xbMdxTag * mpTag = (xbMdxTag *) vpTag; return mpTag->cKeyType; } /***********************************************************************/ //! @brief Get the last key for the given tag. /*! \param vpTag Tag to retrieve last key on. \param iRetrieveSw xbTrue - Retrieve the record on success.
xbFalse - Don't retrieve record. \returns Return Codes */ xbInt16 xbIxMdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){ return GetLastKey( 0, vpTag, iRetrieveSw ); } /***********************************************************************/ //! @brief Get the last key for the given tag and starting node. /*! \param ulBlockNo Starting node \param vpTag Tag to retrieve last key on. \param iRetrieveSw xbTrue - Retrieve the record if key found.
xbFalse - Don't retrieve record. \returns Return Codes */ xbInt16 xbIxMdx::GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 iRetrieveSw ){ // if UlNodeNo is zero, start at head node, otherwise start at ulNodeNo xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; // convert the tag pointer to mdx tag pointer xbMdxTag * mpTag = (xbMdxTag *) vpTag; try{ xbUInt32 ulNoOfKeys = 0; // clear out any history if( mpTag->npNodeChain ){ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } // Get the root if( ulBlockNo == 0 ){ if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } //if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ if(( iRc = GetBlock( vpTag, PageToBlock( mpTag->ulRootPage ), 1 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } else { if(( iRc = GetBlock( vpTag, ulBlockNo, 1 )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){ iRc = XB_EMPTY; return iRc; } mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); xbUInt32 ulKeyPtr = 0; while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node // std::cout << "Considered an interior node\n"; if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; } // leaf node has one fewer keys than the interior node mpTag->npCurNode->iCurKeyNo--; ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); // retrieve record to data buf if( iRetrieveSw ){ if(( iRc = GetKeyPtr( vpTag, (ulNoOfKeys-1), mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::GetLastKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Get the last key for a block number. /*! \param vpTag Tag to retrieve first key on. \param ulBlockNo Block number for key retrieval. \param cpBuf output buffer for key placement \returns Return Codes */ xbInt16 xbIxMdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){ // returns the last key for a given block number xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag * npTag = (xbMdxTag *) vpTag; try{ xbIxNode * npSaveNodeChain = npTag->npNodeChain; xbIxNode * npSaveCurNode = npTag->npCurNode; npTag->npNodeChain = NULL; if(( iRc = GetLastKey( ulBlockNo, npTag, 0 )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); // free memory FreeNodeChain( npTag->npNodeChain ); npTag->npNodeChain = npSaveNodeChain; npTag->npCurNode = npSaveCurNode; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::GetLastKeyForBlockNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc ) ); } return iRc; } /***********************************************************************/ //! @brief Get the next key for the given tag. /*! \param vpTag Tag to retrieve next key on. \param iRetrieveSw xbTrue - Retrieve the record on success.
xbFalse - Don't retrieve record. \returns Return Codes */ xbInt16 xbIxMdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; // convert the tag pointer to mdx tag pointer xbMdxTag * mpTag = (xbMdxTag *) vpTag; try{ if( !mpTag->npCurNode ) return GetFirstKey( vpTag, iRetrieveSw ); // more keys on this node? xbUInt32 ulKeyPtr; if( (eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1) > mpTag->npCurNode->iCurKeyNo ){ mpTag->npCurNode->iCurKeyNo++; if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if( iRetrieveSw ){ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } else { return iRc; } } else { return iRc; } } // if at end head node, then at eof if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) return XB_EOF; // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node xbIxNode * TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); // While no more right keys && not head node, pop up one node while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) && mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } // if head node && right most key, return eof if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) && mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData )) return XB_EOF; // move one to the right mpTag->npCurNode->iCurKeyNo++; if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } // traverse down the left side of the tree while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node { if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } } // retrieve record to data buf if( iRetrieveSw ){ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } } else { return iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::GetNextKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Get the previous key for the given tag. /*! \param vpTag Tag to retrieve previous key on. \param iRetrieveSw xbTrue - Retrieve the record on success.
xbFalse - Don't retrieve record. \returns Return Codes */ xbInt16 xbIxMdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){ xbString s; xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; // convert the tag pointer to mdx tag pointer xbMdxTag * mpTag = (xbMdxTag *) vpTag; try{ if( !mpTag->npCurNode ){ return GetLastKey( 0, vpTag, iRetrieveSw ); } xbUInt32 ulKeyPtr = 0; // more keys on this assumed-leaf node? if( mpTag->npCurNode->iCurKeyNo > 0 ){ mpTag->npCurNode->iCurKeyNo--; if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if( iRetrieveSw ){ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } else { return iRc; } } else { return iRc; } } //if head node = start node, at eof if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor )) return XB_BOF; // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node xbIxNode * TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); // While no more left keys && not head node, pop up one node while( mpTag->npCurNode->iCurKeyNo == 0 && mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){ TempIxNode = mpTag->npCurNode; mpTag->npCurNode = mpTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } //if head node && left most key, return bof if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) && mpTag->npCurNode->iCurKeyNo == 0 ) return XB_BOF; // move one to the left mpTag->npCurNode->iCurKeyNo--; if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } // traverse down the right side of the tree xbUInt32 ulNoOfKeys; while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node { ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); mpTag->npCurNode->iCurKeyNo = ulNoOfKeys; if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } } // ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); mpTag->npCurNode->iCurKeyNo = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1; // retrieve record to data buf if( iRetrieveSw ){ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } } else { return iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::GetPrevKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief GetReuseEmptyNode swuitch setting. /*! \returns xbFalse - Do not reuse empty MDX index nodes (Dbase 6. behavior). xbTrue - Reuse empty MDX index nodes. */ xbBool xbIxMdx::GetReuseEmptyNodesSw() const { return bReuseEmptyNodes; } /***********************************************************************/ xbBool xbIxMdx::GetSortOrder( void *vpTag ) const { // return true if descending xbMdxTag *mTag = (xbMdxTag *) vpTag; if( mTag->cKeyFmt2 & 0x08 ) return 0x01; else return 0x00; } /***********************************************************************/ //! @brief Get tag for tag number. /*! \param iTagNo - Zero based, which tag to retrieve. \returns Pointer to mdx tag for a given tag number. */ void * xbIxMdx::GetTag( xbInt16 iTagNo ) const { xbMdxTag *tt = mdxTagTbl; xbInt16 i = 0; while( i < iTagNo && tt->next ){ tt = tt->next; i++; } if( i == iTagNo ) return (void *) tt; else return NULL; } /***********************************************************************/ //! @brief Get tag for tag name. /*! \sTagName - Tag name to retrieve. \returns Pointer to mdx tag for a given tag number. */ void * xbIxMdx::GetTag( xbString &sTagName ) const { xbMdxTag *tt = mdxTagTbl; while( sTagName != tt->cTagName && tt->next ){ tt = tt->next; } if( sTagName == tt->cTagName ) return (void *) tt; else return NULL; } /***********************************************************************/ xbInt16 xbIxMdx::GetTagCount() const { return iTagUseCnt; } /***********************************************************************/ void xbIxMdx::GetTagName( void *vpTag, xbString &sTagName ){ xbMdxTag *mpTag = (xbMdxTag *) vpTag; sTagName = mpTag->sTagName->Str(); } /***********************************************************************/ //const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 iOpt ) const { const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 ) const { xbMdxTag *mpTag = (xbMdxTag *) vpTag; return mpTag->cTagName; } /***********************************************************************/ xbString &xbIxMdx::GetTagName( void *vpTag ) const { xbMdxTag *mpTag = (xbMdxTag *) vpTag; return *mpTag->sTagName; } /***********************************************************************/ void *xbIxMdx::GetTagTblPtr() const { return (void *) mdxTagTbl; } /***********************************************************************/ xbBool xbIxMdx::GetUnique( void *vpTag ) const { //! @brief Determine unique setting for given tag. /*! \param vpTag Tag to retrieve expression from. \returns xbTrue if unique key. */ xbMdxTag *mTag = (xbMdxTag *) vpTag; return mTag->cUnique; } /***********************************************************************/ //! @brief Harvest Empty Node. /*! Harvest empty MDX node and add it to the chain of link nodes \param mpTag Tag to harvest. \param iOpt - 0 Don't write the node info to disk, handled elsewhere (don't write it twice) 1 Write the update into to disk \returns Return Codes */ xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbBool bRootPage = xbFalse; xbInt32 iOffset = 0; try{ if( mpTag->ulRootPage == BlockToPage( npNode->ulBlockNo ) && !bHarvestRoot ){ bRootPage = xbTrue; } memset( npNode->cpBlockData, 0x00, GetBlockSize()); char *pTrg = npNode->cpBlockData; if( !bRootPage ){ pTrg += 4; ePutUInt32( pTrg, ulFirstFreePage ); } if( bRootPage ){ if( mpTag->cHasKeys ){ // std::cout << "setting has keys\n"; mpTag->cHasKeys = 0x00; if(( iRc = xbFseek( ((mpTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if(( iRc = xbFwrite( &mpTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // might need to update left sibling and right sibling here. // Fields don't seem to be updated consistently by other xbase tools, // for now, not updating } } else { // update header // seek to position byte 13 ulFirstFreePage = BlockToPage( npNode->ulBlockNo ); if(( iRc = xbFseek( 36, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } // write it char c4[4]; ePutUInt32( c4, ulFirstFreePage ); if(( iRc = xbFwrite( c4, 4, 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } } if( iOpt == 1 ){ if(( iRc = xbFseek( (xbInt64) ((npNode->ulBlockNo * GetBlockSize() )) + iOffset, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } // write out the block if(( iRc = xbFwrite( npNode->cpBlockData, GetBlockSize(), 1 )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::HarvestEmptyNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Harvest Tag Nodes. /*! Save all nodes for a given tag into the free node chain. Used for reindexing or deleting a given tag. \param mpTag Tag for harvesting nodes \param bHarvestRoot Set to True when deleting tag \returns Return Codes */ xbInt16 xbIxMdx::HarvestTagNodes( xbMdxTag *mpTag, xbBool bHarvestRoot ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbUInt32 ulBlkNo; xbLinkListOrd ll; xbLinkListNode * llN; xbIxNode * n; try{ ll.SetDupKeys( xbFalse ); // clear out any history if( mpTag->npNodeChain ){ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } while( GetNextKey( mpTag, 0 ) == 0 ){ n = mpTag->npNodeChain; while(n){ ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo ); n = n->npNext; } } if( bHarvestRoot ) ll.InsertKey( PageToBlock( mpTag->ulTagHdrPageNo ), 0 ); llN = ll.GetHeadNode(); if(( n = xbIx::AllocateIxNode( GetBlockSize())) == NULL ){ iErrorStop = 100; iRc = XB_NO_MEMORY; throw iRc; } while( llN ){ ulBlkNo = llN->GetKey(); // read in a block for the block number if(( iRc = ReadBlock( ulBlkNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // harvest it n->ulBlockNo = ulBlkNo; if(( iRc = HarvestEmptyNode( mpTag, n, 1, bHarvestRoot )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } llN = llN->GetNextNode(); } n = FreeNodeChain( n ); mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain ); mpTag->npCurNode = NULL; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::HarvestTagNodes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Insert key into interior node. /*! Insert key into non-full interior node.
Assumes valid inputs \param vpTag Tag in play. \param npNode Node for insertion. \param iSlotNo Slot number to insert key. \param ulPtr Page number to insert. \returns Return Codes */ xbInt16 xbIxMdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char *pNewKeyPos; char *pTrg; char *pLastKey = NULL; xbMdxTag * npTag; npTag = (xbMdxTag *) vpTag; xbInt16 iCopyLen; xbInt16 iNewKeyPos = 8; try{ xbInt32 lKeyCnt = GetKeyCount( npNode ); iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); char *pSrc = npNode->cpBlockData; if( iSlotNo < lKeyCnt ) iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) + 4; else iCopyLen = 0; xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 12; if( ulRqdBufSize > npNode->ulBufSize ){ npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen; npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize ); if( !npNode->cpBlockData ){ iErrorStop = 100; iRc = XB_NO_MEMORY; throw iRc; } } // if not appending to the end of the node, make some room, move things to the right pNewKeyPos = npNode->cpBlockData; pNewKeyPos += iNewKeyPos; if( iSlotNo < lKeyCnt ){ pTrg = pNewKeyPos; pTrg += npTag->iKeyItemLen; memmove( pTrg, pNewKeyPos, (size_t) iCopyLen ); } // get the right most key for the left part of the split node xbUInt32 ulKeyPtr2; if(( iRc = GetKeyPtr( vpTag, npNode->iCurKeyNo, npNode, ulKeyPtr2 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // get the new right key value for the freshly split node pLastKey = (char *) malloc((size_t) npTag->iKeyLen); if(( iRc = GetLastKeyForBlockNo( vpTag, PageToBlock( ulKeyPtr2 ), pLastKey )) != XB_NO_ERROR ){ iRc = 120; throw iRc; } // write the key values pTrg = pNewKeyPos; pTrg += 4; pSrc = pLastKey; for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) *pTrg++ = *pSrc++; pTrg = pNewKeyPos; //pTrg+= npTag->iKeyItemLen - 4; pTrg+= npTag->iKeyItemLen; ePutUInt32( pTrg, ulPtr); ePutInt32( npNode->cpBlockData, ++lKeyCnt ); // write out the updated block to disk if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if( pLastKey ) free( pLastKey ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::InsertNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( pLastKey ) free( pLastKey ); } return iRc; } /***********************************************************************/ //! @brief Insert key into leaf node. /*! Insert key into non-full leaf node.
Assumes valid inputs \param vpTag Tag in play. \param npNode Node for insertion. \param iSlotNo Slot number to insert key. \param ulPtr Pointer number to insert. \returns Return Codes */ xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ // format of block data is // 4 bytes number of keys on block // 4 bytes - next free block or split block num // repeating // 4 bytes record number // x bytes key data // Special processing note: when splitting node, new key is first inserted into full left node before // the node is split. This routine will make additional room in the buffer for that scenario xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char *pNewKeyPos; // pointer to position in record for new key composite char *pTrg; xbInt16 iNewKeyPos = 8; // position in data block where new key begins. // is the position of the record number, where the fmt is // [four byte rec number][actual key data] repeats xbMdxTag * npTag = (xbMdxTag *) vpTag; xbInt16 iCopyLen; try{ xbInt32 lKeyCnt = GetKeyCount( npNode ); iNewKeyPos += (iSlotNo * npTag->iKeyItemLen); // length of number of keys that need to be moved to the right if( iSlotNo < lKeyCnt ) iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen; else iCopyLen = 0; // +8 is to include the first two 4 byte fields in the block xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 8; if( ulRqdBufSize > npNode->ulBufSize ){ npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen; npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize ); // init the newly acquired buffer space char *p = npNode->cpBlockData; p += (npNode->ulBufSize - (xbUInt32) npTag->iKeyItemLen); memset( p, 0x00, (size_t) npTag->iKeyItemLen ); if( !npNode->cpBlockData ){ iErrorStop = 100; iRc = XB_NO_MEMORY; throw iRc; } } // if not appending to end, move things right pNewKeyPos = npNode->cpBlockData; pNewKeyPos += iNewKeyPos; if( iSlotNo < lKeyCnt ) { pTrg = pNewKeyPos; pTrg += npTag->iKeyItemLen; memmove( pTrg, pNewKeyPos, (size_t) iCopyLen ); } // write rec number ePutUInt32( pNewKeyPos, ulPtr ); // write the key value pTrg = pNewKeyPos; pTrg += 4; char * pSrc = cpKeyBuf; for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) *pTrg++ = *pSrc++; // update number of keys on the node ePutInt32( npNode->cpBlockData, ++lKeyCnt ); // determine length of node, zap everything to the right of it xbUInt32 iStartPos = 8 + ((xbUInt32) lKeyCnt * (xbUInt32) npTag->iKeyItemLen ); xbUInt32 iClearLen = npNode->ulBufSize - iStartPos; char *p = npNode->cpBlockData; p += iStartPos; memset( p, 0x00, iClearLen ); // write out the updated block to disk if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::InsertNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ inline xbBool xbIxMdx::IsLeaf( void *vpTag, xbIxNode *npNode ) const{ // for performance reasons, does no data checking // will result in potentially hard to find segfaults if passing invalid npNode xbMdxTag *mTag = (xbMdxTag *) vpTag; char *p = npNode->cpBlockData; xbInt32 lNoOfKeys = eGetInt32( p ); // mdx interior nodes have a sibling number to the right of the right most key in the node p+=8; p+= mTag->iKeyItemLen * lNoOfKeys; if( eGetUInt32( p ) == 0 ){ // std::cout << "leaf node\n"; return true; } else { // std::cout << "interior node\n"; return false; } } /***********************************************************************/ xbInt16 xbIxMdx::KeyExists( void * vpTag ) { // this method assumes the key has already been built xbMdxTag *mpTag = (xbMdxTag *) vpTag; xbInt16 iRc = FindKey( vpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 ); if( iRc == 0 ) return 1; else return 0; } /***********************************************************************/ //! @brief Set position for key add. /*! This routine is called by the AddKey() method and is used to position the node chain to the position the new key should be added to the index. \param npTag Pointer to npTag. \param ulAddRecNo Record number to add. \returns Return Codes */ xbInt16 xbIxMdx::KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddRecNo ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ iRc = FindKey( mpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 ); if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) return XB_NO_ERROR; // good position else if( iRc != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } // get here if key was found, get the right most instance of any non unique key for append, find correct spot for update if( GetUnique( mpTag ) == 0 ){ xbUInt32 ulCurRecNo; if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } xbBool bKeysMatch = xbTrue; while( bKeysMatch && ulAddRecNo > ulCurRecNo && iRc == XB_NO_ERROR ){ if(( iRc = GetNextKey( mpTag, 0 )) == XB_NO_ERROR ){ if( memcmp( GetKeyData( mpTag->npCurNode, mpTag->npCurNode->iCurKeyNo, mpTag->iKeyItemLen ), mpTag->cpKeyBuf, (size_t) mpTag->iKeyLen )) bKeysMatch = xbFalse; else{ if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } } } } if( iRc == XB_EOF ){ // eof condition if(( iRc = GetLastKey( 0, mpTag, 0 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } mpTag->npCurNode->iCurKeyNo++; return XB_NO_ERROR; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::KeySetPosAdd() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Set position for key delete. /*! This routine is called by the DeleteKey() method and is used to position the node chain to the position the old key should be deleted from the index. \param npTag Pointer to npTag. \returns Return Codes */ xbInt16 xbIxMdx::KeySetPosDel( xbMdxTag *npTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbString sMsg; try{ iRc = FindKey( npTag, npTag->cpKeyBuf2, npTag->iKeyLen, 0 ); if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) return XB_NO_ERROR; // good pos ition else if( iRc != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } xbUInt32 ulIxRecNo; if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } if( ulIxRecNo == dbf->GetCurRecNo()) return XB_NO_ERROR; if( GetUnique( npTag ) == 1 ){ iErrorStop = 120; iRc = XB_NOT_FOUND; throw iRc; } xbBool bFound = xbFalse; xbBool bKeysMatch = xbTrue; while( bKeysMatch && !bFound && iRc == XB_NO_ERROR ){ if(( iRc = GetNextKey( npTag, 0 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){ bKeysMatch = xbFalse; } else { if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } if( ulIxRecNo == dbf->GetCurRecNo()) bFound = xbTrue; } } if( bFound ) return XB_NO_ERROR; else return XB_NOT_FOUND; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::KeySetPosDel() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Returns key update status. /*! \param vpTag Tag to check status on. \returns xbtrue - Key was updated.
xbFalse - Key not updated. */ /* inline xbBool xbIxMdx::KeyFiltered( void *vpTag ) const{ xbMdxTag *mpTag = (xbMdxTag *) vpTag; return mpTag->bKeyFiltered; } */ /***********************************************************************/ xbInt16 xbIxMdx::LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ){ // option 1 - Load the entire tag detail // option 2 - Load the dynamic variables only xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; size_t iReadSize; char *buf = NULL; char *p; try{ // set the read length based on the option if( iOption == 1 ) iReadSize = 1024; else if( iOption == 2 ) // iReadSize = 4; iReadSize = 260; else{ iRc = XB_INVALID_OPTION; iErrorStop = 100; throw iRc; } if(( buf = (char *) calloc( 1, (size_t) iReadSize )) == NULL ){ iErrorStop = 110; iRc = XB_NO_MEMORY; throw iRc; } if(( iRc = ReadBlock( tte->ulTagHdrPageNo,(xbUInt32) (GetBlockSize() / (xbUInt16) iBlockFactor), iReadSize, buf )) != XB_NO_ERROR ){ free( buf ); iErrorStop = 30; throw iRc; } p = buf; tte->ulRootPage = eGetUInt32( p ); if( iOption == 1 ){ p+=4; tte->ulTagSize = eGetUInt32( p ); p+=4; tte->cKeyFmt2 = *p; p++; tte->cKeyType2 = *p; p+=3; tte->iKeyLen = eGetInt16( p ); p+=2; tte->iKeysPerBlock = *p; p+=2; tte->iSecKeyType = eGetInt16( p ); p+=2; tte->iKeyItemLen = eGetInt16( p ); p+=2; tte->cSerialNo = *p; p+=3; tte->cUnique = *p; p++; // next line assumes expression is a null terminated string in the block tte->sKeyExp = new xbString(); tte->sKeyExp->Sprintf( "%s", p ); p+=221; tte->cHasFilter = *p; p+=1; tte->cHasKeys = *p; p+=2; tte->ulLeftChild = eGetUInt32( p ); p+=4; tte->ulRightChild = eGetUInt32( p ); p+=5; tte->cTagYY = *p; p++; tte->cTagMM = *p; p++; tte->cTagDD = *p; // p+=223; p+=221; tte->cKeyFmt3 = *p; if( tte->cHasFilter ){ p+=282; tte->sFiltExp = new xbString(); tte->sFiltExp->Sprintf( "%s", p ); tte->filter = new xbExp( xbase, dbf ); if(( iRc = tte->filter->ParseExpression( tte->sFiltExp->Str())) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } tte->npNodeChain = NULL; tte->npCurNode = NULL; tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 ); tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 ); tte->exp = new xbExp( xbase, dbf ); if(( iRc = tte->exp->ParseExpression( tte->sKeyExp->Str() )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } } else if( iOption == 2 ){ // refresh the dynamic tag variables p+=4; tte->ulTagSize = eGetUInt32( p ); p+= 16; tte->cSerialNo = *p; p+= 226; tte->cHasKeys = *p; p+=2; tte->ulLeftChild = eGetUInt32( p ); p+=4; tte->ulRightChild = eGetUInt32( p ); p+=5; tte->cTagYY = *p; p++; tte->cTagMM = *p; p++; tte->cTagDD = *p; } if( buf ) free( buf ); } catch (xbInt16 iRc ){ if( buf ) free( buf ); xbString sMsg; sMsg.Sprintf( "xbIxMdx::LoadTagDetail() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ xbInt16 xbIxMdx::LoadTagTable() { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char * buf = NULL; //std::cout << "xbIxMdx::LoadTagTable() tag use cnt = " << iTagUseCnt << "\n"; try{ if( iTagUseCnt > 46 ){ iErrorStop = 100; iRc = XB_INVALID_INDEX; throw iRc; } xbInt16 iBufSize = (xbInt16) iTagLen * iTagUseCnt; if(( buf = (char *) malloc( (size_t) iBufSize )) == NULL ){ iErrorStop = 110; iRc = XB_NO_MEMORY; throw iRc; } if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if(( iRc = xbFread( buf, (size_t) iBufSize, 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } xbInt16 iPos; char *p; xbMdxTag *tte; xbMdxTag *ttel = NULL; for( xbInt16 i = 0; i < iTagUseCnt; i++ ){ iPos = i * iTagLen; p = buf + iPos; if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){ iErrorStop = 140; iRc = XB_NO_MEMORY; throw iRc; } // set the current tag to the first tag in the table if( !vpCurTag ) xbIx::SetCurTag( (void *) tte ); if( mdxTagTbl ) ttel->next = tte; else mdxTagTbl = tte; ttel = tte; tte->next = NULL; tte->ulTagHdrPageNo = eGetUInt32( p ); p += 4; for( xbUInt32 i = 0; i < 11; i ++ ) tte->cTagName[i] = *p++; tte->cTagName[11] = 0x00; tte->cKeyFmt = *p++; tte->cLeftChild = *p++; tte->cRightChild = *p++; tte->cParent = *p++; tte->c2 = *p++; tte->cKeyType = *p; tte->sTagName = new xbString(); tte->sTagName->Set( tte->cTagName ); tte->sTagName->Trim(); if(( iRc = LoadTagDetail( 1, tte )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } } if( buf ) free( buf ); } catch (xbInt16 iRc ){ if( buf ) free( buf ); xbString sMsg; sMsg.Sprintf( "xbIxMdx::LoadTagTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); if( iErrorStop == 100 ){ sMsg.Sprintf( "xbIxMdx::LoadTagTable() Invalid Tag Count: %d", iTagUseCnt ); xbase->WriteLogMessage( sMsg.Str()); } xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /**************************************************************************************************************/ //! @brief Calculate the block number for a given page. /*! This routine is called by any function needing to calculate the block number for a given page. Page numbers are stored internally in the physical file, and the library reads and writes in blocks of one or more pages. Assumes valid data input \param ulPageNo Page Number \returns Calculated block number. */ inline xbUInt32 xbIxMdx::PageToBlock( xbUInt32 ulPageNo ){ return ulPageNo / (xbUInt32) iBlockFactor; } /**************************************************************************************************************/ #ifdef XB_DEBUG_SUPPORT xbInt16 xbIxMdx::PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ){ xbString sPre; sPre.Sprintf( "%c ", cType ); for( xbInt16 i = 0; i < iDepth; i++ ) sPre += "|"; xbString sPost; sPost.Sprintf( "\tThisBlock=[%ld] KeyNo=[%d] Depth=[%d]", npNode->ulBlockNo, iKeyNo, iDepth ); xbMdxTag * mpTag = (xbMdxTag *) vpTag; char *p = npNode->cpBlockData + (8 + (iKeyNo * mpTag->iKeyItemLen )); xbString sKeyPtr; xbUInt32 ulNoOfKeys = 0; if( cType == 'I' ) { // interior sKeyPtr.Sprintf( " ptr=[%ld]", eGetUInt32( p )); ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData ); } else if( cType == 'L' ) // leaf sKeyPtr.Sprintf( " rec=[%ld]", eGetUInt32( p )); p += 4; xbString s; if(( cType == 'I' && iKeyNo < (xbInt16) ulNoOfKeys) || cType == 'L' ){ if( mpTag->cKeyType2 == 'C' ){ //CHAR for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ ) s += *p++; } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC xbBcd bcd( p ); xbString s2; bcd.ToString( s2 ); s += s2; } else if( mpTag->cKeyType2 == 'D' ){ // DATE xbInt32 lDate = (xbInt32) eGetDouble( p ); xbDate d( lDate ); //xbString s2; //d.JulToDate8( lDate, s2 ); s.Sprintf( "%s%s", s.Str(), d.Str()); } } else { s = "Rightmost InteriorNode Pointer"; } xbString sOut( sPre ); sOut += s; sOut += sPost; sOut += sKeyPtr; xbase->WriteLogMessage( sOut, iOutputOpt ); return XB_NO_ERROR; } #endif /***********************************************************************/ //! @brief ReadHeadBlock. /*! Read values off head block in MDX file \param iOpt 0 - Read entire block, initialize as needed.
1 - Read in only dynamic section of block
\returns Return Codes */ xbInt16 xbIxMdx::ReadHeadBlock( xbInt16 iOpt ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ if( !FileIsOpen()){ iRc = XB_NOT_OPEN; iErrorStop = 100; throw iRc; } char sBuf[48]; memset( sBuf, 0x00, 48 ); if( iOpt == 0 ){ if(( iRc = xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } if(( iRc = xbFread( sBuf, 47, 1 )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } else { if(( iRc = xbFseek( 28, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if(( iRc = xbFread( sBuf, 19, 1 )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } } char *p = sBuf; if( iOpt == 0 ){ cVersion = *p++; cCreateYY = *p++; cCreateMM = *p++; cCreateDD = *p++; sFileName.Assign( p, 1, 16 ); p+=16; iBlockFactor = eGetInt16( p ); p+=2; SetBlockSize( (xbUInt32) eGetInt16( p )); p+=2; cProdIxFlag = *p++; cTagEntryCnt = *p++; iTagLen = *p; p+=2; iTagUseCnt = eGetInt16( p ); //lTagUseCnt = eGetInt32( p ); //p+=4; p+=2; cNextTag = *p++; c1B = *p++; ulPageCnt = eGetUInt32( p ); p+=4; ulFirstFreePage = eGetUInt32( p ); p+=4; ulNoOfBlockAvail = eGetUInt32( p ); p+=4; cUpdateYY = *p++; cUpdateMM = *p++; cUpdateDD = *p; if( cNodeBuf ) free( cNodeBuf ); if(( cNodeBuf = (char *) malloc( (size_t) GetBlockSize())) == NULL ){ iErrorStop = 150; throw XB_NO_MEMORY; } if(( iRc = xbIxMdx::LoadTagTable()) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } } else { iTagUseCnt = eGetInt16( p ); p+=4; ulPageCnt = eGetUInt32( p ); p+=4; ulFirstFreePage = eGetUInt32( p ); p+=4; ulNoOfBlockAvail = eGetUInt32( p ); p+=4; cUpdateYY = *p++; cUpdateMM = *p++; cUpdateDD = *p; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::ReadHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( cNodeBuf ) free( cNodeBuf ); } return iRc; } /***********************************************************************/ //! @brief Reindex /*! Reindex specifed tag or all tags \param **vpTag &tag - Tag to reindex.
NULL - Reindex all tags
\returns Return Codes If this method fails, the index is left in an undefined state */ xbInt16 xbIxMdx::Reindex( void **vpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag * mpTag; #ifdef XB_LOCKING_SUPPORT xbBool bLocked = xbFalse; #endif if( vpTag ) mpTag = (xbMdxTag *) *vpTag; else mpTag = NULL; struct tagInfo{ xbBool bUnique; xbBool bDesc; char sTagName[11]; xbString *sKeyExp; xbString *sFiltExp; tagInfo *next; }; tagInfo *ti = NULL; try{ #ifdef XB_LOCKING_SUPPORT if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } bLocked = xbTrue; } #endif if( mpTag == NULL ){ // do all tags xbMdxTag *tt = mdxTagTbl; tagInfo *pHead = NULL; tagInfo *pEnd = NULL; if( tt ){ while( tt ){ ti = (tagInfo *) calloc( 1, sizeof( tagInfo )); ti->bUnique = tt->cUnique ? 1 : 0; ti->bDesc = (((tt->cKeyFmt2 & 0x08) > 0) ? 1 : 0); memcpy( ti->sTagName, tt->cTagName, 11 ); ti->sKeyExp = new xbString( tt->sKeyExp->Str()); if( tt->cHasFilter ) ti->sFiltExp = new xbString( tt->sFiltExp->Str()); else ti->sFiltExp = new xbString( "" ); if( !pHead ) pHead = ti; else pEnd->next = ti; pEnd = ti; tt = tt->next; } } // get the file name and save it xbString sMdxFileName = GetFqFileName(); // close the mdx file if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // delete the file xbRemove(); // init variables Init(); tagInfo *p = pHead; tagInfo *pDel; // create new file & add the tags while( p ){ if(( iRc = CreateTag( p->sTagName, p->sKeyExp->Str(), p->sFiltExp->Str(), p->bDesc, p->bUnique, xbTrue, vpTag )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } delete p->sKeyExp; delete p->sFiltExp; pDel = p; p = p->next; free( pDel ); } } else { if(( iRc = HarvestTagNodes( mpTag )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } } xbUInt32 ulRecCnt = 0; if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ iErrorStop = 140; throw iRc; } xbInt16 iCurTag = 0; xbBool bDone = xbFalse; for( xbUInt32 ulRec = 1; ulRec <= ulRecCnt; ulRec++ ){ if(( iRc = dbf->GetRecord( ulRec )) != XB_NO_ERROR ){ iErrorStop = 150; throw iRc; } bDone = xbFalse; iCurTag = 0; if( !vpTag ) mpTag = (xbMdxTag *) GetTag( iCurTag++ ); while( !bDone ){ // do the tag things // CreateKey if(( iRc = CreateKey( mpTag, 1 )) != XB_NO_ERROR ){ iErrorStop = 160; throw iRc; } if( mpTag->iKeySts == XB_ADD_KEY ){ if( mpTag->cUnique ){ if(( iRc = CheckForDupKey( mpTag )) != XB_NO_ERROR ){ iErrorStop = 170; throw iRc; } } if(( iRc = AddKey( mpTag, ulRec )) != XB_NO_ERROR ){ iErrorStop = 180; throw iRc; } } if( vpTag || iCurTag >= GetTagCount()) bDone = xbTrue; else mpTag = (xbMdxTag *) GetTag( iCurTag++ ); } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::ReIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); this->DeleteTag( mpTag ); } #ifdef XB_LOCKING_SUPPORT if( bLocked ){ dbf->LockTable( XB_UNLOCK ); } #endif return iRc; } /***********************************************************************/ xbInt16 xbIxMdx::SetCurTag( xbString &sTagName ) { xbMdxTag *tt = (xbMdxTag *) GetTag( sTagName ); if( tt ){ xbIx::SetCurTag((void *) tt ); return XB_NO_ERROR; } else return XB_INVALID_TAG; } /***********************************************************************/ xbInt16 xbIxMdx::SetCurTag( xbInt16 iTagNo ) { xbMdxTag *tt = (xbMdxTag *) GetTag( iTagNo ); if( tt ){ xbIx::SetCurTag((void *) tt ); return XB_NO_ERROR; } else return XB_INVALID_TAG; } /***********************************************************************/ //! @brief SetReuseEmptyNode switch setting. /*! \param bEmptyNodeSw xbFalse - Do not reuse empty MDX index nodes (Dbase 6. behavior). xbTrue - Reuse empty MDX index nodes. */ void xbIxMdx::SetReuseEmptyNodesSw( xbBool bEmptyNodesSw ) { bReuseEmptyNodes = bEmptyNodesSw; } /***********************************************************************/ //! @brief Split an interior node /*! This routine splits an interior node into two nodes, divided by dSplitFactor.
This behaves differently than V7 Dbase. V7 does not balance the nodes.
For V7, if adding a key to the end of a node, it will create a right node with only one key, and the left node is still full.

Possible performance improvement options.
Two modes when splitting:
a) Split nodes in the middle - good for random access applications
b) Split off right node with only one key - good for applications with expectation of ascending keys added moving forward.
This routine first inserts the key into the left node in the appropriate location then splits the node based on the split factor setting. \param vpTag Tag in play. \param npLeft Left node to split. \param npRight Right node to split. \param iSlotNo Slot number for split. \param ulPtr Pointer number to insert. \returns Return Codes */ xbInt16 xbIxMdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 ulPtr ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag * npTag = (xbMdxTag *) vpTag; xbDouble dSplitFactor = .5; // split the nodes 50/50 xbInt16 iLen; char *pSrc; char *pTrg; try{ xbInt32 lKeyCnt = GetKeyCount( npLeft ); xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor); xbInt32 lNewRightKeyCnt = lKeyCnt - lNewLeftKeyCnt; if(( iRc = InsertNodeI( vpTag, npLeft, iSlotNo, ulPtr )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } // move the right half of the left node to the right node pSrc = npLeft->cpBlockData; pSrc += 8 + ((lNewLeftKeyCnt+1) * npTag->iKeyItemLen); pTrg = npRight->cpBlockData; pTrg += 8; iLen = (lNewRightKeyCnt * npTag->iKeyItemLen) + 4; memmove( pTrg, pSrc, (size_t) iLen ); // eliminate chattle on the right iLen = 12 + (lNewLeftKeyCnt * npTag->iKeyItemLen); pSrc = npLeft->cpBlockData; pSrc += iLen; memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen ); // write the new key counts into the nodes pTrg = npLeft->cpBlockData; ePutInt32( pTrg, lNewLeftKeyCnt ); pTrg = npRight->cpBlockData; ePutInt32( pTrg, lNewRightKeyCnt ); // write out the block if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // write out the block if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::SplitNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Split a leaf node. /*! This routine splits an index leaf into two nodes, divided by dSplitFactor.
Possible performance improvement options.
Two modes when splitting:
a) Split nodes in the middle - good for random access applications
b) Split off right node with only one key - good for applications with expectation of ascending keys added moving forward.
This routine first inserts the key into the left node in the appropriate location then splits the node based on the split factor setting. \param vpTag Tag in play. \param npLeft Left node to split. \param npRight Right node to split. \param iSlotNo Slot number for split. \param ulPtr Pointer number to insert. \returns Return Codes */ xbInt16 xbIxMdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbDouble dSplitFactor = .5; // can adjust performance with this number xbMdxTag *mpTag = (xbMdxTag *) vpTag; xbString sMsg; xbInt16 iLen; char *pSrc; char *pTrg; // std::cout << "In xbIxMdx::SplitNodeL()\n"; try{ xbInt32 lKeyCnt = GetKeyCount( npLeft ); xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt; if(( iRc = InsertNodeL( vpTag, npLeft, iSlotNo, cpKeyBuf, ulPtr )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } // move right half off of left node to the right node pSrc = npLeft->cpBlockData; pSrc += 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen); pTrg = npRight->cpBlockData; pTrg += 8; iLen = lNewRightKeyCnt * mpTag->iKeyItemLen; memmove( pTrg, pSrc, (size_t) iLen ); // write the new key counts into the nodes pTrg = npLeft->cpBlockData; ePutInt32( pTrg, lNewLeftKeyCnt ); pTrg = npRight->cpBlockData; ePutInt32( pTrg, lNewRightKeyCnt ); // zero out the next key number so this node is not confused with interior node iLen = 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen); pSrc = npLeft->cpBlockData; pSrc += iLen; memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen ); // write out the left block if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } // write out the right block if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::SplitNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /**************************************************************************************************************/ //! @brief TagSerialNo. /*! This routine is used internally for reading or updating the serial number on a given tag when the tag. \param iOption 1 - Read tag serial number off disk, save in structure
2 - Write serial number from memory to disk
3 - Read serial number off disk, increment, write updated number to disk
mpTag - Pointer to tag for serial number update \returns Return Codes */ xbInt16 xbIxMdx::TagSerialNo( xbInt16 iOption, xbMdxTag * mpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ xbInt64 lPos = (mpTag->ulTagHdrPageNo * 512) + 20; if( iOption != 2 ){ if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if(( iRc = xbFgetc( mpTag->cSerialNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } if( iOption == 3 ) mpTag->cSerialNo++; if( iOption != 1 ){ if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if(( iRc = xbFwrite( &mpTag->cSerialNo, 1, 1 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::UpdateSerialNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief UpdateTagKey /*! This routine updates a key or a given tag. The file header is considered to be the first 2048 bytes in the file. \param cAction A - Add a key.
D - Delete a key.
R - Revise a key.
\param vpTg - Pointer to tag.
\param ulRecNo - Record number association with the action.
\returns Return Codes */ xbInt16 xbIxMdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbMdxTag *npTag = (xbMdxTag *) vpTag; try{ // save off any needed fields for updating xbUInt32 ulTagSizeSave = npTag->ulTagSize; if( cAction == 'D' || cAction == 'R' ){ // std::cout << "UpdateTagKey-delete going to DeleteKey \n"; if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } if( cAction == 'A' || cAction == 'R' ){ if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } if( ulTagSizeSave != npTag->ulTagSize ){ if(( iRc = UpdateTagSize( npTag, npTag->ulTagSize )) != XB_NO_ERROR) { iErrorStop = 120; throw iRc; } } // update the serial number if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::UpdateTagKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /**************************************************************************************************************/ //! @brief Write head block. /*! This routine updates the MDX file header and commits changes to disk. The file header is considered to be the first 2048 bytes in the file. \param iOption 0 - Entire 2048 byte header, used for creating a new mdx file.
1 - Bytes 28 through 46, used when adding or deleting a tag.
2 - Bytes 32 through 46, used after updating keys in the file. \returns Return Codes */ xbInt16 xbIxMdx::WriteHeadBlock( xbInt16 iOption ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ xbDate d; d.Sysdate(); // set to system date, today cUpdateYY = (char) d.YearOf() - 1900; cUpdateMM = (char) d.MonthOf(); cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); if( iOption > 0 ){ char buf[48]; memset( buf, 0x00, 48 ); xbUInt32 ulStartPos = 0; xbUInt32 ulLen = 0; if( iOption == 1 ){ ePutInt16( &buf[28], iTagUseCnt ); buf[30] = cNextTag; buf[31] = 0x1b; ulStartPos = 28; ulLen = 19; } else { ulStartPos = 32; ulLen = 16; } ePutUInt32( &buf[32], ulPageCnt ); ePutUInt32( &buf[36], ulFirstFreePage ); ePutUInt32( &buf[40], ulNoOfBlockAvail ); buf[44] = cUpdateYY; buf[45] = cUpdateMM; buf[46] = cUpdateDD; if(( iRc = xbFseek( ulStartPos, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if(( iRc = xbFwrite( &buf[ulStartPos], ulLen, 1 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } else if( iOption == 0 ){ char buf[2048]; memset( buf, 0x00, 2048 ); buf[0] = cVersion; cCreateYY = cUpdateYY; cCreateMM = cUpdateMM; cCreateDD = cUpdateDD; buf[1] = cCreateYY; buf[2] = cCreateMM; buf[3] = cCreateDD; for( xbUInt32 l = 0; l < sFileName.Len() && l < 10; l++ ){ buf[l+4] = sFileName[l+1]; } ePutInt16( &buf[20], iBlockFactor ); ePutInt16( &buf[22], (xbInt16) GetBlockSize() ); buf[24] = cProdIxFlag; buf[25] = cTagEntryCnt; ePutInt16 ( &buf[26], iTagLen ); ePutInt16 ( &buf[28], iTagUseCnt ); buf[30] = cNextTag; buf[31] = c1B; ePutUInt32( &buf[32], ulPageCnt ); ePutUInt32( &buf[36], ulFirstFreePage ); ePutUInt32( &buf[40], ulNoOfBlockAvail ); buf[44] = cUpdateYY; buf[45] = cUpdateMM; buf[46] = cUpdateDD; // not sure what the following "1" is for in a sea of zeroes.... // maybe it's current tag or default tag or something along those lines? buf[529] = 0x01; xbRewind(); if(( iRc = xbFwrite( buf, 2048, 1 )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } } else { iErrorStop = 130; iRc = XB_INVALID_OPTION; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::WriteHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] option = [%d]", iErrorStop, iRc, iOption ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ xbInt16 xbIxMdx::UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char buf[4]; try{ ePutUInt32( &buf[0], ulTagSz ); if(( iRc = xbFseek( (xbInt64) ((mpTag->ulTagHdrPageNo *512 )+ 4), SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if(( iRc = xbFwrite( &buf[0], 4, 1 )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxMdx::UpdateTagSize() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //void xbIxMdx::TestStub( char *s, void *vpTag ){ void xbIxMdx::TestStub( char *, void * ){ } /***********************************************************************/ } /* namespace */ #endif /* XB_MDX_SUPPORT */