git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@405 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1304 lines
26 KiB
C++
1304 lines
26 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: fscursor.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
#include "fscursor.h"
|
|
|
|
FSTATIC int nodeIdCompFn(
|
|
void * pvData1,
|
|
void * pvData2,
|
|
void * pvUserData);
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FSIndexCursor::FSIndexCursor()
|
|
{
|
|
m_pbTree = NULL;
|
|
m_bTreeOpen = FALSE;
|
|
m_pucCurKeyDataBuf = NULL;
|
|
m_uiCurKeyDataBufSize = 0;
|
|
m_uiCurKeyDataLen = 0;
|
|
m_pIxd = NULL;
|
|
m_pLFile = NULL;
|
|
m_pDb = NULL;
|
|
m_eTransType = XFLM_NO_TRANS;
|
|
m_pNodeIdSet = NULL;
|
|
resetCursor();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FSIndexCursor::~FSIndexCursor()
|
|
{
|
|
closeBTree();
|
|
if (m_pucCurKeyDataBuf)
|
|
{
|
|
f_free( &m_pucCurKeyDataBuf);
|
|
}
|
|
if (m_pbTree)
|
|
{
|
|
gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pbTree);
|
|
}
|
|
if (m_pNodeIdSet)
|
|
{
|
|
m_pNodeIdSet->Release();
|
|
m_pNodeIdSet = NULL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
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;
|
|
if (m_pNodeIdSet)
|
|
{
|
|
m_pNodeIdSet->Release();
|
|
m_pNodeIdSet = NULL;
|
|
}
|
|
m_bElimDups = FALSE;
|
|
m_bMovingForward = TRUE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Comparison function for comparing node ids.
|
|
****************************************************************************/
|
|
FSTATIC int nodeIdCompFn(
|
|
void * pvData1,
|
|
void * pvData2,
|
|
void * // pvUserData
|
|
)
|
|
{
|
|
if (*((FLMUINT64 *)pvData1) < *((FLMUINT64 *)pvData2))
|
|
{
|
|
return( -1);
|
|
}
|
|
else if (*((FLMUINT64 *)pvData1) > *((FLMUINT64 *)pvData2))
|
|
{
|
|
return( 1);
|
|
}
|
|
else
|
|
{
|
|
return( 0);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Allocate a result set for duplicate checking.
|
|
****************************************************************************/
|
|
RCODE FSIndexCursor::allocDupCheckSet( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
char szTmpDir [F_PATH_MAX_SIZE];
|
|
|
|
// If it is not a compound index, no need for a result set.
|
|
|
|
if (!m_bElimDups)
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (m_pNodeIdSet)
|
|
{
|
|
m_pNodeIdSet->Release();
|
|
m_pNodeIdSet = NULL;
|
|
}
|
|
if ((m_pNodeIdSet = f_new FDynSearchSet) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = gv_pXFlmDbSystem->getTempDir( szTmpDir)))
|
|
{
|
|
if (rc == NE_FLM_IO_PATH_NOT_FOUND)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!szTmpDir [0] && m_pDb)
|
|
{
|
|
if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce(
|
|
m_pDb->m_pDatabase->m_pszDbPath, szTmpDir, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = m_pNodeIdSet->setup( szTmpDir, sizeof( FLMUINT64))))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pNodeIdSet->setCompareFunc( nodeIdCompFn, NULL);
|
|
|
|
Exit:
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (m_pNodeIdSet)
|
|
{
|
|
m_pNodeIdSet->Release();
|
|
m_pNodeIdSet = NULL;
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Check to see if we have already returned the node.
|
|
****************************************************************************/
|
|
RCODE FSIndexCursor::checkIfDup(
|
|
FLMUINT64 ui64NodeId,
|
|
FLMBOOL * pbDup
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
*pbDup = FALSE;
|
|
|
|
// Should only be called if m_bElimDups is TRUE
|
|
|
|
flmAssert( m_bElimDups);
|
|
|
|
// If we have not yet allocated the result set, do it now.
|
|
|
|
if (!m_pNodeIdSet)
|
|
{
|
|
if (RC_BAD( rc = allocDupCheckSet()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// See if we can add the node id to the result set
|
|
|
|
if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId)))
|
|
{
|
|
if (rc == NE_XFLM_EXISTS)
|
|
{
|
|
*pbDup = TRUE;
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Resets to a new transaction that may change the read consistency of
|
|
the query.
|
|
****************************************************************************/
|
|
RCODE FSIndexCursor::resetTransaction(
|
|
F_Db * pDb)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
LFILE * pLFile;
|
|
IXD * pIxd;
|
|
|
|
if (RC_BAD( rc = pDb->m_pDict->getIndex( m_uiIndexNum,
|
|
&pLFile, &pIxd)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (m_pDb != pDb || pLFile != m_pLFile || pIxd != m_pIxd)
|
|
{
|
|
m_pLFile = pLFile;
|
|
m_pIxd = pIxd;
|
|
if (m_bTreeOpen)
|
|
{
|
|
closeBTree();
|
|
}
|
|
m_pDb = pDb;
|
|
m_eTransType = pDb->m_eTransType;
|
|
}
|
|
m_ixCompare.setIxInfo( pDb, m_pIxd);
|
|
m_ui64CurrTransId = pDb->m_ui64CurrTransID;
|
|
m_uiBlkChangeCnt = pDb->m_uiBlkChangeCnt;
|
|
|
|
Exit:
|
|
|
|
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,
|
|
IXD * pIxd,
|
|
PATH_PRED * pPred,
|
|
FLMBOOL * pbDoNodeMatch,
|
|
FLMBOOL * pbCanCompareOnKey,
|
|
FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks
|
|
FLMUINT * puiTotalRefs, // [out] total references
|
|
FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating.
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_Btree * pUntilBTree = NULL;
|
|
FLMINT iCompare;
|
|
|
|
m_uiIndexNum = pIxd->uiIndexNum;
|
|
|
|
// Need to eliminate dups if we are working with
|
|
// a compound index or an index that has one or more
|
|
// data components along with the key. This is because
|
|
// the query code only looks at the 0th key component
|
|
// of keys that are returned. So when we have compound
|
|
// keys or keys with data components, we could get back
|
|
// multiple keys from the index with the same 0th
|
|
// component (but different 1st, 2nd, etc. components).
|
|
|
|
m_bElimDups = (pIxd->uiNumKeyComponents > 1 ||
|
|
pIxd->uiNumDataComponents)
|
|
? TRUE
|
|
: FALSE;
|
|
|
|
if (RC_BAD( rc = checkTransaction( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = flmBuildFromAndUntilKeys( pIxd, pPred,
|
|
&m_fromExtKey, m_fromKey.ucKey, &m_fromKey.uiKeyLen,
|
|
&m_untilExtKey, m_untilKey.ucKey, &m_untilKey.uiKeyLen,
|
|
pbDoNodeMatch, pbCanCompareOnKey)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_curKey.uiKeyLen = 0;
|
|
m_bSetup = TRUE;
|
|
|
|
// Want any of the counts back?
|
|
|
|
if (puiLeafBlocksBetween || puiTotalRefs)
|
|
{
|
|
|
|
// Get a btree object
|
|
|
|
if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pUntilBTree)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_OK( rc = setKeyPosition( 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_pIxd, &m_untilExtKey,
|
|
NULL, NULL,
|
|
FALSE, FALSE,
|
|
m_curKey.ucKey, m_curKey.uiKeyLen,
|
|
m_untilKey.ucKey, m_untilKey.uiKeyLen,
|
|
&iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (iCompare <= 0)
|
|
{
|
|
KEYPOS tmpUntilKey;
|
|
|
|
if (RC_OK( rc = pUntilBTree->btOpen( pDb, m_pLFile,
|
|
isAbsolutePositionable(), FALSE,
|
|
&m_ixCompare)))
|
|
{
|
|
|
|
// Going forward so position is exclusive
|
|
|
|
rc = setKeyPosition( pDb, FALSE, FALSE,
|
|
&m_untilExtKey, &m_untilKey,
|
|
&tmpUntilKey, FALSE, NULL, pUntilBTree, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = RC_SET( NE_XFLM_BOF_HIT);
|
|
m_bAtBOF = TRUE;
|
|
m_bAtEOF = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rc == NE_XFLM_EOF_HIT)
|
|
{
|
|
m_bAtEOF = TRUE;
|
|
m_bAtBOF = FALSE;
|
|
}
|
|
else if (rc == NE_XFLM_BOF_HIT)
|
|
{
|
|
m_bAtEOF = FALSE;
|
|
m_bAtBOF = TRUE;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
|
|
// Empty tree or empty set case.
|
|
|
|
if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT)
|
|
{
|
|
if (puiLeafBlocksBetween)
|
|
{
|
|
*puiLeafBlocksBetween = 0;
|
|
}
|
|
if (puiTotalRefs)
|
|
{
|
|
*puiTotalRefs = 0;
|
|
}
|
|
if (pbTotalsEstimated)
|
|
{
|
|
*pbTotalsEstimated = FALSE;
|
|
}
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree,
|
|
puiLeafBlocksBetween, puiTotalRefs,
|
|
pbTotalsEstimated,
|
|
(pDb->m_pDatabase->m_uiBlockSize * 3) / 4)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
m_bAtBOF = TRUE;
|
|
|
|
Exit:
|
|
|
|
if (pUntilBTree)
|
|
{
|
|
gv_XFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Open the F_Btree object if not already open.
|
|
****************************************************************************/
|
|
RCODE FSIndexCursor::openBTree(
|
|
F_Db * pDb
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
if (!m_bTreeOpen)
|
|
{
|
|
if ( !m_pbTree)
|
|
{
|
|
if (RC_BAD( rc = gv_XFlmSysData.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_pIxd);
|
|
}
|
|
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_XFLM_OK;
|
|
|
|
m_uiCurKeyDataLen = 0;
|
|
|
|
// See if there is a data part
|
|
|
|
if (m_pIxd->pFirstData && 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, XFLM_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_XFLM_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.setCompareDocId( pExtSrchKey ? FALSE : TRUE);
|
|
m_ixCompare.setCompareNodeIds( pExtSrchKey ? FALSE : TRUE);
|
|
if (RC_BAD( rc = pBTree->btLocateEntry( pFoundKey->ucKey, XFLM_MAX_KEY_SIZE,
|
|
&pFoundKey->uiKeyLen,
|
|
(bGoingForward && bExcludeKey)
|
|
? XFLM_EXCL
|
|
: XFLM_INCL,
|
|
puiAbsolutePos, &uiDataLen, NULL, NULL)))
|
|
{
|
|
if (rc != NE_XFLM_EOF_HIT)
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (bGoingForward)
|
|
{
|
|
if (rc == NE_XFLM_EOF_HIT)
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Going backwards or to last. See if we positioned too far.
|
|
|
|
if (rc == NE_XFLM_EOF_HIT)
|
|
{
|
|
|
|
// Position to last key in tree.
|
|
|
|
if (RC_BAD( rc = pBTree->btLastEntry( pFoundKey->ucKey, XFLM_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_pIxd, pExtSrchKey,
|
|
NULL, NULL,
|
|
pExtSrchKey ? FALSE : TRUE,
|
|
pExtSrchKey ? FALSE : TRUE,
|
|
pFoundKey->ucKey, pFoundKey->uiKeyLen,
|
|
pSearchKey->ucKey, pSearchKey->uiKeyLen,
|
|
&iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (bExcludeKey || iCompare > 0)
|
|
{
|
|
|
|
// Position to the previous key.
|
|
|
|
if (RC_BAD( rc = pBTree->btPrevEntry( pFoundKey->ucKey, XFLM_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_XFLM_OK;
|
|
|
|
pKey->reset();
|
|
if (RC_BAD( rc = pKey->inputKey( m_pIxd, m_curKey.ucKey, m_curKey.uiKeyLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if there is a data part
|
|
|
|
if (m_pIxd->pFirstData && m_uiCurKeyDataLen)
|
|
{
|
|
|
|
// Parse the data
|
|
|
|
if (RC_BAD( rc = pKey->inputData( m_pIxd, 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_XFLM_OK;
|
|
|
|
if (RC_BAD( rc = checkTransaction( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (m_bAtBOF)
|
|
{
|
|
rc = RC_SET( NE_XFLM_BOF_HIT);
|
|
goto Exit;
|
|
}
|
|
if (m_bAtEOF)
|
|
{
|
|
rc = RC_SET( NE_XFLM_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_XFLM_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_pIxd, &m_untilExtKey,
|
|
NULL, NULL,
|
|
FALSE, FALSE,
|
|
m_curKey.ucKey, m_curKey.uiKeyLen,
|
|
m_untilKey.ucKey, m_untilKey.uiKeyLen,
|
|
&iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (iCompare > 0)
|
|
{
|
|
m_bAtEOF = TRUE;
|
|
rc = RC_SET( NE_XFLM_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_pIxd, &m_fromExtKey,
|
|
NULL, NULL,
|
|
FALSE, FALSE,
|
|
m_curKey.ucKey, m_curKey.uiKeyLen,
|
|
m_fromKey.ucKey, m_fromKey.uiKeyLen,
|
|
&iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (iCompare < 0)
|
|
{
|
|
m_bAtBOF = TRUE;
|
|
rc = RC_SET( NE_XFLM_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_XFLM_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_XFLM_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;
|
|
}
|
|
|
|
// On firstKey, clear out the duplicate elimination result
|
|
// set, if there is one.
|
|
|
|
if (m_bElimDups)
|
|
{
|
|
if (RC_BAD( rc = allocDupCheckSet()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = populateKey( pKey)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_bElimDups)
|
|
{
|
|
FLMUINT64 ui64NodeId = pKey->getID( 0);
|
|
|
|
if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId)))
|
|
{
|
|
flmAssert( rc != NE_XFLM_EXISTS);
|
|
goto Exit;
|
|
}
|
|
}
|
|
m_bMovingForward = TRUE;
|
|
|
|
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_XFLM_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_XFLM_EOF_HIT);
|
|
goto Exit;
|
|
}
|
|
if (m_bAtBOF || !m_curKey.uiKeyLen)
|
|
{
|
|
rc = firstKey( pDb, pKey);
|
|
goto Exit;
|
|
}
|
|
|
|
// On change of direction, clear out the duplicate elimination result
|
|
// set, if there is one.
|
|
|
|
if (m_bElimDups && !m_bMovingForward)
|
|
{
|
|
if (RC_BAD( rc = allocDupCheckSet()))
|
|
{
|
|
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_XFLM_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.setCompareNodeIds( TRUE);
|
|
m_ixCompare.setCompareDocId( TRUE);
|
|
|
|
// Get the next key, if any
|
|
|
|
if (RC_BAD( rc = m_pbTree->btNextEntry( m_curKey.ucKey, XFLM_MAX_KEY_SIZE,
|
|
&m_curKey.uiKeyLen, &uiDataLen, NULL, NULL)))
|
|
{
|
|
if (rc == NE_XFLM_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;
|
|
}
|
|
|
|
if (m_bElimDups)
|
|
{
|
|
FLMBOOL bDup;
|
|
|
|
if (RC_BAD( rc = checkIfDup( pKey->getID( 0), &bDup)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (bDup)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
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_pIxd, NULL,
|
|
NULL, NULL,
|
|
FALSE, FALSE,
|
|
m_curKey.ucKey, m_curKey.uiKeyLen,
|
|
saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen,
|
|
&iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the key part is the same.
|
|
|
|
if (iCompare != 0)
|
|
{
|
|
goto Check_Key;
|
|
}
|
|
}
|
|
|
|
m_bMovingForward = TRUE;
|
|
|
|
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_XFLM_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_XFLM_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;
|
|
}
|
|
|
|
// On firstKey, clear out the duplicate elimination result
|
|
// set, if there is one.
|
|
|
|
if (m_bElimDups)
|
|
{
|
|
if (RC_BAD( rc = allocDupCheckSet()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = populateKey( pKey)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_bElimDups)
|
|
{
|
|
FLMUINT64 ui64NodeId = pKey->getID( 0);
|
|
|
|
if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId)))
|
|
{
|
|
flmAssert( rc != NE_XFLM_EXISTS);
|
|
goto Exit;
|
|
}
|
|
}
|
|
m_bMovingForward = FALSE;
|
|
|
|
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_XFLM_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_XFLM_BOF_HIT);
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_bAtEOF || !m_curKey.uiKeyLen)
|
|
{
|
|
rc = lastKey( pDb, pKey);
|
|
goto Exit;
|
|
}
|
|
|
|
// On change of direction, clear out the duplicate elimination result
|
|
// set, if there is one.
|
|
|
|
if (m_bElimDups && m_bMovingForward)
|
|
{
|
|
if (RC_BAD( rc = allocDupCheckSet()))
|
|
{
|
|
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_XFLM_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.setCompareNodeIds( TRUE);
|
|
m_ixCompare.setCompareDocId( TRUE);
|
|
|
|
// Get the previous key, if any
|
|
|
|
if (RC_BAD( rc = m_pbTree->btPrevEntry( m_curKey.ucKey, XFLM_MAX_KEY_SIZE,
|
|
&m_curKey.uiKeyLen, &uiDataLen, NULL, NULL)))
|
|
{
|
|
if (rc == NE_XFLM_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;
|
|
}
|
|
|
|
if (m_bElimDups)
|
|
{
|
|
FLMBOOL bDup;
|
|
|
|
if (RC_BAD( rc = checkIfDup( pKey->getID( 0), &bDup)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (bDup)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
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_pIxd, NULL,
|
|
NULL, NULL,
|
|
FALSE, FALSE,
|
|
m_curKey.ucKey, m_curKey.uiKeyLen,
|
|
saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen,
|
|
&iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the key part is the same.
|
|
|
|
if (iCompare != 0)
|
|
{
|
|
goto Check_Key;
|
|
}
|
|
}
|
|
|
|
m_bMovingForward = FALSE;
|
|
|
|
Exit:
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
m_curKey.uiKeyLen = 0;
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|