git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@509 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2805 lines
63 KiB
C++
2805 lines
63 KiB
C++
//-------------------------------------------------------------------------
|
|
// 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);
|
|
}
|