Files
mars-flaim/sql/src/fscursor.cpp
dsandersoremutah a7e6b942f8 Latest round of changes for SQL query optimization.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@709 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-07-25 22:40:53 +00:00

1333 lines
28 KiB
C++

//------------------------------------------------------------------------------
// Desc: Cursor routines to get the complexity of the file system out
// of the search code.
//
// 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$
//------------------------------------------------------------------------------
#include "flaimsys.h"
#include "fscursor.h"
FSTATIC RCODE copyKey(
KEYPOS * pDestKey,
F_DataVector * pDestSrchKey,
KEYPOS * pSrcKey,
F_DataVector * pSrcSrchKey);
/****************************************************************************
Desc:
****************************************************************************/
FSIndexCursor::FSIndexCursor()
{
m_pbTree = NULL;
m_bTreeOpen = FALSE;
m_pucCurKeyDataBuf = NULL;
m_uiCurKeyDataBufSize = 0;
m_uiCurKeyDataLen = 0;
m_pIndex = NULL;
m_pLFile = NULL;
m_pDb = NULL;
m_eTransType = SFLM_NO_TRANS;
resetCursor();
}
/****************************************************************************
Desc:
****************************************************************************/
FSIndexCursor::~FSIndexCursor()
{
closeBTree();
if (m_pucCurKeyDataBuf)
{
f_free( &m_pucCurKeyDataBuf);
}
if (m_pbTree)
{
gv_SFlmSysData.pBtPool->btpReturnBtree( &m_pbTree);
}
}
/****************************************************************************
Desc: Resets any allocations, keys, state, etc.
****************************************************************************/
void FSIndexCursor::resetCursor( void)
{
closeBTree();
m_uiIndexNum = 0;
m_uiBlkChangeCnt = 0;
m_ui64CurrTransId = 0;
m_curKey.uiKeyLen = 0;
m_uiCurKeyDataLen = 0;
m_bAtBOF = TRUE;
m_bAtEOF = FALSE;
m_bSetup = FALSE;
m_bDoRowMatch = FALSE;
m_bCanCompareOnKey = TRUE;
m_ui64Cost = 0;
m_ui64LeafBlocksBetween = 0;
m_ui64TotalRefs = 0;
m_bTotalsEstimated = FALSE;
}
/****************************************************************************
Desc: Resets to a new transaction that may change the read consistency of
the query.
****************************************************************************/
RCODE FSIndexCursor::resetTransaction(
F_Db * pDb)
{
RCODE rc = NE_SFLM_OK;
LFILE * pLFile;
F_INDEX * pIndex;
if ((pIndex = pDb->m_pDict->getIndex( m_uiIndexNum)) == NULL)
{
rc = RC_SET( NE_SFLM_INVALID_INDEX_NUM);
goto Exit;
}
if (pIndex->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED))
{
rc = RC_SET( NE_SFLM_INDEX_OFFLINE);
goto Exit;
}
pLFile = &pIndex->lfInfo;
if (m_pDb != pDb || pLFile != m_pLFile || pIndex != m_pIndex)
{
m_pLFile = pLFile;
m_pIndex = pIndex;
if (m_bTreeOpen)
{
closeBTree();
}
m_pDb = pDb;
m_eTransType = pDb->m_eTransType;
}
m_ixCompare.setIxInfo( pDb, m_pIndex);
m_ui64CurrTransId = pDb->m_ui64CurrTransID;
m_uiBlkChangeCnt = pDb->m_uiBlkChangeCnt;
Exit:
return( rc);
}
/****************************************************************************
Desc: Estimate the cost of going between the from and until key and
fetching every row.
****************************************************************************/
RCODE FSIndexCursor::calculateCost( void)
{
RCODE rc = NE_SFLM_OK;
F_Btree * pUntilBTree = NULL;
FLMINT iCompare;
// Get a btree object
if (RC_BAD( rc = gv_SFlmSysData.pBtPool->btpReserveBtree( &pUntilBTree)))
{
goto Exit;
}
if (RC_OK( rc = setKeyPosition( m_pDb, TRUE, FALSE,
&m_fromExtKey, &m_fromKey, &m_curKey, TRUE, NULL, NULL, NULL)))
{
// All keys between FROM and UNTIL may be gone.
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE,
&m_untilExtKey, NULL,
m_curKey.ucKey, m_curKey.uiKeyLen,
&m_untilExtKey, NULL,
m_untilKey.ucKey, m_untilKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
if (iCompare <= 0)
{
KEYPOS tmpUntilKey;
if (RC_OK( rc = pUntilBTree->btOpen( m_pDb, m_pLFile,
isAbsolutePositionable(), FALSE,
&m_ixCompare)))
{
// Going forward so position is exclusive
rc = setKeyPosition( m_pDb, FALSE, FALSE,
&m_untilExtKey, &m_untilKey,
&tmpUntilKey, FALSE, NULL, pUntilBTree, NULL);
}
}
else
{
rc = RC_SET( NE_SFLM_BOF_HIT);
m_bAtBOF = TRUE;
m_bAtEOF = FALSE;
}
}
else
{
if (rc == NE_SFLM_EOF_HIT)
{
m_bAtEOF = TRUE;
m_bAtBOF = FALSE;
}
else if (rc == NE_SFLM_BOF_HIT)
{
m_bAtEOF = FALSE;
m_bAtBOF = TRUE;
}
}
if (RC_BAD( rc))
{
// Empty tree or empty set case.
if (rc == NE_SFLM_EOF_HIT || rc == NE_SFLM_BOF_HIT)
{
m_ui64Cost = 0;
m_ui64LeafBlocksBetween = 0;
m_ui64TotalRefs = 0;
m_bTotalsEstimated = FALSE;
rc = NE_SFLM_OK;
}
goto Exit;
}
else
{
if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree,
&m_ui64LeafBlocksBetween, &m_ui64TotalRefs,
&m_bTotalsEstimated,
(m_pDb->m_pDatabase->m_uiBlockSize * 3) / 4)))
{
goto Exit;
}
if ((m_ui64Cost = m_ui64LeafBlocksBetween + m_ui64TotalRefs) == 0)
{
m_ui64Cost = 1;
}
}
Exit:
if (pUntilBTree)
{
gv_SFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree);
}
return( rc);
}
/****************************************************************************
Desc: Setup the from and until keys in the cursor. Return counts
after positioning to the from and until key in the index.
****************************************************************************/
RCODE FSIndexCursor::setupKeys(
F_Db * pDb,
F_INDEX * pIndex,
F_TABLE * pTable,
SQL_PRED ** ppKeyComponents)
{
RCODE rc = NE_SFLM_OK;
m_uiIndexNum = pIndex->uiIndexNum;
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
if (RC_BAD( rc = flmBuildFromAndUntilKeys( pDb->getDict(), pIndex, pTable,
ppKeyComponents,
&m_fromExtKey, m_fromKey.ucKey, &m_fromKey.uiKeyLen,
&m_untilExtKey, m_untilKey.ucKey, &m_untilKey.uiKeyLen,
&m_bDoRowMatch, &m_bCanCompareOnKey)))
{
goto Exit;
}
m_curKey.uiKeyLen = 0;
m_bSetup = TRUE;
if (RC_BAD( rc = calculateCost()))
{
goto Exit;
}
m_bAtBOF = TRUE;
Exit:
return( rc);
}
/****************************************************************************
Desc: Set the destination key equal to the source key.
****************************************************************************/
FSTATIC RCODE copyKey(
KEYPOS * pDestKey,
F_DataVector * pDestSrchKey,
KEYPOS * pSrcKey,
F_DataVector * pSrcSrchKey)
{
RCODE rc = NE_SFLM_OK;
// Copy the key buffer.
if ((pDestKey->uiKeyLen = pSrcKey->uiKeyLen) != 0)
{
f_memcpy( pDestKey->ucKey, pSrcKey->ucKey, pSrcKey->uiKeyLen);
}
// Copy the data vector.
if (RC_BAD( rc = pDestSrchKey->copyVector( pSrcSrchKey)))
{
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Union the keys from another index cursor into this one.
****************************************************************************/
RCODE FSIndexCursor::unionKeys(
F_Db * pDb,
FSIndexCursor * pFSIndexCursor,
FLMBOOL * pbUnioned,
FLMINT * piCompare)
{
RCODE rc = NE_SFLM_OK;
FLMINT iCompare;
FLMBOOL bIncomingLessThan = FALSE;
FLMBOOL bIncomingGreaterThan = FALSE;
*pbUnioned = FALSE;
// Make sure both cursors are referencing the same index. If not,
// they cannot be unioned.
if (pFSIndexCursor->m_uiIndexNum != m_uiIndexNum)
{
goto Exit; // Will return NE_SFLM_OK
}
// Both cursors better already be setup
flmAssert( m_bSetup && pFSIndexCursor->m_bSetup);
// Set both cursors to have their m_pDb, m_pIndex, etc. set up.
// These are needed for comparison of keys.
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
if (RC_BAD( rc = pFSIndexCursor->checkTransaction( pDb)))
{
goto Exit;
}
// Compare the two from keys.
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE,
&pFSIndexCursor->m_fromExtKey, NULL,
pFSIndexCursor->m_fromKey.ucKey,
pFSIndexCursor->m_fromKey.uiKeyLen,
&m_fromExtKey, NULL,
m_fromKey.ucKey,
m_fromKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
if (iCompare < 0)
{
// If the incoming from key is less than our from key,
// see if the incoming until key is >= our from key.
// If so, they overlap.
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE,
&pFSIndexCursor->m_untilExtKey, NULL,
pFSIndexCursor->m_untilKey.ucKey,
pFSIndexCursor->m_untilKey.uiKeyLen,
&m_fromExtKey, NULL,
m_fromKey.ucKey,
m_fromKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
if (iCompare < 0)
{
// Incoming until key is less than our from key - no union.
*piCompare = -1;
goto Exit;
}
// Incoming from key is < our from key, and incoming until key
// is >= our from key - definitely an overlap, and we need to
// set our from key equal to the incoming from key.
*pbUnioned = TRUE;
if (RC_BAD( rc = copyKey( &m_fromKey, &m_fromExtKey,
&pFSIndexCursor->m_fromKey,
&pFSIndexCursor->m_fromExtKey)))
{
goto Exit;
}
bIncomingLessThan = TRUE;
// Setup so that we will have to reposition on a call to
// firstKey or nextKey.
m_curKey.uiKeyLen = 0;
m_bAtBOF = TRUE;
// If the incoming until key was > our from key, see if it is
// also greater than our until key. If so, our until key will
// be set to be equal to the incoming until key.
if (iCompare > 0)
{
goto Compare_Until_Keys;
}
}
else
{
if (iCompare > 0)
{
// The incoming from key is > than our from key,
// see if the incoming from key is > our until key there
// is no overlap.
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE,
&pFSIndexCursor->m_fromExtKey, NULL,
pFSIndexCursor->m_fromKey.ucKey,
pFSIndexCursor->m_fromKey.uiKeyLen,
&m_untilExtKey, NULL,
m_untilKey.ucKey,
m_untilKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
if (iCompare > 0)
{
// Incoming from key is also greater than our until key - no overlap.
*piCompare = 1;
goto Exit;
}
}
// From key is between our from and until key - definitely a union case.
// Just need to see if the incoming until key is > our until key.
// If so, set our until key to be equal to the incoming until key.
*pbUnioned = TRUE;
Compare_Until_Keys:
// Compare the until keys.
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE,
&pFSIndexCursor->m_untilExtKey, NULL,
pFSIndexCursor->m_untilKey.ucKey,
pFSIndexCursor->m_untilKey.uiKeyLen,
&m_untilExtKey, NULL,
m_untilKey.ucKey,
m_untilKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
if (iCompare > 0)
{
// Incoming until key is > our until key, set our until key to be
// equal to the incoming until key.
if (RC_BAD( rc = copyKey( &m_untilKey, &m_untilExtKey,
&pFSIndexCursor->m_untilKey,
&pFSIndexCursor->m_untilExtKey)))
{
goto Exit;
}
bIncomingGreaterThan = TRUE;
}
// else iCompare <= 0
// Incoming until key is <= our until key - no need to change our
// until key.
}
// If we need to recalculate the cost, do so here.
if (bIncomingLessThan && bIncomingGreaterThan)
{
// Our range was a complete subset of the incoming range, so
// we should be able to just use whatever cost was calculated
// for it, unless no cost was calculated, in which case we
// need to calculate a cost.
if (pFSIndexCursor->m_ui64Cost)
{
m_ui64Cost = pFSIndexCursor->m_ui64Cost;
m_ui64LeafBlocksBetween = pFSIndexCursor->m_ui64LeafBlocksBetween;
m_ui64TotalRefs = pFSIndexCursor->m_ui64TotalRefs;
m_bTotalsEstimated = pFSIndexCursor->m_bTotalsEstimated;
}
else
{
if (RC_BAD( rc = calculateCost()))
{
goto Exit;
}
}
}
// If we have not calculated a cost, or the union created a larger
// range of keys on the from or until side, then we need to
// recalculate the cost.
else if (bIncomingLessThan || bIncomingGreaterThan || !m_ui64Cost)
{
if (RC_BAD( rc = calculateCost()))
{
goto Exit;
}
}
// If we unioned the keys, set the do row match and can compare on key
// flags to the worst case. If the incoming cursor required us to
// match on the row, then we need to set this cursor to match on the row.
// If the incoming cursor did not allow us to compare on the key, then
// we don't want to allow this cursor to compare on the key.
if (*pbUnioned)
{
if (pFSIndexCursor->m_bDoRowMatch)
{
m_bDoRowMatch = TRUE;
}
if (!pFSIndexCursor->m_bCanCompareOnKey)
{
m_bCanCompareOnKey = FALSE;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Open the F_Btree object if not already open.
****************************************************************************/
RCODE FSIndexCursor::openBTree(
F_Db * pDb
)
{
RCODE rc = NE_SFLM_OK;
if (!m_bTreeOpen)
{
if ( !m_pbTree)
{
if (RC_BAD( rc = gv_SFlmSysData.pBtPool->btpReserveBtree( &m_pbTree)))
{
goto Exit;
}
}
Open_Btree:
if (RC_BAD( rc = m_pbTree->btOpen( pDb, m_pLFile,
isAbsolutePositionable(), FALSE,
&m_ixCompare)))
{
goto Exit;
}
m_bTreeOpen = TRUE;
m_pDb = pDb;
m_eTransType = pDb->m_eTransType;
m_ixCompare.setIxInfo( m_pDb, m_pIndex);
}
else
{
if (pDb != m_pDb || pDb->m_eTransType != m_eTransType)
{
closeBTree();
goto Open_Btree;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Get a key's data part.
****************************************************************************/
RCODE FSIndexCursor::getKeyData(
F_Btree * pBTree,
FLMUINT uiDataLen)
{
RCODE rc = NE_SFLM_OK;
m_uiCurKeyDataLen = 0;
// See if there is a data part
if (m_pIndex->pDataIcds && uiDataLen)
{
// If the data will fit in the search key buffer, just
// reuse it since we are not going to do anything with
// it after this. Otherwise, allocate a new buffer.
if (uiDataLen > m_uiCurKeyDataBufSize)
{
FLMBYTE * pucNewBuf;
FLMUINT uiNewLen = uiDataLen;
if (uiNewLen < 256)
{
uiNewLen = 256;
}
if (RC_BAD( rc = f_alloc( uiNewLen, &pucNewBuf)))
{
goto Exit;
}
if (m_pucCurKeyDataBuf)
{
f_free( &m_pucCurKeyDataBuf);
}
m_pucCurKeyDataBuf = pucNewBuf;
m_uiCurKeyDataBufSize = uiNewLen;
}
// Retrieve the data
if (RC_BAD( rc = pBTree->btGetEntry(
m_curKey.ucKey, SFLM_MAX_KEY_SIZE, m_curKey.uiKeyLen,
m_pucCurKeyDataBuf, m_uiCurKeyDataBufSize,
&m_uiCurKeyDataLen)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
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(
F_Db * pDb,
FLMBOOL bGoingForward,
FLMBOOL bExcludeKey,
F_DataVector * pExtSrchKey,
KEYPOS * pSearchKey, // Search key
KEYPOS * pFoundKey,
FLMBOOL bGetKeyData,
FLMUINT * puiDataLen,
F_Btree * pBTree, // BTree to use. NULL means use our
// internal one.
FLMUINT * puiAbsolutePos)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiDataLen;
FLMINT iCompare = 0;
// if pBTree is NULL, we are to use m_pbTree. Otherwise, we
// need to open the pBTree and use it.
if (!pBTree)
{
if (RC_BAD( rc = openBTree( pDb)))
{
goto Exit;
}
pBTree = m_pbTree;
}
if (pFoundKey != pSearchKey)
{
f_memcpy( pFoundKey->ucKey, pSearchKey->ucKey,
pSearchKey->uiKeyLen);
pFoundKey->uiKeyLen = pSearchKey->uiKeyLen;
}
m_ixCompare.setSearchKey( pExtSrchKey);
m_ixCompare.setCompareRowId( pExtSrchKey ? FALSE : TRUE);
if (RC_BAD( rc = pBTree->btLocateEntry( pFoundKey->ucKey, SFLM_MAX_KEY_SIZE,
&pFoundKey->uiKeyLen,
(bGoingForward && bExcludeKey)
? FLM_EXCL
: FLM_INCL,
puiAbsolutePos, &uiDataLen, NULL, NULL)))
{
if (rc != NE_SFLM_EOF_HIT)
{
goto Exit;
}
}
if (bGoingForward)
{
if (rc == NE_SFLM_EOF_HIT)
{
goto Exit;
}
}
else
{
// Going backwards or to last. See if we positioned too far.
if (rc == NE_SFLM_EOF_HIT)
{
// Position to last key in tree.
if (RC_BAD( rc = pBTree->btLastEntry( pFoundKey->ucKey, SFLM_MAX_KEY_SIZE,
&pFoundKey->uiKeyLen,
&uiDataLen, NULL, NULL)))
{
goto Exit;
}
}
else
{
// We want to go to the previous key if we went past the key
// we were aiming for, or if we landed on that key, but we
// are doing an exclusive lookup.
if (!bExcludeKey)
{
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex,
pExtSrchKey ? FALSE : TRUE,
pExtSrchKey, NULL,
pFoundKey->ucKey, pFoundKey->uiKeyLen,
pExtSrchKey, NULL,
pSearchKey->ucKey, pSearchKey->uiKeyLen,
&iCompare)))
{
goto Exit;
}
}
if (bExcludeKey || iCompare > 0)
{
// Position to the previous key.
if (RC_BAD( rc = pBTree->btPrevEntry( pFoundKey->ucKey, SFLM_MAX_KEY_SIZE,
&pFoundKey->uiKeyLen,
&uiDataLen, NULL, NULL)))
{
goto Exit;
}
}
}
}
// See if there is any data to get
if (bGetKeyData)
{
flmAssert( pFoundKey == &m_curKey);
if (RC_BAD( rc = getKeyData( pBTree, uiDataLen)))
{
goto Exit;
}
}
if (puiDataLen)
{
*puiDataLen = uiDataLen;
}
Exit:
if (RC_BAD( rc))
{
pFoundKey->uiKeyLen = 0;
if (pBTree == m_pbTree)
{
closeBTree();
}
}
return( rc);
}
/****************************************************************************
Desc: Populate a key from the current key.
****************************************************************************/
RCODE FSIndexCursor::populateKey(
F_DataVector * pKey)
{
RCODE rc = NE_SFLM_OK;
pKey->reset();
if (RC_BAD( rc = pKey->inputKey( m_pDb, m_uiIndexNum,
m_curKey.ucKey, m_curKey.uiKeyLen)))
{
goto Exit;
}
// See if there is a data part
if (m_pIndex->pDataIcds && m_uiCurKeyDataLen)
{
// Parse the data
if (RC_BAD( rc = pKey->inputData( m_pDb, m_uiIndexNum, m_pucCurKeyDataBuf,
m_uiCurKeyDataLen)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Return the current record and record id.
****************************************************************************/
RCODE FSIndexCursor::currentKey(
F_Db * pDb,
F_DataVector * pKey)
{
RCODE rc = NE_SFLM_OK;
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
if (m_bAtBOF)
{
rc = RC_SET( NE_SFLM_BOF_HIT);
goto Exit;
}
if (m_bAtEOF)
{
rc = RC_SET( NE_SFLM_EOF_HIT);
goto Exit;
}
// If we get this far, we are positioned on some key.
flmAssert( m_curKey.uiKeyLen);
if (RC_BAD( rc = populateKey( pKey)))
{
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Make sure the current key is positioned in a key set. If not,
move to the next or previous key set until it is.
****************************************************************************/
RCODE FSIndexCursor::checkIfKeyInRange(
FLMBOOL bPositionForward)
{
RCODE rc = NE_SFLM_OK;
FLMINT iCompare;
if (bPositionForward)
{
// See if the key is within the bounds of the current key set.
// It is already guaranteed to be >= the fromKey, we just need to
// make sure it is <= the until key.
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE,
&m_untilExtKey, NULL,
m_curKey.ucKey, m_curKey.uiKeyLen,
&m_untilExtKey, NULL,
m_untilKey.ucKey, m_untilKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
if (iCompare > 0)
{
m_bAtEOF = TRUE;
rc = RC_SET( NE_SFLM_EOF_HIT);
goto Exit;
}
}
else
{
// See if the key is within the bounds of the current key set.
// It is already guaranteed to be <= the untilKey, we just need to
// make sure it is >= the from key.
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE,
&m_fromExtKey, NULL,
m_curKey.ucKey, m_curKey.uiKeyLen,
&m_fromExtKey, NULL,
m_fromKey.ucKey, m_fromKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
if (iCompare < 0)
{
m_bAtBOF = TRUE;
rc = RC_SET( NE_SFLM_BOF_HIT);
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Position to and return the first key.
****************************************************************************/
RCODE FSIndexCursor::firstKey(
F_Db * pDb,
F_DataVector * pKey)
{
RCODE rc = NE_SFLM_OK;
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
flmAssert( m_bSetup);
// If at BOF and we have a key, then we are positioned on the first
// key already - this would have happened if we had positioned to
// calculate a cost. Rather than do the positioning again, we simply
// set m_bAtBOF to FALSE.
if (m_bAtBOF && m_curKey.uiKeyLen)
{
m_bAtBOF = FALSE;
}
else
{
m_bAtBOF = m_bAtEOF = FALSE;
if (RC_BAD( rc = setKeyPosition( pDb, TRUE, FALSE,
&m_fromExtKey, &m_fromKey,
&m_curKey, TRUE, NULL, NULL, NULL)))
{
if (rc == NE_SFLM_EOF_HIT)
{
m_bAtEOF = TRUE;
}
goto Exit;
}
}
// Make sure the current key is within the FROM/UNTIL range.
if (RC_BAD( rc = checkIfKeyInRange( TRUE)))
{
goto Exit;
}
if (RC_BAD( rc = populateKey( pKey)))
{
goto Exit;
}
Exit:
if (RC_BAD( rc))
{
m_curKey.uiKeyLen = 0;
}
return( rc);
}
/****************************************************************************
Desc: Position to the next key.
****************************************************************************/
RCODE FSIndexCursor::nextKey(
F_Db * pDb,
F_DataVector * pKey,
FLMBOOL bSkipCurrKey)
{
RCODE rc = NE_SFLM_OK;
KEYPOS saveCurrentKey;
FLMUINT uiDataLen;
FLMINT iCompare;
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
flmAssert( m_bSetup);
if (m_bAtEOF)
{
rc = RC_SET( NE_SFLM_EOF_HIT);
goto Exit;
}
if (m_bAtBOF || !m_curKey.uiKeyLen)
{
rc = firstKey( pDb, pKey);
goto Exit;
}
// Save the current key, so we can tell when we have gone beyond it.
if (bSkipCurrKey)
{
getCurrKey( &saveCurrentKey);
}
// See if we need to reset the b-tree object we are using
if (m_bTreeOpen &&
(pDb != m_pDb || pDb->m_eTransType != m_eTransType))
{
closeBTree();
}
for (;;)
{
// checkTransaction may have closed the B-tree, in which case we
// need to reopen the b-tree and position exclusive of the current key.
if (!m_bTreeOpen)
{
if (RC_BAD( rc = setKeyPosition( pDb, TRUE, TRUE,
NULL, &m_curKey, &m_curKey, FALSE, &uiDataLen,
NULL, NULL)))
{
if (rc == NE_SFLM_EOF_HIT)
{
m_bAtEOF = TRUE;
}
goto Exit;
}
}
else
{
// set the compare object's search key to NULL because m_curKey
// should always be something we obtained from the index, and
// we should not need a search key for doing extended
// comparisons on truncated keys.
m_ixCompare.setSearchKey( NULL);
m_ixCompare.setCompareRowId( TRUE);
// Get the next key, if any
if (RC_BAD( rc = m_pbTree->btNextEntry( m_curKey.ucKey, SFLM_MAX_KEY_SIZE,
&m_curKey.uiKeyLen, &uiDataLen, NULL, NULL)))
{
if (rc == NE_SFLM_EOF_HIT)
{
m_bAtEOF = TRUE;
}
goto Exit;
}
}
if (!bSkipCurrKey)
{
Check_Key:
if (RC_BAD( rc = getKeyData( m_pbTree, uiDataLen)))
{
goto Exit;
}
// We got to the next key, make sure it is in the key range.
if (RC_BAD( rc = checkIfKeyInRange( TRUE)))
{
goto Exit;
}
if (RC_BAD( rc = populateKey( pKey)))
{
goto Exit;
}
break;
}
// If the bSkipCurrKey flag is TRUE, we want to skip keys until
// we come to one where the key part is different.
if (RC_BAD( rc = ixKeyCompare( pDb, m_pIndex, FALSE,
NULL, NULL,
m_curKey.ucKey, m_curKey.uiKeyLen,
NULL, NULL,
saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
// See if the key part is the same.
if (iCompare != 0)
{
goto Check_Key;
}
}
Exit:
if (RC_BAD( rc))
{
m_curKey.uiKeyLen = 0;
}
return( rc);
}
/****************************************************************************
Desc: Position to and return the last key in the range.
****************************************************************************/
RCODE FSIndexCursor::lastKey(
F_Db * pDb,
F_DataVector * pKey)
{
RCODE rc = NE_SFLM_OK;
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
flmAssert( m_bSetup);
// Position to the until key.
m_bAtBOF = m_bAtEOF = FALSE;
if (RC_BAD( rc = setKeyPosition( pDb, FALSE, FALSE,
&m_untilExtKey, &m_untilKey,
&m_curKey, TRUE, NULL, NULL, NULL)))
{
if (rc == NE_SFLM_BOF_HIT)
{
m_bAtBOF = TRUE;
}
goto Exit;
}
// Make sure the current key is within one of the FROM/UNTIL sets.
if (RC_BAD( rc = checkIfKeyInRange( FALSE)))
{
goto Exit;
}
if (RC_BAD( rc = populateKey( pKey)))
{
goto Exit;
}
Exit:
if (RC_BAD( rc))
{
m_curKey.uiKeyLen = 0;
}
return( rc);
}
/****************************************************************************
Desc: Position to the PREVIOUS key in the range.
****************************************************************************/
RCODE FSIndexCursor::prevKey(
F_Db * pDb,
F_DataVector * pKey,
FLMBOOL bSkipCurrKey)
{
RCODE rc = NE_SFLM_OK;
KEYPOS saveCurrentKey;
FLMUINT uiDataLen;
FLMINT iCompare;
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
flmAssert( m_bSetup);
if (m_bAtBOF)
{
rc = RC_SET( NE_SFLM_BOF_HIT);
goto Exit;
}
if (m_bAtEOF || !m_curKey.uiKeyLen)
{
rc = lastKey( pDb, pKey);
goto Exit;
}
// Save the current key, so we can tell when we have gone beyond it.
if (bSkipCurrKey)
{
getCurrKey( &saveCurrentKey);
}
// See if we need to reset the b-tree object we are using
if (m_bTreeOpen &&
(pDb != m_pDb || pDb->m_eTransType != m_eTransType))
{
closeBTree();
}
for (;;)
{
// checkTransaction may have closed the B-tree, in which case we
// need to reopen the b-tree and position exclusive of the current key.
if (!m_bTreeOpen)
{
if (RC_BAD( rc = setKeyPosition( pDb, FALSE, TRUE,
NULL, &m_curKey, &m_curKey,
FALSE, &uiDataLen, NULL, NULL)))
{
if (rc == NE_SFLM_BOF_HIT)
{
m_bAtBOF = TRUE;
}
goto Exit;
}
}
else
{
// set the compare object's search key to NULL because m_curKey
// should always be something we obtained from the index, and
// we should not need a search key for doing extended
// comparisons on truncated keys.
m_ixCompare.setSearchKey( NULL);
m_ixCompare.setCompareRowId( TRUE);
// Get the previous key, if any
if (RC_BAD( rc = m_pbTree->btPrevEntry( m_curKey.ucKey, SFLM_MAX_KEY_SIZE,
&m_curKey.uiKeyLen, &uiDataLen, NULL, NULL)))
{
if (rc == NE_SFLM_BOF_HIT)
{
m_bAtBOF = TRUE;
}
goto Exit;
}
}
if (!bSkipCurrKey)
{
Check_Key:
if (RC_BAD( rc = getKeyData( m_pbTree, uiDataLen)))
{
goto Exit;
}
// We got to the previous key, make sure it is in the key range.
if (RC_BAD( rc = checkIfKeyInRange( FALSE)))
{
goto Exit;
}
if (RC_BAD( rc = populateKey( pKey)))
{
goto Exit;
}
break;
}
// If the bSkipCurrKey flag is TRUE, we want to skip keys until
// we come to one where the key part is different.
if (RC_BAD( rc = ixKeyCompare( pDb, m_pIndex, FALSE,
NULL, NULL,
m_curKey.ucKey, m_curKey.uiKeyLen,
NULL, NULL,
saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen,
&iCompare)))
{
goto Exit;
}
// See if the key part is the same.
if (iCompare != 0)
{
goto Check_Key;
}
}
Exit:
if (RC_BAD( rc))
{
m_curKey.uiKeyLen = 0;
}
return( rc);
}