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