summaryrefslogtreecommitdiff
path: root/src/core/xbixndx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/xbixndx.cpp')
-rwxr-xr-xsrc/core/xbixndx.cpp2834
1 files changed, 2834 insertions, 0 deletions
diff --git a/src/core/xbixndx.cpp b/src/core/xbixndx.cpp
new file mode 100755
index 0000000..b28dd9d
--- /dev/null
+++ b/src/core/xbixndx.cpp
@@ -0,0 +1,2834 @@
+/* xbixndx.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
+
+*/
+
+#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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 110;
+ 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 = 120;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = 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 ){
+ npRightNode = FreeNodeChain( npRightNode );
+ npRightNode = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 );
+ if( !npRightNode ){
+ iErrorStop = 140;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, ulTempBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ 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 = 160;
+ 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 = 170;
+ throw iRc;
+ }
+ npRightNode = FreeNodeChain( npRightNode );
+ }
+ }
+
+ // update the header
+ if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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<br>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<br>
+ 0 = Syslog<br>
+ 1 = Stdout<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ 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 );
+
+ 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 = 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;
+ }
+ }
+
+ // verify the index count matches the tag count
+ xbUInt32 ulDbfCnt = 0;
+ if(( iRc = dbf->GetRecordCnt( ulDbfCnt )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ 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 = 150;
+ 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 = 160;
+ throw iRc;
+ }
+ if(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){
+ ulThisRecNo = j;
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld]", ulIxCnt, ulDbfCnt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+ 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 == 170 ){
+ sMsg.Sprintf( "xbIxNdx::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;
+
+}
+/***********************************************************************/
+//! @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.<br>
+ 1 = Append Mode, Create key for an append, only use rec buf 0, set updated switch.<br>
+ 2 = Update Mode, Create old version and new version keys, check if different, set update switch appropriately.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 100;
+ 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 = 110;
+ 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.<br>xbFalse - Not unique.
+ \param iOverLay xbTrue - Overlay if file already exists.<br>xbFalse - Don't overlay.
+ \param vpTag Output from method Pointer to vptag pointer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 100;
+ throw iRc;
+ }
+ if(( iRc = xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+
+ //set up the key expression
+ npTag->exp = new xbExp( dbf->GetXbasePtr());
+ if(( iRc = npTag->exp->ParseExpression( dbf, sKey )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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 = 140;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ npTag->iUnique = iUnique;
+ npTag->ulRootBlock = 1L;
+ //npTag->ulTotalBlocks = 2l;
+ npTag->ulTotalBlocks = 2L;
+ npTag->sKeyExpression = sKey;
+
+ GetFileNamePart( npTag->sTagName );
+
+ if( npTag->iKeyLen > 100 ){
+ iErrorStop = 150;
+ 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 = 160;
+ throw iRc;
+ }
+
+ //write out block binary zeroes
+ char buf[512];
+ memset( buf, 0x00, 512 );
+ if(( iRc = xbFwrite( buf, 1, 512 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param vpTag - Not required for single tag NDX files.
+ \returns void
+*/
+
+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 = 100;
+ 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 = 110;
+ 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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \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<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns XB_INVALID_OBJECT<br>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.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = dbf->Abort()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+ if( npTag->tNodeChainTs < tFileTs ){
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ 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 = 150;
+ 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 = 160;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ 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 = 180;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ 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.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 100;
+ 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 = 110;
+ 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 = 120;
+ throw iRc;
+ }
+ if( ulIxRecNo == ulDbfRecNo )
+ bCurRecsMatch = true;
+
+ xbInt16 iCompRc;
+ while( !bCurRecsMatch && bKeysMatch ){
+
+ if(( iRc = GetNextKey( vpTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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 = 140;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 100;
+ 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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 100;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ if( iRetrieveSw ){
+ if(( iRc = GetDbfPtr( npTag, 0, npTag->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( "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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 100;
+ 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.<br>
+ XB_DEL_KEY Key deleted.<br>
+ XB_ADD_KEY Key added.<br>
+ 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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 100;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = GetBlock( npTag, ulNodeNo, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ 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 = 130;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ 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 = 150;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 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 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 = 120;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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 = 140;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ if( iRetrieveSw ){
+ if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ 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.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 100;
+ throw iRc;
+ }
+ if( iRetrieveSw ){
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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 = 140;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ 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 = 160;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ 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.<br> 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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 120;
+ 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.<br>
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 100;
+ 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.<br> 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.<br> 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+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 = 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() == 0 ){
+ xbUInt32 ulCurRecNo;
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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 = 120;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 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() == 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( "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.<br>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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::ReadHeadBlock( xbInt16 iOpt = 0 ) {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+ if( !FileIsOpen()){
+ iRc = XB_NOT_OPEN;
+ iErrorStop = 100;
+ throw iRc;
+ }
+ xbInt16 iLen;
+ iOpt == 0 ? iLen = 512 : iLen = 21;
+
+ if(( iRc = ReadBlock( (xbUInt32) 0, (size_t) iLen, cNodeBuf )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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 = 120;
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::Reindex( void **vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag *npTag = ndxTag;
+
+ try{
+ xbInt16 iUnique = GetUnique( *vpTag );
+
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ npTag->ulRootBlock = 1L;
+ npTag->ulTotalBlocks = 2L;
+
+ if(( iRc = xbTruncate( 1024 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ char buf[512];
+ memset( buf, 0x00, 512 );
+
+ if(( iRc = WriteBlock( 1, 0, buf )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ xbUInt32 ulRecCnt = 0;
+ if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ for( xbUInt32 l = 1; l <= ulRecCnt; l++ ){
+ if(( iRc = dbf->GetRecord( l )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if(( iRc = CreateKey( npTag, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if( iUnique ){
+ // iRc = CheckForDupKey( vpTag2 );
+ iRc = CheckForDupKey( npTag );
+ if( iRc != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ }
+
+ if(( iRc = AddKey( npTag, l )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ *vpTag = npTag;
+ }
+ 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 ));
+ this->DeleteTag( NULL ); // Don't leave the index in an incomplete state
+ }
+ 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.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ 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.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 = 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::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.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ 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.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ 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 <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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 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( "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.<br>
+ D - Delete a key.<br>
+ R - Revise a key.<br>
+ \param vpTg - Pointer to tag.<br>
+ \param ulRecNo - Record number association with the action.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+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' ){
+ if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ }
+ 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.<br>
+ 1 - Update root block, number of blocks and seq number.<br>
+ 2 - Update sequence number only<br>
+ \returns <a href="xbretcod_8h.html">
+*/
+
+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 = 120;
+ 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 = 130;
+ 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 */
+
+
+