/* xbixndx.cpp XBase64 Software Library Copyright (c) 1997,2003,2014,2022 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 */ #include "xbase.h" #ifdef XB_NDX_SUPPORT namespace xb{ /***********************************************************************/ //! @brief Class constructor. /*! \param dbf Pointer to dbf instance. */ xbIxNdx::xbIxNdx( xbDbf *dbf ) : xbIx( dbf ){ ndxTag = (xbNdxTag *) calloc( 1, sizeof( xbNdxTag )); SetBlockSize( XB_NDX_BLOCK_SIZE ); cNodeBuf = (char *) malloc( XB_NDX_BLOCK_SIZE ); } /***********************************************************************/ //! @brief Class Destructor. xbIxNdx::~xbIxNdx(){ if( ndxTag ){ ndxTag->npNodeChain = FreeNodeChain( ndxTag->npNodeChain ); if( ndxTag->cpKeyBuf ) free( ndxTag->cpKeyBuf ); if( ndxTag->cpKeyBuf2 ) free( ndxTag->cpKeyBuf2 ); if( ndxTag->exp ){ delete ndxTag->exp; ndxTag->exp = NULL; } ndxTag->sKeyExpression.Set( NULL ); ndxTag->sTagName.Set( NULL ); free( ndxTag ); ndxTag = NULL; } if( cNodeBuf ) free( cNodeBuf ); } /***********************************************************************/ //! @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 xbIxNdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts ) return XB_NO_ERROR; xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbInt16 iHeadNodeUpdateOpt = 2; try{ if(( iRc = xbIxNdx::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 if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } } else { // land here with a full leaf node iHeadNodeUpdateOpt = 1; // section B - split the leaf node xbIxNode * npRightNode = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 ); if( !npRightNode ){ iErrorStop = 100; iRc = XB_NO_MEMORY; throw iRc; } if(( iRc = SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 110; 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 ){ npRightNode = FreeNodeChain( npRightNode ); npRightNode = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 ); if( !npRightNode ){ iErrorStop = 200; iRc = XB_NO_MEMORY; throw iRc; } if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, ulTempBlockNo )) != XB_NO_ERROR ){ iErrorStop = 210; throw iRc; } ulTempBlockNo = npRightNode->ulBlockNo; npTag->npCurNode = npParent; npParent = npParent->npPrev; } // section D - if cur node is split root, create new root if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock ){ // xbase->WriteLogMessage( "Section d" ); if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){ iErrorStop = 300; throw iRc; } npRightNode = FreeNodeChain( npRightNode ); } else { // else section E, put key in parent if(( iRc = InsertNodeI( vpTag, npParent, npParent->iCurKeyNo, npRightNode->ulBlockNo )) != XB_NO_ERROR ){ iErrorStop = 400; throw iRc; } npRightNode = FreeNodeChain( npRightNode ); } } // update the header if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } // ---- free whatever is left of the node chain here, this might not be right, might need to restore it to // the point right after SetKeyPosAdd npTag->npNodeChain = FreeNodeChain( ndxTag->npNodeChain ); npTag->npCurNode = NULL; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::AddKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Add new root node. /*! \param npTag Tag to update. \param npLeft Left node. \param npRight Right node. \returns Return Codes */ xbInt16 xbIxNdx::AddKeyNewRoot( xbNdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbString sMsg; char *pLastKey = NULL; try{ xbIxNode *npRoot = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 ); if( !npRoot ){ iErrorStop = 100; iRc = XB_NO_MEMORY; throw iRc; } npTag->ulRootBlock = npRoot->ulBlockNo; pLastKey = (char *) malloc( (size_t) ndxTag->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 += 4; ePutUInt32( pTrg, npLeft->ulBlockNo ); // set the key pTrg+= 8; memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen ); // set the right node number pTrg+= (npTag->iKeyItemLen - 8); ePutUInt32( pTrg, npRight->ulBlockNo ); // write out the block if(( iRc = WriteBlock( npRoot->ulBlockNo, GetBlockSize(), npRoot->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 120; throw iRc; } if( pLastKey ) free( pLastKey ); NodeFree( npRoot ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::AddKeyNewRoot() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( pLastKey ) free( pLastKey ); } return iRc; } /***********************************************************************/ //! @brief Append node to node chain. /*! Append a node to the current node chain for a given tag. \param vpTag Tag to update. \param npNode Node to add to node chain. \returns void */ void xbIxNdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; if( npTag->npNodeChain == NULL ){ npTag->npNodeChain = npNode; npTag->npCurNode = npNode; } else { npNode->npPrev = npTag->npCurNode; npTag->npCurNode->npNext = npNode; npTag->npCurNode = npNode; } // time stamp the node chain GetFileMtime( npTag->tNodeChainTs ); } /***********************************************************************/ //! @brief Allocate a node. /*! \param ulBufSize Buffer size. \param iOpt 0 - Don't update the node block number on the node. 1 - Set node block number to the next available block number. \returns Pointer to new node. */ xbIxNode * xbIxNdx::AllocateIxNode( xbUInt32 ulBufSize, xbInt16 iOpt ){ xbIxNode *n = xbIx::AllocateIxNode( ulBufSize ); if( n && iOpt == 1 ) n->ulBlockNo = ndxTag->ulTotalBlocks++; return n; } /***********************************************************************/ //! @brief Check for duplicate key. /*! \param vpTag Tag to check. \returns XB_KEY_NOT_UNIQUE
XB_NO_ERROR */ xbInt16 xbIxNdx::CheckForDupKey( void *vpTag ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag *npTag = (xbNdxTag *) vpTag; npTag->bFoundSts = xbFalse; try{ if( GetUnique()){ if( npTag->iKeySts == XB_ADD_KEY || npTag->iKeySts == XB_UPD_KEY ) if( KeyExists( vpTag )){ if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){ npTag->bFoundSts = xbTrue; return 0; } else { return XB_KEY_NOT_UNIQUE; } } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); 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 xbIxNdx::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; xbInt16 iAutoLock = xbFalse; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ // xbase->WriteLogMessage( "xbIxNdx::CheckTagIntegrity()", iOpt ); #ifdef XB_LOCKING_SUPPORT iAutoLock = dbf->GetAutoLock(); if( iAutoLock && !dbf->GetTableLocked() ){ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } } #endif memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyLen ); cKeyType = GetKeyType( vpTag ); sMsg.Sprintf( "Checking index type [%c]", cKeyType ); xbase->WriteLogMessage( sMsg, iOpt ); pPrevKeyBuf = (char *) calloc( 1, (size_t) ndxTag->iKeyLen ); // for each key in the index, make sure it is trending in the right direction iRc = GetFirstKey( vpTag, 0 ); 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 ){ sMsg.Sprintf( "Key sequence error at key number [%ld].", ulIxCnt ); xbase->WriteLogMessage( sMsg, iOpt ); iErrorStop = 100; iRc = XB_INVALID_INDEX; throw iRc; } ulThisRecNo = 0; if(( iRc3 = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulThisRecNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc3; } if( iRc2 == 0 && (ulThisRecNo <= ulPrevRecNo )){ sMsg.Sprintf( "Dbf record number sequence error at key number [%ld].", iOpt ); xbase->WriteLogMessage( sMsg, iOpt ); iErrorStop = 120; 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; } } // verify the index count matches the tag count xbUInt32 ulDbfCnt = 0; if(( iRc = dbf->GetRecordCnt( ulDbfCnt )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && GetUnique( vpTag )){ // Can't compare counts if using XB_EMULATE_DBASE and it's a unique index } else { if( ulDbfCnt != ulIxCnt ){ sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] does not match dbf record count [%ld]", ulIxCnt, ulDbfCnt ); xbase->WriteLogMessage( sMsg, iOpt ); iErrorStop = 140; iRc = XB_INVALID_INDEX; throw iRc; } // 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(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){ ulThisRecNo = j; iErrorStop = 160; throw iRc; } } sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld]", ulIxCnt, ulDbfCnt ); xbase->WriteLogMessage( sMsg, iOpt ); } #ifdef XB_LOCKING_SUPPORT if( iAutoLock && !dbf->GetTableLocked() ){ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } } #endif if( pPrevKeyBuf ) free( pPrevKeyBuf ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg, iOpt ); xbase->WriteLogMessage( GetErrorMessage( iRc ), iOpt ); if( pPrevKeyBuf ) free( pPrevKeyBuf ); if( iErrorStop == 160 ){ sMsg.Sprintf( "xbIxNdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo ); xbase->WriteLogMessage( sMsg, iOpt ); } } return iRc; } /***********************************************************************/ //! @brief Create key for tag. /*! Append a node to the current node chain for a given tag. \param vpTag Tag to create key for. \param iOpt 0 = Build a key for FindKey usage, only rec buf 0.
1 = Append Mode, Create key for an append, only use rec buf 0, set updated switch.
2 = Update Mode, Create old version and new version keys, check if different, set update switch appropriately. \returns Return Codes */ xbInt16 xbIxNdx::CreateKey( void * vpTag, xbInt16 iOpt ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){ iErrorStop = 10; 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 ); memcpy( npTag->cpKeyBuf, &d, 8 ); } else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){ xbDouble d; npTag->exp->GetNumericResult( d ); memcpy( npTag->cpKeyBuf, &d, 8 ); } npTag->iKeySts = 0; if( iOpt == 1 ) npTag->iKeySts = XB_ADD_KEY; else if( iOpt == 2 ){ if(( iRc = npTag->exp->ProcessExpression( 1 )) != XB_NO_ERROR ){ iErrorStop = 20; 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 || npTag->exp->GetReturnType() == XB_EXP_DATE ){ xbDouble d; npTag->exp->GetNumericResult( d ); memcpy( npTag->cpKeyBuf2, &d, 8 ); } if( memcmp( npTag->cpKeyBuf, npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )) npTag->iKeySts = XB_UPD_KEY; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::CreateKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Create new tag. /*! This routine creates a new tag. Since NDX files have only one tag, this creates a new NDX file. \param sName Tag Name, including .NDX suffix \param sKey Key Expression \param sFilter Filter expression. Not supported by NDX indices. \param iDescending Not supported by NDX indices. \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 xbIxNdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &, xbInt16, xbInt16 iUnique, xbInt16 iOverLay, void **vpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag *npTag = ndxTag; *vpTag = ndxTag; try{ //xbString sMsg; SetFileName( sName ); if( FileExists() && !iOverLay ) return XB_FILE_EXISTS; if( FileIsOpen()){ if(( iRc = xbTruncate(0)) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } if(( iRc = xbFclose()) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); npTag->npCurNode = NULL; npTag->sKeyExpression.Set( "" ); if( npTag->cpKeyBuf ){ free( npTag->cpKeyBuf ); npTag->cpKeyBuf = NULL; } if( npTag->cpKeyBuf2 ){ free( npTag->cpKeyBuf2 ); npTag->cpKeyBuf2 = NULL; } } if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } //set up the key expression npTag->exp = new xbExp( dbf->GetXbasePtr()); if(( iRc = npTag->exp->ParseExpression( dbf, sKey )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } switch( npTag->exp->GetReturnType()){ case XB_EXP_CHAR: npTag->cKeyType = 'C'; npTag->iKeyType = 0; npTag->iKeyLen = npTag->exp->GetResultLen(); break; case XB_EXP_NUMERIC: npTag->cKeyType = 'F'; npTag->iKeyType = 1; npTag->iKeyLen = 8; break; case XB_EXP_DATE: npTag->cKeyType = 'D'; npTag->iKeyType = 1; npTag->iKeyLen = 8; break; default: iErrorStop = 50; iRc = XB_INVALID_INDEX; throw iRc; } npTag->iUnique = iUnique; npTag->ulRootBlock = 1L; npTag->ulTotalBlocks = 2l; npTag->sKeyExpression = sKey; GetFileNamePart( npTag->sTagName ); if( npTag->iKeyLen > 100 ){ iErrorStop = 60; throw iRc; } npTag->iKeyItemLen = npTag->iKeyLen + 8; while(( npTag->iKeyItemLen % 4 )!= 0 ) npTag->iKeyItemLen++; npTag->iKeysPerBlock = (xbInt16) (GetBlockSize() - 8 ) / npTag->iKeyItemLen; ndxTag->cpKeyBuf = (char *) malloc( (size_t) ndxTag->iKeyLen ); ndxTag->cpKeyBuf2 = (char *) malloc( (size_t) ndxTag->iKeyLen ); if(( iRc = WriteHeadBlock(0)) != XB_NO_ERROR ){ iErrorStop = 70; throw iRc; } //write out block binary zeroes char buf[512]; memset( buf, 0x00, 512 ); if(( iRc = xbFwrite( buf, 1, 512 )) != XB_NO_ERROR ){ iErrorStop = 80; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Delete a key. /*! 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 xbIxNdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ xbInt32 lKeyCnt = GetKeyCount( npNode ); xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen; if( !IsLeaf( vpTag, npNode )) iLen += 4; if( iLen > 0 ){ char *pTrg = npNode->cpBlockData; pTrg += (4 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos; char *pSrc = pTrg; pSrc += npTag->iKeyItemLen; memmove( pTrg, pSrc, (size_t) iLen ); } // set the new number of keys ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 ); // write out the block if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::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 xbIxNdx::DeleteKey( void *vpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; // 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 = xbIxNdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } // Delete key needs to handle two scenarios // 1 - if the delete is on the only key of a leaf node, then traverse up the tree, trimming as needed // 2 - if the last key on a node is deleted, and the key value is not the same as the prev key value // go up the 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; } } 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->ulRootBlock && 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 = 130; 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; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::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 tag. /*! In the case of an ndx tag, it deletes the ndx file as it contains only one tag. \returns Return Codes */ xbInt16 xbIxNdx::DeleteTag( void * ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; //xbNdxTag * npTag; //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ // if open, close it if( FileIsOpen()){ if(( iRc = Close()) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } // delete file if(( iRc = xbRemove()) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ #ifdef XB_DEBUG_SUPPORT //! @brief Dump a block for a given tag. /*! Dump blocks for given tag for debugging purposes. \param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\param vpTag - Not required for single tag NDX files. \returns void */ //xbInt16 xbIxNdx::DumpTagBlocks( xbInt16 iOpt, void *vpTag ){ xbInt16 xbIxNdx::DumpTagBlocks( xbInt16 iOpt, void * ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; xbUInt32 lNoOfKeys; char *p; xbString s; xbBool bIsLeaf = false; try{ if( !FileIsOpen()){ iRc = XB_NOT_OPEN; iErrorStop = 10; throw iRc; } xbUInt32 ulStartBlock; xbUInt32 ulEndBlock; ulStartBlock = 1; ulEndBlock = ndxTag->ulTotalBlocks; for( xbUInt32 lBlk = ulStartBlock; lBlk < ulEndBlock; lBlk++ ){ memset( cNodeBuf, 0x00, XB_NDX_BLOCK_SIZE ); if(( iRc = ReadBlock( lBlk, XB_NDX_BLOCK_SIZE, cNodeBuf )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } p = cNodeBuf; lNoOfKeys = eGetUInt32( p ); if( eGetUInt32( p + 4 ) > 0 ){ bIsLeaf = false; s.Sprintf( "Node # %ld - Interior Node - Key Type [%c] Key Count [%ld]", lBlk, ndxTag->cKeyType, lNoOfKeys ); } else { bIsLeaf = true; s.Sprintf( "Node # %ld - Leaf Node - Key Type [%c] Key count [%ld]", lBlk, ndxTag->cKeyType, lNoOfKeys ); } xbase->WriteLogMessage( s, iOpt ); xbase->WriteLogMessage( "Key Child Dbf Rec Key", iOpt ); p += 4; xbUInt32 ulLeftBranch; xbUInt32 ulRecNo; xbString sKey; xbDouble d; xbUInt32 l; for( l = 0; l < lNoOfKeys; l++ ){ ulLeftBranch = eGetUInt32( p ); p+= 4; ulRecNo = eGetUInt32( p ); p+= 4; if( ndxTag->cKeyType == 'C' ){ sKey.Assign( p, 1, (xbUInt32) ndxTag->iKeyLen ); } else if( ndxTag->cKeyType == 'D' ){ xbInt32 lDate = (xbInt32) eGetDouble( p ); xbDate dt( lDate ); //xbString s2; //dt.JulToDate8( lDate, s2 ); sKey.Sprintf( "%ld - %s", lDate, dt.Str()); } else { d = eGetDouble( p ); sKey.Sprintf( "%f", d ); } p+= (ndxTag->iKeyItemLen-8); s.Sprintf( "%3d %9d %9d %s", l+1, ulLeftBranch, ulRecNo, sKey.Str() ); xbase->WriteLogMessage( s, iOpt ); } if( !bIsLeaf ){ ulLeftBranch = eGetUInt32( p ); s.Sprintf( "%3d %9d", l+1, ulLeftBranch ); xbase->WriteLogMessage( s, iOpt ); } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::DumpTagBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); 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
\returns Return Codes */ xbInt16 xbIxNdx::DumpHeader( xbInt16 iOpt, xbInt16 ){ xbString s; xbInt16 iRc; if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ) return iRc; s.Sprintf( "Index Header Node for %s", GetFileName().Str()); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "--------------------------------" ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Root block = %ld", ndxTag->ulRootBlock ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Total blocks = %ld", ndxTag->ulTotalBlocks ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Key types = %c,%d", ndxTag->cKeyType, ndxTag->iKeyType ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Key Length = %d", ndxTag->iKeyLen ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Keys Per Block = %d", ndxTag->iKeysPerBlock ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "Key Item Len = %ld", ndxTag->iKeyItemLen ); xbase->WriteLogMessage( s, iOpt); s.Sprintf( "Serial No = %d", ndxTag->cSerNo ); xbase->WriteLogMessage( s, iOpt); s.Sprintf( "Unique = %d", ndxTag->iUnique ); xbase->WriteLogMessage( s, iOpt ); s.Sprintf( "KeyExpression = %s", ndxTag->sKeyExpression.Str() ); xbase->WriteLogMessage( s, iOpt ); return XB_NO_ERROR; } /***********************************************************************/ //! @brief Dump the index for a tag. /*! Stub. \returns XB_NO_ERROR */ xbInt16 xbIxNdx::DumpIxForTag( void *, xbInt16 ) { return XB_NO_ERROR; } /***********************************************************************/ //! @brief Dump the index node chain. /*! Dump the index node chain for debugging purposes. \param vpTag Tag of node chain to dump. \param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\returns void */ void xbIxNdx::DumpIxNodeChain( void *vpTag, xbInt16 iOpt ) const { xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; xbString s( "Dump Node Chain" ); xbase->WriteLogMessage( s, iOpt ); if( npTag->npNodeChain ){ xbIxNode *n = npTag->npNodeChain; xbInt16 iCtr = 0; char cLeaf; s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo NoOfKeys Type" ); xbase->WriteLogMessage( s, iOpt ); while( n ){ IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I'; s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %c", iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo, n->ulBlockNo, eGetUInt32( n->cpBlockData ), cLeaf ); xbase->WriteLogMessage( s, iOpt ); n = n->npNext; } } else { s = "Empty Node Chain"; xbase->WriteLogMessage( s, iOpt ); } } /***********************************************************************/ //! @brief Dump node. /*! Dump a node for debugging purposes. \param vpTag Tag of node chain to dump. \param pNode Node to dump. \param iOpt Output message destination
0 = stdout
1 = Syslog
2 = Both
\returns XB_NVALID_OBJECT
XB_NO_ERROR */ xbInt16 xbIxNdx::DumpNode( void *vpTag, xbIxNode *pNode, xbInt16 iOpt ) const { xbString s; xbString sKey; xbUInt32 lLeftBranch; xbUInt32 lRecNo; xbDouble d; if( !pNode ) return XB_INVALID_OBJECT; xbIx::DumpNode( vpTag, pNode, iOpt ); xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; xbUInt32 lNoOfKeys = eGetUInt32( pNode->cpBlockData ); xbBool bIsLeaf = IsLeaf( vpTag, pNode ); if( bIsLeaf ) xbase->WriteLogMessage( "Leaf node", iOpt ); else xbase->WriteLogMessage( "Interior node", iOpt ); s.Sprintf( "Key type = [%c] No Of Keys =[%d] Prev =[%x] Next =[%x]", npTag->cKeyType, lNoOfKeys, pNode->npPrev, pNode->npNext ); xbase->WriteLogMessage( s, iOpt ); char *p = pNode->cpBlockData; p += 4; xbUInt32 l; for( l = 0; l < lNoOfKeys; l++ ){ lLeftBranch = eGetUInt32( p ); p+= 4; lRecNo = eGetUInt32( p ); p+= 4; if( npTag->cKeyType == 'C' ){ sKey.Assign( p, 1, (xbUInt32) npTag->iKeyLen ); } else if( npTag->cKeyType == 'D' ){ xbInt32 lDate = (xbInt32) eGetDouble( p ); xbDate dt( lDate ); sKey.Sprintf( "%ld - %s", lDate, dt.Str()); } else { d = eGetDouble( p ); sKey.Sprintf( "%f", d ); } p+= (npTag->iKeyItemLen-8); s.Sprintf( "%3d %9d %9d %s", l+1, lLeftBranch, lRecNo, sKey.Str() ); xbase->WriteLogMessage( s, iOpt ); } if( !bIsLeaf ){ lLeftBranch = eGetUInt32( p ); s.Sprintf( "%3d %9d", l+1, lLeftBranch ); xbase->WriteLogMessage( s.Str(), iOpt ); } return XB_NO_ERROR; } #endif /***********************************************************************/ //! @brief Find key /*! \param vpTag Pointer to tag to search. \param vpKey Void pointer to key data to search on. \param lSearchKeyLen Length of key to search for. \param iRetrieveSw xbTrue - Retrieve the record if key found.
xbFalse - Don't retrieve record, check for key existence only. \returns XB_NO_ERROR - Key found.
XB_NOT_FOUND - Key not found.
Return Codes */ xbInt16 xbIxNdx::FindKey( void *vpTag, const void *vpKey, xbInt32 lSearchKeyLen, xbInt16 iRetrieveSw ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; xbString sMsg; // xbInt16 iFindSts; 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 = 10; throw iRc; } } else { if(( iRc = dbf->Abort()) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } } } } xbUInt32 ulNoOfKeys; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; char cKeyType = npTag->cKeyType; if( npTag->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 = 30; throw iRc; } if( npTag->tNodeChainTs < tFileTs ){ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); npTag->npCurNode = NULL; if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 50; throw iRc; } } else { // pop up the chain looking for appropriate starting point xbBool bDone = false; xbIxNode * TempIxNode; while( npTag->npCurNode && !bDone && npTag->npCurNode->ulBlockNo != npTag->ulRootBlock ){ // not root node iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, 0, npTag->iKeyItemLen ), (size_t) lSearchKeyLen ); if( iRc <= 0 ){ TempIxNode = npTag->npCurNode; npTag->npCurNode = npTag->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( npTag->npCurNode->cpBlockData ) - 1; iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, ulKeyCtr, npTag->iKeyItemLen), (size_t) lSearchKeyLen ); if( iRc > 0 ){ TempIxNode = npTag->npCurNode; npTag->npCurNode = npTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } else { bDone = true; } } } } } else { if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 60; throw iRc; } } // if cur node is the base node and no keys on this node, then the index is empty if( npTag->ulRootBlock == npTag->npCurNode->ulBlockNo ){ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); if( ulNoOfKeys == 0 && IsLeaf( npTag, npTag->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( !IsLeaf( npTag, npTag->npCurNode ) ){ // get the number of keys on the block and compare the key to the rightmost key ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); if( ulNoOfKeys == 0 ) // interior nodes can have zero keys, just a link to the next lower node npTag->npCurNode->iCurKeyNo = 0; else { iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, ulNoOfKeys - 1, npTag->iKeyItemLen), (size_t) lSearchKeyLen ); if( iRc > 0 ){ npTag->npCurNode->iCurKeyNo = ulNoOfKeys; } else { npTag->npCurNode->iCurKeyNo = (xbUInt32) BSearchBlock( cKeyType, npTag->npCurNode, (xbInt32) npTag->iKeyItemLen, vpKey, (xbInt32) lSearchKeyLen, iSearchRc ); } } if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 80; throw iRc; } if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 90; throw iRc; } } // should be on a the correct leaf node, it may or may not contain the actual key ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); xbInt16 iCompRc = 0; if( ulNoOfKeys == 0 ){ iRc = XB_NOT_FOUND; return iRc; } else { iRc = BSearchBlock( cKeyType, npTag->npCurNode, npTag->iKeyItemLen, vpKey, lSearchKeyLen, iCompRc ); // iCompRc // 0 found // < 0 eof encountered, search key > last key in file // > 0 not found, positioned to next key // std::cout << "xbIxNdx::FindKey -Rc = " << iRc << " CompRc = " << iCompRc << " NoOfKeys = " << ulNoOfKeys << " blk no = " << npTag->npCurNode->ulBlockNo << "\n"; if( iCompRc >= 0 ){ npTag->npCurNode->iCurKeyNo = (xbUInt32) iRc; if( iRetrieveSw ){ xbUInt32 ulKey = npTag->npCurNode->iCurKeyNo; if( ulKey >= ulNoOfKeys ) // if position past end of keys, need to go back one and position to last key ulKey--; if(( iRc = GetDbfPtr( vpTag, ulKey, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 110; 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( "xbIxNdx::FindKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Find key for current record /*! This routine is called when updating a key. \param vpTag Pointer to tag to search. XB_NOT_FOUND Key not found.
Return Codes */ xbInt16 xbIxNdx::FindKeyForCurRec( void * vpTag ) { xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } // find key iRc = FindKey( vpTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 ); if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY || iRc == XB_EOF ) return iRc; if( iRc != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } // if keys are unique, and the recrd number matches, then we are good if( GetUnique() ) return XB_NO_ERROR; // get here if key found and not unique, need to move forward looking for correct rec no xbUInt32 ulDbfRecNo = dbf->GetCurRecNo(); xbBool bKeysMatch = true; // keys match? xbBool bCurRecsMatch = false; // cur recod number matches? xbUInt32 ulIxRecNo = 0; char cKeyType = GetKeyType( vpTag ); if(( iRc = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } if( ulIxRecNo == ulDbfRecNo ) bCurRecsMatch = true; xbInt16 iCompRc; while( !bCurRecsMatch && bKeysMatch ){ if(( iRc = GetNextKey( vpTag, 0 )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } // do compare key here iCompRc = CompareKey( cKeyType, npTag->cpKeyBuf, GetKeyData( npTag->npCurNode, 0, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); if( iCompRc != 0 ) bKeysMatch = false; else{ if(( iRc = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ iErrorStop = 50; throw iRc; } if( ulIxRecNo == ulDbfRecNo ) bCurRecsMatch = true; } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::FindKeyForCurRec() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); 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 xbIxNdx::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 = 10; throw XB_INVALID_KEYNO; } #endif xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; char *p = ( np->cpBlockData); p += (8 + (iKeyNo * npTag->iKeyItemLen)); ulDbfPtr = eGetUInt32 ( p ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::GetDbfPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Get the first key for the given tag. /*! \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 xbIxNdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ // clear out any history if( npTag->npNodeChain ){ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); npTag->npCurNode = NULL; } if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } // lRootBlock is now available if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } // if no keys on this node, and it's a leaf node then the index is empty xbUInt32 ulKeyPtr = eGetUInt32( npTag->npCurNode->cpBlockData ); if( ulKeyPtr == 0 && IsLeaf( npTag, npTag->npCurNode )){ iRc = XB_EMPTY; return iRc; } while( !IsLeaf( npTag, npTag->npCurNode )) // go down the chain looking for a leaf node { if(( iRc = GetKeyPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } } if( iRetrieveSw ){ if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 50; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 60; throw iRc; } } } catch( xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::GetFirstKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Get the key expression for the given tag. /*! \param vpTag Tag to retrieve expression from. \returns Key expression. */ xbString &xbIxNdx::GetKeyExpression( const void * vpTag ) const{ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; return npTag->sKeyExpression; } /***********************************************************************/ //! @brief Get the key filter for the given tag. /*! NDX index files do not support filters. This returns NULL. \returns NULL. */ xbString &xbIxNdx::GetKeyFilter( const void * ) const{ return sNullString; } /***********************************************************************/ //! @brief Get the key length for the given tag. /*! \param vpTag Tag to retrieve key length for. \returns Length of key. */ xbInt32 xbIxNdx::GetKeyLen( const void * vpTag ) const{ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; return npTag->iKeyLen; } /***********************************************************************/ //! @brief Get child node 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 ulKeyPtr Output node number \returns Return Codes */ xbInt16 xbIxNdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) 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 = 10; throw XB_INVALID_KEYNO; } #endif xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; char *p = ( np->cpBlockData); p += (4 + (iKeyNo * npTag->iKeyItemLen)); ulKeyPtr = eGetUInt32 ( p ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::GetKeyPtr() 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 XB_UPD_KEY Key updated.
XB_DEL_KEY Key deleted.
XB_ADD_KEY Key added.
0 No key updates */ xbInt16 xbIxNdx::GetKeySts( void *vpTag ) const{ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; return npTag->iKeySts; } /***********************************************************************/ //! @brief Get character key type for given tag. /*! \param vpTag Tag to retrieve key type for. \returns Char key type. */ char xbIxNdx::GetKeyType( const void * vpTag ) const{ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; return npTag->cKeyType; } /***********************************************************************/ //! @brief Get numeric key type for given tag. /*! \param vpTag Tag to retrieve first key for. \returns Numeric key type. */ xbInt16 xbIxNdx::GetKeyTypeN( const void * vpTag ) const{ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; return npTag->iKeyType; } /***********************************************************************/ //! @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 xbIxNdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){ return GetLastKey( 0, vpTag, iRetrieveSw ); // return GetLastKey( 0, vpTag, 1 ); } /***********************************************************************/ //! @brief Get the last key for the given tag and starting node. /*! \param ulNodeNo 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 xbIxNdx::GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbUInt32 ulKeyPtr = 0; xbUInt32 ulNoOfKeys = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ // clear out any history if( npTag->npNodeChain ){ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); npTag->npCurNode = NULL; } if( ulNodeNo == 0 ){ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } // lRootBlock is now available if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } } else { if(( iRc = GetBlock( npTag, ulNodeNo, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } } // if no keys on this node, then the index is empty ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); if( ulNoOfKeys == 0 && IsLeaf( npTag, npTag->npCurNode )){ iRc = XB_EMPTY; return iRc; } npTag->npCurNode->iCurKeyNo = ulNoOfKeys; while( !IsLeaf( npTag, npTag->npCurNode ) ){ // go down the chain looking for a leaf node npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData ); if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 50; throw iRc; } ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); npTag->npCurNode->iCurKeyNo = ulNoOfKeys; } // get here on a leaf node, it has one fewer iCurKeyNo npTag->npCurNode->iCurKeyNo--; if( iRetrieveSw ){ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData ); if(( iRc = GetDbfPtr( npTag, ulNoOfKeys - 1, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 60; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 70; throw iRc; } } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::GetLastKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); 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 xbIxNdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){ // returns the last key for a given block number xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; 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; } // set the key memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen ); // free memory npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain ); npTag->npNodeChain = npSaveNodeChain; npTag->npCurNode = npSaveCurNode; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::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 xbIxNdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ if( !npTag->npNodeChain ) return GetFirstKey( vpTag, iRetrieveSw ); // more keys on this node? if yes, get the next one to the right xbUInt32 ulKeyPtr; if((eGetUInt32( npTag->npCurNode->cpBlockData ) -1) > npTag->npCurNode->iCurKeyNo ){ npTag->npCurNode->iCurKeyNo++; if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } if( iRetrieveSw ){ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } else { return iRc; } } else { return iRc; } } // if at end of head node, then eof if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock ) return XB_EOF; // This logic assumes that interior nodes have n+1 left node no's where n is he the number of keys in the node xbIxNode * TempIxNode = npTag->npCurNode; npTag->npCurNode = npTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); while( npTag->npCurNode->iCurKeyNo >= eGetUInt32( npTag->npCurNode->cpBlockData ) && (npTag->npCurNode->ulBlockNo != npTag->ulRootBlock )){ TempIxNode = npTag->npCurNode; npTag->npCurNode = npTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } // head node and at end of head node, then eof if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock && npTag->npCurNode->iCurKeyNo == eGetUInt32( npTag->npCurNode->cpBlockData )) return XB_EOF; // move one to the right npTag->npCurNode->iCurKeyNo++; if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 50; throw iRc; } while( !IsLeaf( npTag, npTag->npCurNode )) // go down the chain looking for a leaf node { if(( iRc = GetKeyPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 60; throw iRc; } if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 70; throw iRc; } } if( iRetrieveSw ){ if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 80; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 90; throw iRc; } } } catch( xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::GetNextKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); 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 xbIxNdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; // This method assumes last index call landed on a valid key. // If last call resulted in an error, this method will returns XB_BOF try{ if( !npTag->npNodeChain ) return GetLastKey( 0, vpTag, iRetrieveSw ); xbUInt32 ulKeyPtr; if( npTag->npCurNode->iCurKeyNo > 0 ){ npTag->npCurNode->iCurKeyNo--; if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } if( iRetrieveSw ){ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } else { return iRc; } } } // next two lines might have been an issue if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock && GetKeyCount( npTag->npCurNode ) == 0 && IsLeaf( npTag, npTag->npCurNode )) return XB_EMPTY; if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock ) return XB_BOF; // This logic assumes that interior nodes have n+1 left node no's where n is he the number of keys in the node xbIxNode * TempIxNode = npTag->npCurNode; npTag->npCurNode = npTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); while( npTag->npCurNode->iCurKeyNo == 0 && (npTag->npCurNode->ulBlockNo != npTag->ulRootBlock )){ TempIxNode = npTag->npCurNode; npTag->npCurNode = npTag->npCurNode->npPrev; TempIxNode = FreeNodeChain( TempIxNode ); } // head node and at end of head node, then bof if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock && npTag->npCurNode->iCurKeyNo == 0 ) return XB_BOF; // move one to the left npTag->npCurNode->iCurKeyNo--; if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } while( !IsLeaf( npTag, npTag->npCurNode )){ // go down the chain looking for a leaf node npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData ); if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 60; throw iRc; } if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){ iErrorStop = 70; throw iRc; } } npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData ) - 1; if( iRetrieveSw ){ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 80; throw iRc; } if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){ iErrorStop = 90; throw iRc; } } } catch( xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::GetPrevKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Get the sort order for given tag. /*! Ndx indices only support ascending keys. \returns 0 */ xbBool xbIxNdx::GetSortOrder( void * ) const{ return 0; } /***********************************************************************/ //! @brief Get tag for tag number. /*! \returns Pointer to ndx tag. */ void * xbIxNdx::GetTag( xbInt16 ) const{ return ndxTag; } /***********************************************************************/ //! @brief Get tag for tag name. /*! \returns Pointer to ndx tag. */ void * xbIxNdx::GetTag( xbString & ) const{ return ndxTag; } /***********************************************************************/ //! @brief Get tag count. /*! NDX index files contain one tag. \returns 1 */ xbInt16 xbIxNdx::GetTagCount() const{ return 1; } /***********************************************************************/ //! @brief Get tag name. /*! \returns Tag name. */ xbString &xbIxNdx::GetTagName( void * ) const { // char * xbIxNdx::GetTagName( void * ) const { return ndxTag->sTagName; } /***********************************************************************/ //! @brief Get tag name. /*! \returns Tag name. */ const char * xbIxNdx::GetTagName( void *, xbInt16 ) const { return ndxTag->sTagName; } /***********************************************************************/ //! @brief Get the unique setting for given tag. /*! \param vpTag Tag to unique setting on. \returns xbTrue - Unique index.
xbFalse - Not unique index. */ xbBool xbIxNdx::GetUnique( void * vpTag ) const{ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; return npTag->iUnique; } /***********************************************************************/ //! @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 Pointer number to insert. \returns Return Codes */ xbInt16 xbIxNdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char *pTrg; xbInt16 iSrcPos; char *pLastKey = NULL; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ // update number of keys on the node xbInt32 lKeyCnt = GetKeyCount( npNode ); iSrcPos = 12 + (iSlotNo * npTag->iKeyItemLen); char *pSrc = npNode->cpBlockData; pSrc += iSrcPos; // if not appending to the end of the node, make some room, move things to the right if( iSlotNo < lKeyCnt ) { xbInt16 iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) - 4; pTrg = pSrc; pTrg += npTag->iKeyItemLen; memmove( pTrg, pSrc, (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 = 100; throw iRc; } // get the new right key value for the freshly split node pLastKey = (char *) malloc((size_t) ndxTag->iKeyLen); if(( iRc = GetLastKeyForBlockNo( vpTag, ulKeyPtr2, pLastKey )) != XB_NO_ERROR ){ iRc = 110; throw iRc; } // write the key value pTrg = pSrc; char *pTrg2 = pSrc; pSrc = pLastKey; for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) *pTrg++ = *pSrc++; pTrg2 += (npTag->iKeyItemLen - 8); ePutUInt32( pTrg2, ulPtr ); ePutInt32( npNode->cpBlockData, ++lKeyCnt ); // write out the updated block to disk if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } if( pLastKey ) free( pLastKey ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::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 xbIxNdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char *pSrc; char *pTrg; char *pKeyPos; xbString sMsg; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ xbInt32 lKeyCnt = GetKeyCount( npNode ); xbInt16 iKeyPos = 4 + iSlotNo * npTag->iKeyItemLen; pKeyPos = npNode->cpBlockData; pKeyPos += iKeyPos; // if not appending to end, make space, move things right if( iSlotNo < lKeyCnt ) { xbInt16 iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen; pTrg = pKeyPos; pTrg += npTag->iKeyItemLen; memmove( pTrg, pKeyPos, (size_t) iCopyLen ); } // if leaf, write rec number pTrg = pKeyPos; memset( pTrg, 0x00, 4 ); pTrg += 4; ePutUInt32( pTrg, ulPtr ); pTrg += 4; // write the key value pSrc = cpKeyBuf; for( xbInt16 i = 0; i < npTag->iKeyLen; i++ ) *pTrg++ = *pSrc++; // update number of keys on the node ePutInt32( npNode->cpBlockData, ++lKeyCnt ); // write out the updated block to disk if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::InsertNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Determine node leaf status /*! \param npNode Node to examine. \returns xbTrue - Leaf node.
xbFalse - Interior node. */ xbBool xbIxNdx::IsLeaf( void *, xbIxNode *npNode ) const { xbUInt32 ulBlock = eGetUInt32 ( npNode->cpBlockData+4 ); if( ulBlock > 0 ) // if the second four bytes are a number, it's an interior node return false; else return true; } /***********************************************************************/ //! @brief Determine if key exists. /*! This method assumes the key has already been built and is in either cpKeyBuf or dKey. \param vpTag - Pointer to tag. \returns xbTrue - Key exists.
xbFalse - Key does not exist. */ xbInt16 xbIxNdx::KeyExists( void * vpTag ){ xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; xbInt16 iRc = FindKey( vpTag, npTag->cpKeyBuf, npTag->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 xbIxNdx::KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddRecNo ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ iRc = FindKey( npTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 ); if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) return XB_NO_ERROR; // good position else if( iRc != XB_NO_ERROR ){ iErrorStop = 10; 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() == 0 ){ xbUInt32 ulCurRecNo; if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } xbBool bKeysMatch = xbTrue; while( bKeysMatch && ulAddRecNo > ulCurRecNo && iRc == XB_NO_ERROR ){ if(( iRc = GetNextKey( npTag, 0 )) == XB_NO_ERROR ){ if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf, (size_t) npTag->iKeyLen )) bKeysMatch = xbFalse; else{ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } } } } } if( iRc == XB_EOF ){ // eof condition if(( iRc = GetLastKey( 0, npTag, 0 )) != XB_NO_ERROR ){ iErrorStop = 130; throw iRc; } npTag->npCurNode->iCurKeyNo++; return XB_NO_ERROR; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::KeySetPos() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Set position for key add. /*! 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 xbIxNdx::KeySetPosDel( xbNdxTag *npTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbString sMsg; try{ iRc = FindKey( NULL, npTag->cpKeyBuf2, npTag->iKeyLen, 0 ); if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY ) return XB_NO_ERROR; // good position else if( iRc != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } xbUInt32 ulIxRecNo; if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } if( ulIxRecNo == dbf->GetCurRecNo()) return XB_NO_ERROR; if( GetUnique() == 1 ){ iErrorStop = 40; 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 = 50; 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 = 60; 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( "xbIxNdx::KeySetPosDel() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Returns key filter status. /*! \param vpTag Tag to check status on. \returns xbtrue - Key was updated.
xbFalse - Key not updated. Always true for NDX style indices. */ //inline xbBool xbIxNdx::KeyFiltered( void *vpTag ) const{ // return xbTrue; //} /***********************************************************************/ //! @brief Read head block of index file. /*! \param iOpt 0 - Read in entire block 1 - Read in only dynamic section of block \returns Return Codes */ xbInt16 xbIxNdx::ReadHeadBlock( xbInt16 iOpt = 0 ) { xbInt16 iRc = 0; xbInt16 iErrorStop = 0; try{ if( !FileIsOpen()){ iRc = XB_NOT_OPEN; iErrorStop = 10; throw iRc; } xbInt16 iLen; iOpt == 0 ? iLen = 512 : iLen = 21; if(( iRc = ReadBlock( (xbUInt32) 0, (size_t) iLen, cNodeBuf )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } char *p = cNodeBuf; ndxTag->ulRootBlock = eGetUInt32( p ); p+=4; ndxTag->ulTotalBlocks = eGetUInt32( p ); p+=5; if( iOpt == 0 ){ ndxTag->cKeyType = *p; p+=3; ndxTag->iKeyLen = eGetInt16( p ); p+=2; ndxTag->iKeysPerBlock = eGetInt16( p ); p+=2; ndxTag->iKeyType = eGetInt16( p ); p+=2; ndxTag->iKeyItemLen = eGetInt16( p ); p+=2; ndxTag->cSerNo = *p; p+=3; ndxTag->iUnique = *p; p++; ndxTag->sKeyExpression.Set( p ); if( ndxTag->exp ) delete ndxTag->exp; ndxTag->exp = new xbExp( xbase, dbf ); if(( iRc = ndxTag->exp->ParseExpression( ndxTag->sKeyExpression.Str() )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } if( ndxTag->cpKeyBuf ) free( ndxTag->cpKeyBuf ); if( ndxTag->cpKeyBuf2 ) free( ndxTag->cpKeyBuf2 ); ndxTag->cpKeyBuf = (char *) malloc( (size_t) ndxTag->iKeyLen ); ndxTag->cpKeyBuf2 = (char *) malloc( (size_t) ndxTag->iKeyLen ); if( ndxTag->sTagName == "" ) GetFileNamePart( ndxTag->sTagName ); } else { p+= 11; ndxTag->cSerNo = *p; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::ReadHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Reindex a tag. /*! \param vpTag Pointer to tag pointer. \returns Return Codes */ xbInt16 xbIxNdx::Reindex( void **vpTag ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; //xbNdxTag * npTag; //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; try{ xbString sFileName = GetFqFileName(); xbString sKey; // = GetKeyExpression( vpTag ); sKey.Set( GetKeyExpression( *vpTag )); xbInt16 iUnique = GetUnique( *vpTag ); xbString sFilter = ""; void *vpTag2; if(( iRc = CreateTag( sFileName, sKey, sFilter, 0, iUnique, xbTrue, &vpTag2 )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } xbUInt32 ulRecCnt = 0; if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } for( xbUInt32 l = 1; l <= ulRecCnt; l++ ){ if(( iRc = dbf->GetRecord( l )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } if(( iRc = CreateKey( vpTag2, 1 )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } if( iUnique ){ iRc = CheckForDupKey( vpTag2 ); if( iRc != 0 ){ if( iRc < 0 ){ iErrorStop = 50; throw iRc; } return XB_KEY_NOT_UNIQUE; } } if(( iRc = AddKey( vpTag2, l )) != XB_NO_ERROR ){ iErrorStop = 60; throw iRc; } } *vpTag = vpTag2; } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Set current tag. /*! For ndx indices, there is only one tag. \returns XB_NO_ERROR. */ xbInt16 xbIxNdx::SetCurTag( xbInt16 ) { xbIx::SetCurTag( ndxTag ); return XB_NO_ERROR; } /***********************************************************************/ //! @brief Set current tag. /*! For ndx indices, there is only one tag. \returns XB_NO_ERROR. */ xbInt16 xbIxNdx::SetCurTag( xbString & ) { xbIx::SetCurTag( ndxTag ); dbf->SetCurTag( "NDX", this, GetTag(0) ); return XB_NO_ERROR; } /***********************************************************************/ //! @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 xbIxNdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 ulPtr ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbNdxTag * npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; xbDouble dSplitFactor = .5; // split the nodes 50/50 xbString sMsg; try{ xbInt32 lKeyCnt = GetKeyCount( npLeft ); xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; xbInt32 lNewRightKeyCnt = lKeyCnt - lNewLeftKeyCnt; xbInt16 iSrcPos; xbInt16 iCopyLen; char *pSrc; char *pTrg; // insert the key into the left node 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 iSrcPos = ((lNewLeftKeyCnt + 1) * npTag->iKeyItemLen) + 4; iCopyLen = (lNewRightKeyCnt * npTag->iKeyItemLen) + 4; pSrc = npLeft->cpBlockData; pSrc += iSrcPos; pTrg = npRight->cpBlockData; pTrg += 4; memmove( pTrg, pSrc, (size_t) iCopyLen ); // write the new key counts into the nodes pTrg = npLeft->cpBlockData; ePutInt32( pTrg, lNewLeftKeyCnt ); pTrg = npRight->cpBlockData; ePutInt32( pTrg, lNewRightKeyCnt ); // 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 = 100; throw iRc; } // write out the block if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::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.
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 xbIxNdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; xbDouble dSplitFactor = .5; xbNdxTag *npTag; vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag; xbString sMsg; try{ xbInt32 lKeyCnt = GetKeyCount( npLeft ); xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1; xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt; // xbInt16 iSrcPos; xbInt16 iLen; char *pSrc = npLeft->cpBlockData; char *pTrg; 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 += ((lNewLeftKeyCnt * npTag->iKeyItemLen)+4); pTrg = npRight->cpBlockData; pTrg += 4; iLen = lNewRightKeyCnt * npTag->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 ); // 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( "xbIxNdx::SplitNodeL() 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 xbIxNdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; // ..xbNdxTag *npTag = (xbMdxTag *) vpTag; try{ // save off any needed fileds for updating // xbUInt32 ulTagSizeSave = mpTag->ulTagSize; //xbUInt32 ulLeftChildSave = mpTag->ulLeftChild; //xbUInt32 ulRightChildSave = mpTag->ulRightChild; if( cAction == 'D' || cAction == 'R' ){ // std::cout << "UpdateTagKey delete\n"; if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } } if( cAction == 'A' || cAction == 'R' ){ // std::cout << "UpdateTagKey add\n"; if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } } // if( ulTagSizeSave != mpTag->ulTagSize ){ // std::cout << "UpdateTagKey - tag size was updated need to do something here\n"; // } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::UpdateTagKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ //! @brief Write head block. /*! Commit the index head node to disk. \param iOpt 0 - Entire header.
1 - Update root block, number of blocks and seq number.
2 - Update sequence number only
\returns */ xbInt16 xbIxNdx::WriteHeadBlock( xbInt16 iOpt ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ if( iOpt == 2 ){ // increment the serial number if( ndxTag->cSerNo >= 0 && ndxTag->cSerNo < 127 ) ndxTag->cSerNo++; else ndxTag->cSerNo = 0; if(( iRc = xbFseek( 20, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 100; throw iRc; } if(( iRc = xbFputc( ndxTag->cSerNo )) != XB_NO_ERROR ){ iErrorStop = 110; throw iRc; } } else if( iOpt == 1 ){ xbRewind(); char buf[8]; ePutUInt32( &buf[0], ndxTag->ulRootBlock ); ePutUInt32( &buf[4], ndxTag->ulTotalBlocks ); if(( iRc = xbFwrite( buf, 8, 1 )) != XB_NO_ERROR ){ iErrorStop = 200; throw iRc; } return WriteHeadBlock( 2 ); } else if ( iOpt == 0 ){ char buf[512]; memset( buf, 0x00, 512 ); ePutUInt32( &buf[0], ndxTag->ulRootBlock ); ePutUInt32( &buf[4], ndxTag->ulTotalBlocks ); buf[9] = ndxTag->cKeyType; buf[11] = 0x1B; ePutInt16( &buf[12], ndxTag->iKeyLen ); ePutInt16( &buf[14], ndxTag->iKeysPerBlock ); ePutInt16( &buf[16], ndxTag->iKeyType ); ePutInt16( &buf[18], ndxTag->iKeyItemLen ); if( ndxTag-> iUnique ) buf[23] = 0x01; for( xbUInt32 i = 0; i < ndxTag->sKeyExpression.Len(); i++ ) buf[i+24] = ndxTag->sKeyExpression.GetCharacter(i+1); xbRewind(); if(( iRc = xbFwrite( buf, 512, 1 )) != XB_NO_ERROR ){ iErrorStop = 300; throw iRc; } } else { iRc = XB_INVALID_OPTION; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbIxNdx::WriteHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] option = [%d] ser=[%d]", iErrorStop, iRc, iOpt, ndxTag->cSerNo ); xbase->WriteLogMessage( sMsg ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ } /* namespace */ #endif /* XB_NDX_SUPPORT */