//------------------------------------------------------------------------- // Desc: Routines used during query to traverse through index b-trees. // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fscursor.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC FLMINT FSCompareKeys( FLMBOOL bKey1IsUntilKey, FLMBYTE * pKey1, FLMUINT uiKeyLen1, FLMBOOL bExclusiveKey1, FLMBOOL bKey2IsUntilKey, FLMBYTE * pKey2, FLMUINT uiKeyLen2, FLMBOOL bExclusiveKey2); #define FS_COMPARE_KEYS( bKey1IsUntilKey,pKeyPos1,bKey2IsUntilKey,pKeyPos2) \ FSCompareKeys( (bKey1IsUntilKey), \ (pKeyPos1)->pKey, (pKeyPos1)->uiKeyLen, (pKeyPos1)->bExclusiveKey, \ (bKey2IsUntilKey), \ (pKeyPos2)->pKey, (pKeyPos2)->uiKeyLen, (pKeyPos2)->bExclusiveKey) /**************************************************************************** Desc: ****************************************************************************/ FSIndexCursor::FSIndexCursor() { m_pFirstSet = m_pCurSet = NULL; m_pSavedPos = NULL; m_curKeyPos.bStackInUse = FALSE; reset(); } /**************************************************************************** Desc: ****************************************************************************/ FSIndexCursor::~FSIndexCursor() { releaseBlocks(); freeSets(); } /**************************************************************************** Desc: Resets any allocations, keys, state, etc. ****************************************************************************/ void FSIndexCursor::reset( void) { releaseBlocks(); freeSets(); m_uiIndexNum = m_uiCurrTransId = m_uiBlkChangeCnt = 0; m_pFirstSet = m_pCurSet = NULL; m_pSavedPos = NULL; m_curKeyPos.bExclusiveKey = FALSE; m_DefaultSet.pNext = m_DefaultSet.pPrev = NULL; m_DefaultSet.fromKey.bStackInUse = m_DefaultSet.untilKey.bStackInUse = FALSE; m_bAtBOF = TRUE; m_bAtEOF = FALSE; } /**************************************************************************** Desc: Resets to a new transaction that may change the read consistency of the query. This is usually an old view error internal or external of this class. ****************************************************************************/ RCODE FSIndexCursor::resetTransaction( FDB * pDb) { RCODE rc = FERR_OK; KEYSET * pTmpSet; if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, m_uiIndexNum, &m_pLFile, &m_pIxd))) { goto Exit; } m_uiCurrTransId = pDb->LogHdr.uiCurrTransID; m_uiBlkChangeCnt = pDb->uiBlkChangeCnt; m_bIsUpdateTrans = (pDb->uiTransType == FLM_UPDATE_TRANS) ? TRUE : FALSE; // Need to release all stacks that are currently in use. for( pTmpSet = m_pFirstSet; pTmpSet; pTmpSet = pTmpSet->pNext) { releaseKeyBlocks( &pTmpSet->fromKey); releaseKeyBlocks( &pTmpSet->untilKey); } releaseKeyBlocks( &m_DefaultSet.fromKey); releaseKeyBlocks( &m_DefaultSet.untilKey); if( m_pSavedPos) { releaseKeyBlocks( m_pSavedPos); } releaseKeyBlocks( &m_curKeyPos); Exit: return( rc); } /**************************************************************************** Desc: Free all of the allocated sets. ****************************************************************************/ void FSIndexCursor::freeSets( void) { KEYSET * pCurSet; // Current set. KEYSET * pNextSet; // Current set. for( pCurSet = m_pFirstSet; pCurSet; pCurSet = pNextSet) { pNextSet = pCurSet->pNext; if( pCurSet != &m_DefaultSet) { f_free( &pCurSet); } } m_pFirstSet = m_pCurSet = NULL; if( m_pSavedPos) { releaseKeyBlocks( m_pSavedPos); f_free( &m_pSavedPos); m_pSavedPos = NULL; } } /**************************************************************************** Desc: Releases the cache blocks back to cache. ****************************************************************************/ void FSIndexCursor::releaseBlocks( void) { KEYSET * pCurSet; // Unuse all of the cache blocks in the from and until keys. for( pCurSet = m_pFirstSet; pCurSet; pCurSet = pCurSet->pNext) { releaseKeyBlocks( &pCurSet->fromKey); releaseKeyBlocks( &pCurSet->untilKey); } releaseKeyBlocks( &m_curKeyPos); } /**************************************************************************** Desc: Setup the from and until keys in the cursor. Return counts after positioning to the from and until key in the index. This code does not work with multiple key sets of FROM/UNTIL keys. ****************************************************************************/ RCODE FSIndexCursor::setupKeys( FDB * pDb, IXD * pIxd, QPREDICATE ** ppQPredicateList, FLMBOOL * pbDoRecMatch, // [out] Leave alone or set to TRUE. FLMBOOL * pbDoKeyMatch, // [out] Set to TRUE or FALSE FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks FLMUINT * puiTotalKeys, // [out] total number of keys FLMUINT * puiTotalRefs, // [out] total references FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. { RCODE rc = FERR_OK; FLMUINT uiUntilKeyLen; FLMBYTE pUntilKey [MAX_KEY_SIZ + 4]; DIN_STATE dinState; RESET_DINSTATE( dinState); m_uiIndexNum = pIxd->uiIndexNum; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } m_DefaultSet.fromKey.uiRefPosition = 0; m_DefaultSet.untilKey.uiRefPosition = 0; m_DefaultSet.fromKey.bExclusiveKey = FALSE; m_DefaultSet.untilKey.bExclusiveKey = TRUE; if( RC_BAD( rc = flmBuildFromAndUntilKeys( pIxd, ppQPredicateList, m_DefaultSet.fromKey.pKey, &m_DefaultSet.fromKey.uiKeyLen, m_DefaultSet.untilKey.pKey, &m_DefaultSet.untilKey.uiKeyLen, pbDoRecMatch, pbDoKeyMatch, &m_DefaultSet.untilKey.bExclusiveKey))) { goto Exit; } // Here is a rundown of how the data is setup after this call. // Default.FROM key - block address and current element offset and // generated FROM key. // Default.UNTIL key - full stack setup and the generated UNTIL key // curKeyPos - pKey is the first key positioned the full stack is setup. f_memcpy( m_curKeyPos.pKey, m_DefaultSet.fromKey.pKey, m_curKeyPos.uiKeyLen = m_DefaultSet.fromKey.uiKeyLen); f_memcpy( pUntilKey, m_DefaultSet.untilKey.pKey, uiUntilKeyLen = m_DefaultSet.untilKey.uiKeyLen); m_pCurSet = m_pFirstSet = &m_DefaultSet; m_DefaultSet.fromKey.uiRecordId = m_curKeyPos.uiRecordId = 0; m_DefaultSet.fromKey.uiDomain = m_curKeyPos.uiDomain = MAX_DOMAIN; m_DefaultSet.untilKey.uiRecordId = 0; m_DefaultSet.untilKey.uiDomain = ZERO_DOMAIN; // Want any of the counts back? if( puiLeafBlocksBetween || puiTotalKeys || puiTotalRefs) { if( RC_OK( rc = setKeyPosition( pDb, TRUE, &m_DefaultSet.fromKey, &m_curKeyPos))) { // Copy the b-tree information to the from key. m_DefaultSet.fromKey.uiBlockAddr = m_curKeyPos.uiBlockAddr; m_DefaultSet.fromKey.uiDomain = m_curKeyPos.uiDomain; m_DefaultSet.fromKey.uiBlockTransId = m_curKeyPos.uiBlockTransId; m_DefaultSet.fromKey.uiCurElm = m_curKeyPos.uiCurElm; // All keys bewteen FROM and UNTIL may be gone. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_DefaultSet.untilKey) <= 0) { rc = setKeyPosition( pDb, TRUE, &m_DefaultSet.untilKey, &m_DefaultSet.untilKey); rc = (rc == FERR_EOF_HIT) ? FERR_OK: rc; // Restore the original UNTIL key - throws away what the last key is. f_memcpy( m_DefaultSet.untilKey.pKey, pUntilKey, m_DefaultSet.untilKey.uiKeyLen = uiUntilKeyLen); } else { rc = RC_SET( FERR_BOF_HIT); } } else { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } m_bAtBOF = FALSE; } if( RC_BAD( rc)) { // Empty tree or empty set case. if( rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { if( puiLeafBlocksBetween) *puiLeafBlocksBetween = 0; if( puiTotalKeys) *puiTotalKeys = 0; if( puiTotalRefs) *puiTotalRefs = 0; if( pbTotalsEstimated) *pbTotalsEstimated = FALSE; rc = FERR_OK; } goto Exit; } else { // If this is a positioning index, set the ref positions. if( pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_curKeyPos.pStack, &dinState, &m_DefaultSet.fromKey.uiRefPosition))) { goto Exit; } if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_DefaultSet.untilKey.pStack, &dinState, &m_DefaultSet.untilKey.uiRefPosition))) { goto Exit; } } if( RC_BAD( rc = FSComputeIndexCounts( m_curKeyPos.pStack, m_DefaultSet.untilKey.pStack, puiLeafBlocksBetween, puiTotalKeys, puiTotalRefs, pbTotalsEstimated))) { goto Exit; } } } m_bAtBOF = TRUE; Exit: return( rc); } /**************************************************************************** Public: setupKeys Desc: Setup a cursor with just a single FROM/UNTIL key. ****************************************************************************/ RCODE FSIndexCursor::setupKeys( FDB * pDb, IXD * pIxd, FLMBYTE * pFromKey, FLMUINT uiFromKeyLen, FLMUINT uiFromRecordId, FLMBYTE * pUntilKey, FLMUINT uiUntilKeyLen, FLMUINT uiUntilRecordId, FLMBOOL bExclusiveUntil) { RCODE rc; m_uiIndexNum = pIxd->uiIndexNum; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } m_DefaultSet.pNext = m_DefaultSet.pPrev = NULL; // FROM key m_DefaultSet.fromKey.uiRecordId = uiFromRecordId; m_DefaultSet.fromKey.uiDomain = uiFromRecordId ? DRN_DOMAIN( uiFromRecordId) + 1: MAX_DOMAIN; m_DefaultSet.fromKey.uiKeyLen = uiFromKeyLen; f_memcpy( m_DefaultSet.fromKey.pKey, pFromKey, uiFromKeyLen); m_DefaultSet.fromKey.bExclusiveKey = FALSE; // UNTIL key m_DefaultSet.untilKey.uiRecordId = uiUntilRecordId; m_DefaultSet.untilKey.uiDomain = uiUntilRecordId ? DRN_DOMAIN( uiUntilRecordId) + 1: ZERO_DOMAIN; m_DefaultSet.untilKey.uiKeyLen = uiUntilKeyLen; f_memcpy( m_DefaultSet.untilKey.pKey, pUntilKey, uiUntilKeyLen); m_DefaultSet.untilKey.bExclusiveKey = bExclusiveUntil; m_pFirstSet = &m_DefaultSet; m_bAtBOF = TRUE; m_pCurSet = NULL; // If this is a positioning index we need to setup the FROM/UNTIL // btrees to get the FROM/UNTIL reference positions. if( pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = setupForPositioning( pDb))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Read down all of the b-tree sets (FROM/UNTIL) so the all absolute positioning values can be set. ****************************************************************************/ RCODE FSIndexCursor::setupForPositioning( FDB * pDb) { RCODE rc = FERR_OK; DIN_STATE dinState; FLMUINT uiTempKeyLen; FLMBYTE pTempKey [MAX_KEY_SIZ + 4]; KEYSET * pSrcSet; // Current source set // Read all of the FROM/UNTIL keys going from the last set to the first. // This way m_curKeyPos will be positioned to the first key. for( pSrcSet = m_pFirstSet; pSrcSet->pNext; pSrcSet = pSrcSet->pNext) { ; } for( ; pSrcSet; pSrcSet = pSrcSet->pPrev) { f_memcpy( pTempKey, pSrcSet->untilKey.pKey, uiTempKeyLen = pSrcSet->untilKey.uiKeyLen); if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &pSrcSet->untilKey, &pSrcSet->untilKey))) { goto Exit; } // Copy the key back. f_memcpy( pSrcSet->untilKey.pKey, pTempKey, pSrcSet->untilKey.uiKeyLen = uiTempKeyLen); if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, pSrcSet->untilKey.pStack, &dinState, &pSrcSet->untilKey.uiRefPosition))) { goto Exit; } } if( pSrcSet == m_pFirstSet) { if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &pSrcSet->fromKey, &m_curKeyPos))) { goto Exit; } m_pCurSet = m_pFirstSet; // Copy the b-tree information to the from key. m_pCurSet->fromKey.uiBlockAddr = m_curKeyPos.uiBlockAddr; m_pCurSet->fromKey.uiDomain = m_curKeyPos.uiDomain; m_pCurSet->fromKey.uiBlockTransId = m_curKeyPos.uiBlockTransId; m_pCurSet->fromKey.uiCurElm = m_curKeyPos.uiCurElm; if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_curKeyPos.pStack, &dinState, &pSrcSet->fromKey.uiRefPosition))) { goto Exit; } } } else { f_memcpy( pTempKey, pSrcSet->fromKey.pKey, uiTempKeyLen = pSrcSet->fromKey.uiKeyLen); if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &pSrcSet->fromKey, &pSrcSet->fromKey))) { goto Exit; } // Copy the key back. f_memcpy( pSrcSet->fromKey.pKey, pTempKey, pSrcSet->fromKey.uiKeyLen = uiTempKeyLen); if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, pSrcSet->fromKey.pStack, &dinState, &pSrcSet->fromKey.uiRefPosition))) { goto Exit; } } } } Exit: return( rc); } /**************************************************************************** Desc: Merge the input cursors fromUntil sets as a result of a UNION. ****************************************************************************/ RCODE FSIndexCursor::unionKeys( FSIndexCursor * pFSCursor) { RCODE rc = FERR_OK; KEYSET * pInputSet; // Sets input via pFSCursor. KEYSET * pSrcSet; // Current set KEYSET * pDestSet = NULL; // Newly allocated set KEYSET * pCurDestSet = NULL; // New current set KEYSET * pPrevDestSet; // We need to release all of the blocks. Too complex pFSCursor->releaseBlocks(); releaseBlocks(); pInputSet = pFSCursor->getFromUntilSets(); pSrcSet = m_pFirstSet; while( pSrcSet || pInputSet) { FLMBOOL bFromKeyLessThan; FLMBOOL bUntilKeyGreaterThan; pPrevDestSet = pCurDestSet; if( RC_BAD( rc = f_calloc( sizeof( KEYSET), &pCurDestSet))) { goto Exit; } if( !pSrcSet) { f_memcpy( pCurDestSet, pInputSet, sizeof( KEYSET)); pInputSet = pInputSet->pNext; } else if( !pInputSet) { f_memcpy( pCurDestSet, pSrcSet, sizeof( KEYSET)); pSrcSet = pSrcSet->pNext; } else if( !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { if( bFromKeyLessThan) { f_memcpy( pCurDestSet, pInputSet, sizeof( KEYSET)); pInputSet = pInputSet->pNext; } else { f_memcpy( pCurDestSet, pSrcSet, sizeof( KEYSET)); pSrcSet = pSrcSet->pNext; } } else { f_memcpy( &pCurDestSet->fromKey, bFromKeyLessThan ? &pInputSet->fromKey : &pSrcSet->fromKey, sizeof( KEYPOS)); for(;;) { // Keys overlap - take the lowest FROM key and highest UNTIL key // walking through the lower UNTIL key set while the keys overlap. if( bUntilKeyGreaterThan) { if( ((pSrcSet = pSrcSet->pNext) == NULL) || !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { f_memcpy( &pCurDestSet->untilKey, &pInputSet->untilKey, sizeof( KEYPOS)); pInputSet = pInputSet->pNext; break; } } else { if( ((pInputSet = pInputSet->pNext) == NULL) || !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { f_memcpy( &pCurDestSet->untilKey, &pSrcSet->untilKey, sizeof( KEYPOS)); pSrcSet = pSrcSet->pNext; break; } } } } // Link in. pCurDestSet->pNext = NULL; if( !pDestSet) { pDestSet = pCurDestSet; pCurDestSet->pPrev = NULL; } else { pPrevDestSet->pNext = pCurDestSet; pCurDestSet->pPrev = pPrevDestSet; } } // We went to the trouble of having a default set allocated with this class. // Undo the last allocation. if( pDestSet) then pCurDestSet can be used. freeSets(); if( pDestSet) { f_memcpy( &m_DefaultSet, pCurDestSet, sizeof( KEYSET)); if( pCurDestSet->pPrev) { pCurDestSet->pPrev->pNext = &m_DefaultSet; m_pFirstSet = pDestSet; } else { m_pFirstSet = &m_DefaultSet; } f_free( &pCurDestSet); } m_bAtBOF = TRUE; m_pCurSet = NULL; Exit: return( rc); } /**************************************************************************** Desc: Intersect the from/until key sets of pFSCursor into 'this'. ****************************************************************************/ RCODE FSIndexCursor::intersectKeys( FDB * pDb, FSIndexCursor * pFSCursor) { RCODE rc = FERR_OK; KEYSET * pInputSet; // Sets input via pFSCursor. KEYSET * pSrcSet; // Current set KEYSET * pDestSet = NULL; // Newly allocated set KEYSET * pCurDestSet = NULL; // New current set KEYSET * pPrevDestSet; // Create a new destiniation set and throw away the current set. pFSCursor->releaseBlocks(); releaseBlocks(); pInputSet = pFSCursor->getFromUntilSets(); pSrcSet = m_pFirstSet; while( pSrcSet && pInputSet) { FLMBOOL bFromKeyLessThan; FLMBOOL bUntilKeyGreaterThan; if( !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { // Keys do NOT overlap - go to the next set section. if( bFromKeyLessThan) { pInputSet = pInputSet->pNext; } else { pSrcSet = pSrcSet->pNext; } } else { // Values overlap - see the two boolean values on how they overlap. pPrevDestSet = pCurDestSet; if( RC_BAD( rc = f_calloc( sizeof( KEYSET), &pCurDestSet))) { goto Exit; } pCurDestSet->pNext = NULL; if( !pDestSet) { pDestSet = pCurDestSet; pCurDestSet->pPrev = NULL; } else { pCurDestSet->pPrev = pPrevDestSet; pPrevDestSet->pNext = pCurDestSet; } // Take the highest FROM key. f_memcpy( &pCurDestSet->fromKey, bFromKeyLessThan ? &pSrcSet->fromKey : &pInputSet->fromKey, sizeof( KEYPOS)); // Take the lowest until key and position to the next set. if( bUntilKeyGreaterThan) { f_memcpy( &pCurDestSet->untilKey, &pSrcSet->untilKey, sizeof( KEYPOS)); pSrcSet = pSrcSet->pNext; } else { f_memcpy( &pCurDestSet->untilKey, &pInputSet->untilKey, sizeof( KEYPOS)); pInputSet = pInputSet->pNext; } } } // We went to the trouble of having a default set allocated with this class. // Undo the last allocation. if( pDestSet) then pCurDestSet can be used. freeSets(); if( pDestSet) { f_memcpy( &m_DefaultSet, pCurDestSet, sizeof( KEYSET)); if( pCurDestSet->pPrev) { pCurDestSet->pPrev->pNext = &m_DefaultSet; m_pFirstSet = pDestSet; } else { m_pFirstSet = &m_DefaultSet; } f_free( &pCurDestSet); } m_bAtBOF = TRUE; m_pCurSet = NULL; // If this is a positioning index we need to setup the FROM/UNTIL // btrees to get the FROM/UNTIL reference positions. if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = setupForPositioning( pDb))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Specific compare for a key range against the key ranges in this cursor. ****************************************************************************/ FLMBOOL FSIndexCursor::compareKeyRange( // Returns TRUE if keys overlap. FLMBYTE * pFromKey, FLMUINT uiFromKeyLen, FLMBOOL bExclusiveFrom, FLMBYTE * pUntilKey, FLMUINT uiUntilKeyLen, FLMBOOL bExclusiveUntil, FLMBOOL * pbUntilKeyInSet, // Truth Table: T F F FLMBOOL * pbUntilGreaterThan) // F T F { FLMBOOL bKeysOverlap = FALSE; KEYSET * pSrcSet; // Current source set FLMINT iFromCmp; FLMINT iUntilCmp; FLMINT iFromUntilCmp; FLMINT iUntilFromCmp; for( pSrcSet = m_pFirstSet; pSrcSet; pSrcSet = pSrcSet->pNext) { iFromCmp = FSCompareKeys( FALSE, pFromKey, uiFromKeyLen, bExclusiveFrom, FALSE, pSrcSet->fromKey.pKey, pSrcSet->fromKey.uiKeyLen, pSrcSet->fromKey.bExclusiveKey); if( iFromCmp < 0) { // Move from left-most to right-most to see where the overlap is. iUntilFromCmp = FSCompareKeys( TRUE, pUntilKey, uiUntilKeyLen, bExclusiveUntil, FALSE, pSrcSet->fromKey.pKey, pSrcSet->fromKey.uiKeyLen, pSrcSet->fromKey.bExclusiveKey); if( iUntilFromCmp < 0) { *pbUntilKeyInSet = FALSE; *pbUntilGreaterThan = FALSE; goto Exit; } if( iUntilFromCmp == 0) { bKeysOverlap = TRUE; *pbUntilKeyInSet = TRUE; *pbUntilGreaterThan = FALSE; goto Exit; } // UNTIL > pSrcSet->fromKey iUntilCmp = FSCompareKeys( TRUE, pUntilKey, uiUntilKeyLen, bExclusiveUntil, TRUE, pSrcSet->untilKey.pKey, pSrcSet->untilKey.uiKeyLen, pSrcSet->untilKey.bExclusiveKey); if( iUntilCmp <= 0) { bKeysOverlap = TRUE; *pbUntilKeyInSet = TRUE; *pbUntilGreaterThan = FALSE; goto Exit; } bKeysOverlap = TRUE; // Try the next source set to see if the UNTIL key is in a set. } else { // Move from left-most to right-most to see where the overlap is. if( iFromCmp == 0) { bKeysOverlap = TRUE; } else { iFromUntilCmp = FSCompareKeys( FALSE, pFromKey, uiFromKeyLen, bExclusiveFrom, TRUE, pSrcSet->untilKey.pKey, pSrcSet->untilKey.uiKeyLen, pSrcSet->untilKey.bExclusiveKey); if( iFromUntilCmp <= 0) { bKeysOverlap = TRUE; } else { continue; } } iUntilCmp = FSCompareKeys( TRUE, pUntilKey, uiUntilKeyLen, bExclusiveFrom, TRUE, pSrcSet->untilKey.pKey, pSrcSet->untilKey.uiKeyLen, pSrcSet->untilKey.bExclusiveKey); if( iUntilCmp <= 0) { bKeysOverlap = TRUE; *pbUntilKeyInSet = TRUE; *pbUntilGreaterThan = FALSE; goto Exit; } } } *pbUntilKeyInSet = FALSE; *pbUntilGreaterThan = TRUE; Exit: return bKeysOverlap; } /**************************************************************************** Desc: Compare two From/Until key positions. ****************************************************************************/ FLMBOOL FSIndexCursor::FSCompareKeyPos( // TRUE if keys overlap KEYSET * pSet1, KEYSET * pSet2, FLMBOOL * pbFromKeyLessThan, // pSet1->from < pSet2->from FLMBOOL * pbUntilKeyGreaterThan) // pSet1->until > pSet2->until { if( FS_COMPARE_KEYS( TRUE, &pSet1->untilKey, FALSE, &pSet2->fromKey) < 0) { *pbFromKeyLessThan = TRUE; pbUntilKeyGreaterThan = FALSE; return FALSE; } if( FS_COMPARE_KEYS( FALSE, &pSet1->fromKey, TRUE, &pSet2->untilKey) > 0) { *pbFromKeyLessThan = FALSE; *pbUntilKeyGreaterThan = TRUE; return FALSE; } // Keys overlap. Two more compares needed *pbFromKeyLessThan = (FLMBOOL) ((FS_COMPARE_KEYS( FALSE, &pSet1->fromKey, FALSE, &pSet2->fromKey) < 0) ? TRUE : FALSE); *pbUntilKeyGreaterThan = (FLMBOOL) ((FS_COMPARE_KEYS( TRUE, &pSet1->untilKey, TRUE, &pSet2->untilKey) > 0) ? TRUE : FALSE); return (TRUE); } /**************************************************************************** Desc: Set information from the block into the KEYPOS structure. ****************************************************************************/ FINLINE void setKeyItemsFromBlock( KEYPOS * pKeyPos) { pKeyPos->uiBlockAddr = pKeyPos->pStack->uiBlkAddr; pKeyPos->uiCurElm = pKeyPos->pStack->uiCurElm; pKeyPos->uiKeyLen = pKeyPos->pStack->uiKeyLen; pKeyPos->uiBlockTransId = (pKeyPos->uiBlockAddr != BT_END) ? FB2UD( &pKeyPos->pStack->pBlk[ BH_TRANS_ID]) : 0; } /**************************************************************************** Desc: Set the key position given some KEYPOS structure. Please note that the blocks in the stack may or may not be used. ****************************************************************************/ RCODE FSIndexCursor::setKeyPosition( FDB * pDb, FLMBOOL bGoingForward, KEYPOS * pInKeyPos, // Input key position KEYPOS * pOutKeyPos) // Output to setup the stack/key buffer. // It is ok for Input==Output { RCODE rc; FLMBYTE * pSearchKey; FLMBYTE pTempKey[ MAX_KEY_SIZ + 4]; FLMUINT uiTargetRecordId = pInKeyPos->uiRecordId; // May have to unuse the b-tree blocks. Then setup the stack. if( !pOutKeyPos->bStackInUse) { FSInitStackCache( pOutKeyPos->Stack, BH_MAX_LEVELS); pOutKeyPos->bStackInUse = TRUE; } if( pInKeyPos == pOutKeyPos) { pSearchKey = pTempKey; f_memcpy( pTempKey, pInKeyPos->pKey, pInKeyPos->uiKeyLen); } else { pSearchKey = pInKeyPos->pKey; } // Setup the stack. pOutKeyPos->pStack = pOutKeyPos->Stack; pOutKeyPos->Stack[0].pKeyBuf = pOutKeyPos->pKey; // All of the variables should be setup for the search. if( RC_BAD( rc = FSBtSearch( pDb, m_pLFile, &pOutKeyPos->pStack, pSearchKey, pInKeyPos->uiKeyLen, uiTargetRecordId ? DRN_DOMAIN( uiTargetRecordId) + 1 : pInKeyPos->uiDomain))) { goto Exit; } pOutKeyPos->uiBlockAddr = pOutKeyPos->pStack->uiBlkAddr; pOutKeyPos->uiCurElm = pOutKeyPos->pStack->uiCurElm; if( pOutKeyPos->pStack->uiBlkAddr == BT_END) { pOutKeyPos->bStackInUse = FALSE; rc = RC_SET( FERR_EOF_HIT); goto Exit; } pOutKeyPos->uiKeyLen = pOutKeyPos->pStack->uiKeyLen; if( bGoingForward) { if( pOutKeyPos->pStack->uiCmpStatus == BT_END_OF_DATA) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } } else { BTSK * pStack; // Going backwards or to the last. May have positioned too far. if( pOutKeyPos->pStack->uiCmpStatus == BT_END_OF_DATA || FS_COMPARE_KEYS( TRUE, pOutKeyPos, TRUE, pInKeyPos) > 0) { uiTargetRecordId = 0; pStack = pOutKeyPos->pStack; // The stack should be set up and is pointing to a valid block. if( pStack->uiCmpStatus != BT_END_OF_DATA) { while( BBE_NOT_FIRST( CURRENT_ELM( pStack))) { if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } } } // Position to the last continuation element of the previous element. if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } } } // When using the positioning index, we must always have a complete stack. if( !(m_pIxd->uiFlags & IXD_POSITIONING)) { pOutKeyPos->pStack->uiFlags = NO_STACK; } // Get the record ID at the leaf level of the b-tree. if( uiTargetRecordId) { pOutKeyPos->uiRecordId = pInKeyPos->uiRecordId; rc = FSRefSearch( pOutKeyPos->pStack, &pOutKeyPos->DinState, &pOutKeyPos->uiRecordId); if( rc == FERR_FAILURE) { rc = FERR_OK; } } else { if( bGoingForward) { pOutKeyPos->uiRecordId = FSRefFirst( pOutKeyPos->pStack, &pOutKeyPos->DinState, &pOutKeyPos->uiDomain); pOutKeyPos->uiDomain++; } else { pOutKeyPos->uiRecordId = FSRefLast( pOutKeyPos->pStack, &pOutKeyPos->DinState, &pOutKeyPos->uiDomain); } } Exit: // Save state only on a good return value. if( RC_OK( rc) || ((rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) && pOutKeyPos->bStackInUse)) { setKeyItemsFromBlock( pOutKeyPos); } else { releaseKeyBlocks( pOutKeyPos); } return( rc); } /**************************************************************************** Desc: Return the current record and record id. VISIT: We may want to return BOF/EOF when positioned on an endpoint. ****************************************************************************/ RCODE FSIndexCursor::currentKey( FDB * pDb, FlmRecord ** ppRecordKey, // Will replace what is there FLMUINT * puiRecordId) // Set the record ID { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { rc = RC_SET( FERR_BOF_HIT); goto Exit; } if( m_bAtEOF) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { // The current key is gone. Returns FERR_NOT_FOUND. goto Exit; } } // If this assert happens we have to code for it. flmAssert( m_curKeyPos.uiRecordId != 0); if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } Exit: return( rc); } /**************************************************************************** Desc: Return the current record and record id. VISIT: We may want to return BOF/EOF when positioned on an endpoint. ****************************************************************************/ RCODE FSIndexCursor::currentKeyBuf( FDB * pDb, F_Pool * pPool, FLMBYTE ** ppKeyBuf, FLMUINT * puiKeyLen, FLMUINT * puiRecordId, FLMUINT * puiContainerId) { RCODE rc = FERR_OK; FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { rc = RC_SET( FERR_BOF_HIT); goto Exit; } if( m_bAtEOF) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { // The current key is gone. Returns FERR_NOT_FOUND. goto Exit; } } // If this assert happens we have to code for it. flmAssert( m_curKeyPos.uiRecordId != 0); if( ppKeyBuf) { // If they passed in a non-null key buffer pointer, they also // need to pass in a non-null return length. flmAssert( puiKeyLen != NULL); if( (*puiKeyLen = m_curKeyPos.uiKeyLen) != 0) { if( RC_BAD( rc = pPool->poolAlloc( m_curKeyPos.uiKeyLen, (void **)ppKeyBuf))) { goto Exit; } f_memcpy( *ppKeyBuf, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen); } else { *ppKeyBuf = NULL; } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if (puiContainerId) { if ((*puiContainerId = m_pIxd->uiContainerNum) == 0) { flmAssert( m_curKeyPos.uiKeyLen > getIxContainerPartLen( m_pIxd)); *puiContainerId = getContainerFromKey( m_curKeyPos.pKey, m_curKeyPos.uiKeyLen); } } Exit: return( rc); } /**************************************************************************** Desc: Position to and return the first record. This is hard because positioning using the first key may actually position past or into another FROM/UNTIL set in the list. ****************************************************************************/ RCODE FSIndexCursor::firstKey( FDB * pDb, FlmRecord ** ppRecordKey, // Will replace what is there FLMUINT * puiRecordId) // Set the record ID { RCODE rc; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( !m_pFirstSet) { m_bAtBOF = FALSE; m_bAtEOF = TRUE; rc = RC_SET( FERR_EOF_HIT); goto Exit; } // If at BOF and stack is in use, the key is the first key! // This should only happen when firstKey is called the first time. if( m_bAtBOF && m_curKeyPos.bStackInUse && m_pCurSet) { m_bAtBOF = FALSE; } else { m_pCurSet = m_pFirstSet; m_bAtBOF = m_bAtEOF = FALSE; if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &m_pCurSet->fromKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } // Check to see if the key is within one of the FROM/UNTIL sets. for(;;) { // Check to see if the current key <= UNTIL key. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_pCurSet->untilKey) <= 0) { break; } // Nope - UntilKey < current key. if( !m_pCurSet->pNext) { rc = RC_SET( FERR_EOF_HIT); m_bAtEOF = TRUE; goto Exit; } m_pCurSet = m_pCurSet->pNext; // If (current key < FROM key of the next set) we need to reposition. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, FALSE, &m_pCurSet->fromKey) < 0) { if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &m_pCurSet->fromKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( m_bAtEOF) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the next key and the first reference of that key. ****************************************************************************/ RCODE FSIndexCursor::nextKey( FDB * pDb, FlmRecord ** ppRecordKey, FLMUINT * puiRecordId) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; BTSK * pStack; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtEOF) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } if( m_bAtBOF) { rc = firstKey( pDb, ppRecordKey, puiRecordId); goto Exit; } bKeyGone = bRefGone = FALSE; // Takes care of any re-read of a block if we changed transactions. if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, TRUE, FALSE, &bKeyGone, TRUE, FALSE, &bRefGone))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } // May return FERR_EOF_HIT if all remaining keys are deleted. goto Exit; } } pStack = m_curKeyPos.pStack; for(;;) { FLMINT iCmp; if( !bKeyGone) { FLMBYTE * pCurElm; pCurElm = CURRENT_ELM( pStack); while( BBE_NOT_LAST( pCurElm)) { if( RC_BAD( rc = FSBtNextElm( pDb, m_pLFile, pStack))) { // b-tree corrupt if FERR_BT_END_OF_DATA if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } pCurElm = CURRENT_ELM( pStack); } // Now go to the next element. if( RC_BAD( rc = FSBtNextElm( pDb, m_pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { m_bAtEOF = TRUE; rc = RC_SET( FERR_EOF_HIT); } goto Exit; } bKeyGone = TRUE; m_curKeyPos.uiKeyLen = m_curKeyPos.pStack->uiKeyLen; } // Could have positioned after the current sets until key before // the FROM key of the next set. iCmp = FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_pCurSet->untilKey); if( iCmp <= 0) { setKeyItemsFromBlock( &m_curKeyPos); m_curKeyPos.uiRecordId = FSRefFirst( pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiDomain); break; } // Go to the next set if there is one. if( !m_pCurSet->pNext) { m_bAtEOF = TRUE; rc = RC_SET( FERR_EOF_HIT); goto Exit; } m_pCurSet = m_pCurSet->pNext; // The key may fit in the next set. iCmp = FS_COMPARE_KEYS( FALSE, &m_curKeyPos, FALSE, &m_pCurSet->fromKey); if( iCmp < 0) { // Reposition using the from key. if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &m_pCurSet->fromKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( m_bAtEOF) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the next referece of the current key. ****************************************************************************/ RCODE FSIndexCursor::nextRef( // FERR_OK, FERR_EOF_HIT or error FDB * pDb, FLMUINT * puiRecordId) // Set the record ID { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_pCurSet != NULL); bKeyGone = bRefGone = FALSE; if( !m_curKeyPos.bStackInUse) { // Take care of any re-read of a block if we changed transactions. if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, TRUE, FALSE, &bRefGone))) { // May return FERR_EOF_HIT if all remaining references are deleted. goto Exit; } flmAssert( !bKeyGone); } if( !bRefGone) { if( RC_BAD( rc = FSRefNext( pDb, m_pLFile, m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiRecordId))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_EOF_HIT); } goto Exit; } else { setKeyItemsFromBlock( &m_curKeyPos); } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } Exit: return( rc); } /**************************************************************************** Desc: Position to and return the last record. This is hard because positioning using the first key may actually position past or into another FROM/UNTIL set in the list. ****************************************************************************/ RCODE FSIndexCursor::lastKey( FDB * pDb, FlmRecord ** ppRecordKey, // Will replace what is there FLMUINT * puiRecordId) // Set the record ID { RCODE rc; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } m_pCurSet = m_pFirstSet; if( !m_pCurSet) { m_bAtBOF = TRUE; m_bAtEOF = FALSE; rc = RC_SET( FERR_BOF_HIT); goto Exit; } // Position to the last set. while( m_pCurSet->pNext) { m_pCurSet = m_pCurSet->pNext; } m_bAtBOF = m_bAtEOF = FALSE; for(;;) { if( RC_BAD( rc = setKeyPosition( pDb, FALSE, &m_pCurSet->untilKey, &m_curKeyPos))) { // Returns an error or FERR_EOF_HIT. if( rc != FERR_EOF_HIT) { goto Exit; } // Position to the previous key if possible. if( m_curKeyPos.pStack->uiBlkAddr == BT_END) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); goto Exit; } // Went past the until key. Go back one. if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, m_curKeyPos.pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } } // We may have positioned before the FROM key of this set. if( FS_COMPARE_KEYS( FALSE, &m_pCurSet->fromKey, FALSE, &m_curKeyPos) <= 0) { break; } if( !m_pCurSet->pPrev) { rc = RC_SET( FERR_BOF_HIT); m_bAtBOF = TRUE; goto Exit; } m_pCurSet = m_pCurSet->pPrev; } // Now we are positioned somewhere. Return the key and first record id. m_curKeyPos.uiRecordId = FSRefLast( m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiDomain); setKeyItemsFromBlock( &m_curKeyPos); if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( rc == FERR_BOF_HIT) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the PREVIOUS key and the LAST reference of that key. ****************************************************************************/ RCODE FSIndexCursor::prevKey( FDB * pDb, FlmRecord ** ppRecordKey, FLMUINT * puiRecordId) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; BTSK * pStack = m_curKeyPos.pStack; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { rc = RC_SET( FERR_BOF_HIT); goto Exit; } if( !m_pCurSet || m_bAtEOF) { rc = lastKey( pDb, ppRecordKey, puiRecordId); goto Exit; } bKeyGone = bRefGone = FALSE; // Takes care of any re-read of a block if we changed transactions. if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, FALSE, TRUE, &bKeyGone, FALSE, FALSE, &bRefGone))) { // May return FERR_EOF_HIT if all remaining keys are deleted. if( rc != FERR_BOF_HIT && rc != FERR_EOF_HIT) { goto Exit; } m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); } } for(;;) { if( !bKeyGone) { FLMBYTE * pCurElm; pCurElm = CURRENT_ELM( pStack); while( BBE_NOT_FIRST( pCurElm)) { if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { // b-tree corrupt if FERR_BT_END_OF_DATA if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } pCurElm = CURRENT_ELM( pStack); } // Now go to the previous element. if( RC_BAD(rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { // b-tree corrupt if FERR_BT_END_OF_DATA if( rc == FERR_BT_END_OF_DATA) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); } goto Exit; } bKeyGone = TRUE; m_curKeyPos.uiKeyLen = m_curKeyPos.pStack->uiKeyLen; } // Could have positioned after the current sets until key before // the FROM key of the next set. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_pCurSet->fromKey) >= 0) { setKeyItemsFromBlock( &m_curKeyPos); m_curKeyPos.uiRecordId = FSRefLast( pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiDomain); break; } // Go to the previous set if there is one. if( !m_pCurSet->pPrev) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); goto Exit; } m_pCurSet = m_pCurSet->pPrev; // The key may fit in the previous set. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, FALSE, &m_pCurSet->fromKey) > 0) { // Reposition using the from key. if( RC_BAD( rc = setKeyPosition( pDb, FALSE, &m_pCurSet->untilKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); } goto Exit; } } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( rc == FERR_BOF_HIT) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the previous referece of the current key. ****************************************************************************/ RCODE FSIndexCursor::prevRef( FDB * pDb, FLMUINT * puiRecordId) // Set the record ID { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; flmAssert( m_pCurSet != NULL); if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } bKeyGone = bRefGone = FALSE; if( !m_curKeyPos.bStackInUse) { // Take care of any re-read of a block if we changed transactions. if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, TRUE, &bRefGone))) { // May return FERR_EOF_HIT if all preceeding references are deleted. if( rc == FERR_EOF_HIT) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } flmAssert( !bKeyGone); } if( !bRefGone) { if( RC_BAD( rc = FSRefPrev( pDb, m_pLFile, m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiRecordId))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } else { setKeyItemsFromBlock( &m_curKeyPos); } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } Exit: return( rc); } /**************************************************************************** Desc: Reposition to the current key + recordId. If the current key is gone we may reposition past the UNTIL key and should check. ****************************************************************************/ RCODE FSIndexCursor::reposition( FDB * pDb, FLMBOOL bCanPosToNextKey, // May be TRUE if bPosToPrevKey is FALSE FLMBOOL bCanPosToPrevKey, // May be TRUE if bPosToNextKey is FALSE FLMBOOL * pbKeyGone, // [out] cannot be NULL FLMBOOL bCanPosToNextRef, // May be TRUE if bPosToPrevRef is FALSE FLMBOOL bCanPosToPrevRef, // May be TRUE if bPosToNextRef is FALSE FLMBOOL * pbRefGone) // [out] cannot be NULL { RCODE rc = FERR_OK; FLMUINT uiBlkTransId = 0; FLMBOOL bReread = FALSE; FLMUINT uiTargetRecordId; uiTargetRecordId = m_curKeyPos.uiRecordId; // May have to unuse the b-tree blocks. Then setup the stack again. flmAssert( !m_curKeyPos.bStackInUse); *pbKeyGone = *pbRefGone = FALSE; // Re-read the block and see if it is the same block. if( m_curKeyPos.uiBlockAddr == BT_END) { bReread = TRUE; } else { if( RC_BAD( rc = FSGetBlock( pDb, m_pLFile, m_curKeyPos.uiBlockAddr, m_curKeyPos.pStack))) { if( rc != FERR_DATA_ERROR) { goto Exit; } rc = FERR_OK; bReread = TRUE; } else { uiBlkTransId = FB2UD( &m_curKeyPos.pStack->pBlk[ BH_TRANS_ID]); m_curKeyPos.bStackInUse = TRUE; } } if( m_curKeyPos.uiBlockTransId != uiBlkTransId) { bReread = TRUE; } else if( pDb->uiTransType == FLM_UPDATE_TRANS) { bReread = TRUE; } if( bReread) { FLMBYTE pKey [MAX_KEY_SIZ + 4]; FLMUINT uiKeyLen = m_curKeyPos.uiKeyLen; FLMUINT uiSaveRecId = m_curKeyPos.uiRecordId; f_memcpy( pKey, m_curKeyPos.pKey, uiKeyLen); // This may be a new read transaction. Call BTSearch. // The current reference or current key may go away on if( RC_BAD( rc = setKeyPosition( pDb, bCanPosToPrevKey ? FALSE : TRUE, &m_curKeyPos, &m_curKeyPos))) { if( rc != FERR_EOF_HIT && rc != FERR_BOF_HIT) { goto Exit; } } if( RC_BAD( rc) || uiKeyLen != m_curKeyPos.uiKeyLen || f_memcmp( pKey, m_curKeyPos.pKey, uiKeyLen)) { // It is OK that we may not be positioned inside of the current set. *pbKeyGone = TRUE; *pbRefGone = TRUE; if( !bCanPosToNextKey && !bCanPosToPrevKey) { if( uiKeyLen) { f_memcpy( m_curKeyPos.pKey, pKey, uiKeyLen); } m_curKeyPos.uiKeyLen = uiKeyLen; m_curKeyPos.uiRecordId = uiSaveRecId; releaseKeyBlocks( &m_curKeyPos); m_curKeyPos.uiBlockAddr = BT_END; if( bCanPosToNextKey || bCanPosToNextRef) { rc = RC_SET( FERR_EOF_HIT); } else if( bCanPosToPrevKey || bCanPosToPrevRef) { rc = RC_SET( FERR_BOF_HIT); } else { rc = RC_SET( FERR_NOT_FOUND); } } goto Exit; } } else { m_curKeyPos.bStackInUse = TRUE; } if (uiTargetRecordId && uiTargetRecordId != m_curKeyPos.uiRecordId) { *pbRefGone = TRUE; if( bCanPosToPrevRef && m_curKeyPos.uiRecordId < uiTargetRecordId) { if (RC_OK( rc = FSRefPrev( pDb, m_pLFile, m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiRecordId))) { setKeyItemsFromBlock( &m_curKeyPos); } } else if( !bCanPosToNextRef) { // We have an error positioning. Return NOT_FOUND rc = RC_SET( FERR_NOT_FOUND); } } Exit: return( rc); } /**************************************************************************** Desc: Save the current key position. ****************************************************************************/ void FSIndexCursor::saveCurrKeyPos( KEYPOS * pSaveKeyPos) { f_memcpy( pSaveKeyPos->pKey, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen); pSaveKeyPos->uiKeyLen = m_curKeyPos.uiKeyLen; pSaveKeyPos->uiRecordId = m_curKeyPos.uiRecordId; pSaveKeyPos->uiDomain = m_curKeyPos.uiDomain; } /**************************************************************************** Desc: Restore the current key position. ****************************************************************************/ void FSIndexCursor::restoreCurrKeyPos( KEYPOS * pSaveKeyPos) { f_memcpy( m_curKeyPos.pKey, pSaveKeyPos->pKey, pSaveKeyPos->uiKeyLen); m_curKeyPos.uiKeyLen = pSaveKeyPos->uiKeyLen; m_curKeyPos.uiRecordId = pSaveKeyPos->uiRecordId; m_curKeyPos.uiDomain = pSaveKeyPos->uiDomain; } /**************************************************************************** Desc: Find the key set for the passed in key. ****************************************************************************/ RCODE FSIndexCursor::getKeySet( FLMBYTE * pKey, FLMUINT uiKeyLen, KEYSET ** ppKeySet) { RCODE rc = FERR_OK; KEYSET * pKeySet; pKeySet = m_pFirstSet; while (pKeySet) { // Compare this key against the from key. If it is less than the // from key, we are not inside one of the key ranges. if( FSCompareKeys( FALSE, pKey, uiKeyLen, FALSE, FALSE, pKeySet->fromKey.pKey, pKeySet->fromKey.uiKeyLen, pKeySet->fromKey.bExclusiveKey) < 0) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } // Key is >= from key, see how it compares to until key. if( FSCompareKeys( FALSE, pKey, uiKeyLen, FALSE, TRUE, pKeySet->untilKey.pKey, pKeySet->untilKey.uiKeyLen, pKeySet->untilKey.bExclusiveKey) <= 0) { break; } // Go to the next key range. pKeySet = pKeySet->pNext; } if( !pKeySet) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: *ppKeySet = pKeySet; return( rc); } /**************************************************************************** Desc: Position to the input key + recordId. ****************************************************************************/ RCODE FSIndexCursor::positionTo( FDB * pDb, FLMBYTE * pKey, FLMUINT uiKeyLen, FLMUINT uiRecordId) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; KEYSET * pKeySet; KEYPOS * pSaveKeyPos = NULL; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } // A key with a non-zero key length must be passed in. flmAssert( uiKeyLen != 0); // Make sure we fall inside one of the key ranges and // save the current key position so we can restore it // if the reposition call fails. if( RC_BAD( rc = f_alloc( sizeof( KEYPOS), &pSaveKeyPos))) { goto Exit; } if (RC_BAD( rc = getKeySet( pKey, uiKeyLen, &pKeySet))) { goto Exit; } saveCurrKeyPos( pSaveKeyPos); releaseKeyBlocks( &m_curKeyPos); m_curKeyPos.uiKeyLen = uiKeyLen; f_memcpy( m_curKeyPos.pKey, pKey, uiKeyLen); m_curKeyPos.uiRecordId = uiRecordId; m_curKeyPos.uiDomain = DRN_DOMAIN( uiRecordId) + 1; m_curKeyPos.uiBlockAddr = BT_END; if (RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { restoreCurrKeyPos( pSaveKeyPos); goto Exit; } m_bAtEOF = FALSE; m_bAtBOF = FALSE; m_pCurSet = pKeySet; Exit: if (pSaveKeyPos) { f_free( &pSaveKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the input key + domain ****************************************************************************/ RCODE FSIndexCursor::positionToDomain( FDB * pDb, FLMBYTE * pKey, FLMUINT uiKeyLen, FLMUINT uiDomain) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; KEYSET * pKeySet; KEYPOS * pSaveKeyPos = NULL; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } // A key with a non-zero key length must be passed in. flmAssert( uiKeyLen != 0); // Make sure we fall inside one of the key ranges and // save the current key position so we can restore it // if the reposition call fails. if( RC_BAD( rc = f_alloc( sizeof( KEYPOS), &pSaveKeyPos))) { goto Exit; } if (RC_BAD( rc = getKeySet( pKey, uiKeyLen, &pKeySet))) { goto Exit; } saveCurrKeyPos( pSaveKeyPos); releaseKeyBlocks( &m_curKeyPos); m_curKeyPos.uiKeyLen = uiKeyLen; f_memcpy( m_curKeyPos.pKey, pKey, uiKeyLen); m_curKeyPos.uiRecordId = 0; m_curKeyPos.uiDomain = uiDomain; m_curKeyPos.uiBlockAddr = BT_END; if (RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { restoreCurrKeyPos( pSaveKeyPos); goto Exit; } m_bAtEOF = FALSE; m_bAtBOF = FALSE; m_pCurSet = pKeySet; Exit: if (pSaveKeyPos) { f_free( &pSaveKeyPos); } return( rc); } /**************************************************************************** Desc: Save the current position. ****************************************************************************/ RCODE FSIndexCursor::savePosition( void) { RCODE rc = FERR_OK; if( !m_pSavedPos) { if( RC_BAD( rc = f_calloc( sizeof( KEYPOS), &m_pSavedPos))) { goto Exit; } } f_memcpy( m_pSavedPos, &m_curKeyPos, sizeof( KEYPOS)); m_curKeyPos.bStackInUse = FALSE; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSIndexCursor::restorePosition( void) { if( m_pSavedPos) { releaseKeyBlocks( &m_curKeyPos); f_memcpy( &m_curKeyPos, m_pSavedPos, sizeof(KEYPOS)); } return (FERR_OK); } /**************************************************************************** Desc: Compare two key positions as they would be ordered in the index. ****************************************************************************/ FSTATIC FLMINT FSCompareKeys( // negative is '<', 0 is '=', position='>' FLMBOOL bKey1IsUntilKey, // TRUE (until key) or FALSE (from key) FLMBYTE * pKey1, FLMUINT uiKeyLen1, FLMBOOL bExclusiveKey1, FLMBOOL bKey2IsUntilKey, FLMBYTE * pKey2, FLMUINT uiKeyLen2, FLMBOOL bExclusiveKey2) { FLMINT iCmp; // Handle the first key and last key issues. if( !uiKeyLen1) // FROM or UNTIL key at end point? { if( !bKey1IsUntilKey) // FROM key { iCmp = !uiKeyLen2 && !bKey2IsUntilKey ? 0 : -1; } else // UNTIL key { iCmp = !bKey2IsUntilKey ? 1 : (!uiKeyLen2 ? 0 : 1); } } else if( !uiKeyLen2) { if( !bKey2IsUntilKey) // FROM key { iCmp = !uiKeyLen1 && !bKey1IsUntilKey ? 0 : 1; } else // UNTIL key { iCmp = !bKey1IsUntilKey ? -1 : (!uiKeyLen1 ? 0 : -1); } } else if( uiKeyLen1 > uiKeyLen2) { // Compare the key buffers. No FIRST or LAST key now. if( (iCmp = f_memcmp( pKey1, pKey2, uiKeyLen2)) == 0) { iCmp = 1; } } else if( uiKeyLen1 < uiKeyLen2) { if( (iCmp = f_memcmp( pKey1, pKey2, uiKeyLen1)) == 0) { iCmp = -1; } } else { if( (iCmp = f_memcmp( pKey1, pKey2, uiKeyLen1)) == 0) { // The keys are EQUAL. // bExclusiveKey ONLY applies to an UNTIL key. // Check the exclusive flag and THEN if needed the uiRecordId. if( !bKey1IsUntilKey) { if( bKey2IsUntilKey && bExclusiveKey2) { iCmp = 1; } } else if( !bKey2IsUntilKey) { if( bKey1IsUntilKey && bExclusiveKey1) { iCmp = -1; } } else // both are until keys. { if( bExclusiveKey1 != bExclusiveKey2) { iCmp = bExclusiveKey1 ? -1 : 1; } } } } return (iCmp); } /**************************************************************************** Desc: Allocate and return the first and last keys in an index. ****************************************************************************/ RCODE FSIndexCursor::getFirstLastKeys( FLMBYTE ** ppFirstKey, FLMUINT * puiFirstKeyLen, FLMBYTE ** ppLastKey, FLMUINT * puiLastKeyLen, FLMBOOL * pbLastKeyExclusive) { RCODE rc = FERR_OK; KEYSET * pCurSet = m_pFirstSet; if( !pCurSet) { *ppFirstKey = *ppLastKey = NULL; *puiFirstKeyLen = 0; *pbLastKeyExclusive = TRUE; goto Exit; } if( RC_BAD( rc = f_alloc( pCurSet->fromKey.uiKeyLen, ppFirstKey))) { goto Exit; } *puiFirstKeyLen = pCurSet->fromKey.uiKeyLen; f_memcpy( *ppFirstKey, pCurSet->fromKey.pKey, *puiFirstKeyLen); while( pCurSet->pNext) { pCurSet = pCurSet->pNext; } if( RC_BAD( rc = f_alloc( pCurSet->untilKey.uiKeyLen, ppLastKey))) { if( *ppFirstKey) { f_free( ppFirstKey); } goto Exit; } *puiLastKeyLen = pCurSet->untilKey.uiKeyLen; f_memcpy( *ppLastKey, pCurSet->untilKey.pKey, *puiLastKeyLen); *pbLastKeyExclusive = pCurSet->untilKey.bExclusiveKey; Exit: return( rc); } /**************************************************************************** Desc: Set the absolute position in a positioning index relative to the FROM/UNTIL keys in all of the key sets. Supports multiple key sets. ****************************************************************************/ RCODE FSIndexCursor::setAbsolutePosition( FDB * pDb, FLMUINT uiRefPosition) // 0 -> BOF, -1 -> EOF points, one based { RCODE rc = FERR_OK; FLMUINT uiBtreeRefPosition; KEYSET * pTempSet; if( !isAbsolutePositionable()) { flmAssert(0); rc = RC_SET( FERR_FAILURE); goto Exit; } if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } // Zero value position to BOF if( uiRefPosition == 0 || uiRefPosition == 1) { // Go to the first reference. if( RC_OK( rc = firstKey( pDb, NULL, NULL))) { if( uiRefPosition == 0) { // Should return FERR_BOF_HIT rc = prevKey( pDb, NULL, NULL); } } goto Exit; } // High-values value position to EOF if( uiRefPosition == (FLMUINT) -1) { // Position to EOF if( RC_OK( rc = lastKey( pDb, NULL, NULL))) { // Should return FERR_EOF_HIT rc = nextKey( pDb, NULL, NULL); } goto Exit; } // Find the set that contains this absolute position. uiBtreeRefPosition = 0; for( pTempSet = m_pFirstSet; pTempSet; pTempSet = pTempSet->pNext) { FLMUINT uiTotalInSet; uiTotalInSet = pTempSet->untilKey.uiRefPosition - pTempSet->fromKey.uiRefPosition; if( uiTotalInSet < uiRefPosition) { uiRefPosition -= uiTotalInSet; } else { m_pCurSet = pTempSet; uiBtreeRefPosition = pTempSet->fromKey.uiRefPosition + uiRefPosition - 1; break; } } if( !uiBtreeRefPosition) { // Position to EOF if( RC_OK( rc = lastKey( pDb, NULL, NULL))) { // Should return FERR_EOF_HIT rc = nextKey( pDb, NULL, NULL); } goto Exit; } m_curKeyPos.pStack = m_curKeyPos.Stack; RESET_DINSTATE( m_curKeyPos.DinState); m_curKeyPos.Stack[0].pKeyBuf = m_curKeyPos.pKey; if( RC_BAD( rc = FSPositionSearch( pDb, m_pLFile, uiBtreeRefPosition, &m_curKeyPos.pStack, &m_curKeyPos.uiRecordId, &m_curKeyPos.uiDomain, &m_curKeyPos.DinState))) { goto Exit; } m_curKeyPos.bStackInUse = TRUE; setKeyItemsFromBlock( &m_curKeyPos); m_bAtBOF = m_bAtEOF = FALSE; Exit: return( rc); } /**************************************************************************** Desc: Get the absolute position in a positioning index. Supports multiple sets. ****************************************************************************/ RCODE FSIndexCursor::getAbsolutePosition( FDB * pDb, FLMUINT * puiRefPosition) // 0 -> BOF, -1 -> EOF points, one based { RCODE rc = FERR_OK; FLMUINT uiRefPosition = 0; // Position relative to the FROM key sets. FLMUINT uiBtreeRefPosition; // True absolute position in the btree. KEYSET * pTempSet; if( !isAbsolutePositionable()) { flmAssert(0); rc = RC_SET( FERR_FAILURE); goto Exit; } if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { *puiRefPosition = 0; goto Exit; } if( m_bAtEOF) { *puiRefPosition = (FLMUINT) -1; goto Exit; } if( !m_curKeyPos.bStackInUse) { FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { if( rc != FERR_NOT_FOUND) goto Exit; rc = FERR_OK; } } // Compute where we are relative the the reference position of the current set. if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_curKeyPos.pStack, &m_curKeyPos.DinState, &uiBtreeRefPosition))) { goto Exit; } uiRefPosition = (uiBtreeRefPosition - m_pCurSet->fromKey.uiRefPosition) + 1; for( pTempSet = m_pCurSet->pPrev; pTempSet; pTempSet = pTempSet->pPrev) { FLMUINT uiTemp; uiTemp = pTempSet->untilKey.uiRefPosition - pTempSet->fromKey.uiRefPosition; uiRefPosition += uiTemp; } Exit: if( RC_OK( rc)) { *puiRefPosition = uiRefPosition; } return( rc); } /**************************************************************************** Desc: Get the total number of reference for all of the sets. ****************************************************************************/ RCODE FSIndexCursor::getTotalReferences( FDB * pDb, FLMUINT * puiTotalRefs, FLMBOOL * pbTotalsEstimated) { RCODE rc = FERR_OK; KEYSET * pKeySet; // Current source set KEYSET * pTempSet = NULL; FLMUINT uiTotalRefs = 0; FLMUINT uiRefCount; FLMUINT uiStackPos; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } *pbTotalsEstimated = FALSE; // We have to be careful not to change the current position in any way. for( pKeySet = m_pFirstSet; pKeySet; pKeySet = pKeySet->pNext) { // If this is an positioning index, the ref positions MAY be set up. if( pKeySet->fromKey.uiRefPosition && pKeySet->untilKey.uiRefPosition) { uiTotalRefs += pKeySet->untilKey.uiRefPosition - pKeySet->fromKey.uiRefPosition; continue; } else if( !pTempSet) { if( RC_BAD( rc = f_calloc( sizeof( KEYSET), &pTempSet))) { goto Exit; } } // Compute the counts the old fashion way. f_memcpy( pTempSet, pKeySet, sizeof( KEYSET)); for( uiStackPos = 0; uiStackPos < BH_MAX_LEVELS; uiStackPos++) { pTempSet->fromKey.Stack[uiStackPos].pSCache = NULL; pTempSet->fromKey.Stack[uiStackPos].pBlk = NULL; pTempSet->untilKey.Stack[uiStackPos].pSCache = NULL; pTempSet->untilKey.Stack[uiStackPos].pBlk = NULL; } pTempSet->fromKey.bStackInUse = FALSE; pTempSet->untilKey.bStackInUse = FALSE; // Search down the tree and get the counts. if( RC_OK( rc = setKeyPosition( pDb, TRUE, &pTempSet->fromKey, &pTempSet->fromKey))) { // All keys bewteen low and high may be gone. if( FS_COMPARE_KEYS( FALSE, &pTempSet->fromKey, TRUE, &pKeySet->untilKey) > 0) { rc = RC_SET( FERR_BOF_HIT); } else { rc = setKeyPosition( pDb, FALSE, &pTempSet->untilKey, &pTempSet->untilKey); } } if( RC_BAD( rc)) { // Empty tree case. if( rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { rc = FERR_OK; } else { goto Exit; } } else { if( RC_BAD( rc = FSComputeIndexCounts( pTempSet->fromKey.pStack, pTempSet->untilKey.pStack, NULL, NULL, &uiRefCount, pbTotalsEstimated))) { goto Exit; } uiTotalRefs += uiRefCount; } releaseKeyBlocks( &pTempSet->fromKey); releaseKeyBlocks( &pTempSet->untilKey); } Exit: if( pTempSet) { releaseKeyBlocks( &pTempSet->fromKey); releaseKeyBlocks( &pTempSet->untilKey); f_free( &pTempSet); } if( RC_OK(rc)) { *puiTotalRefs = uiTotalRefs; } return( rc); }