Files
mars-flaim/xflaim/src/fsdatacu.cpp
dsandersoremutah c55dab446f Renamed version4 to flaim and version5 to xflaim
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-01-27 21:06:39 +00:00

906 lines
18 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: fsdatacu.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "flaimsys.h"
#include "fscursor.h"
/****************************************************************************
Desc:
****************************************************************************/
FSCollectionCursor::FSCollectionCursor()
{
m_pbTree = NULL;
m_bTreeOpen = FALSE;
m_pCollection = NULL;
m_bDocumentIds = FALSE;
m_pLFile = NULL;
m_pDb = NULL;
m_eTransType = XFLM_NO_TRANS;
resetCursor();
}
/****************************************************************************
Desc:
****************************************************************************/
FSCollectionCursor::~FSCollectionCursor()
{
closeBTree();
if (m_pbTree)
{
gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pbTree);
}
}
/****************************************************************************
Desc: Resets any allocations, keys, state, etc.
****************************************************************************/
void FSCollectionCursor::resetCursor( void)
{
closeBTree();
m_uiCollection = 0;
m_bDocumentIds = FALSE;
m_uiBlkChangeCnt = 0;
m_ui64CurrTransId = 0;
m_ui64CurNodeId = 0;
m_bAtBOF = TRUE;
m_bAtEOF = FALSE;
m_bSetup = FALSE;
}
/****************************************************************************
Desc: Resets to a new transaction that may change the read consistency of
the query.
****************************************************************************/
RCODE FSCollectionCursor::resetTransaction(
F_Db * pDb)
{
RCODE rc = NE_XFLM_OK;
F_COLLECTION * pCollection;
if (RC_BAD( rc = pDb->m_pDict->getCollection( m_uiCollection,
&pCollection)))
{
goto Exit;
}
if (pCollection != m_pCollection)
{
m_pCollection = pCollection;
m_pLFile = &pCollection->lfInfo;
if (m_bTreeOpen)
{
closeBTree();
}
m_pDb = pDb;
m_eTransType = pDb->m_eTransType;
}
m_ui64CurrTransId = pDb->m_ui64CurrTransID;
m_uiBlkChangeCnt = pDb->m_uiBlkChangeCnt;
Exit:
return( rc);
}
/****************************************************************************
Desc: Open the F_Btree object if not already open.
****************************************************************************/
RCODE FSCollectionCursor::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, FALSE, FALSE)))
{
goto Exit;
}
m_bTreeOpen = TRUE;
m_pDb = pDb;
m_eTransType = pDb->m_eTransType;
}
else
{
if (pDb != m_pDb || pDb->m_eTransType != m_eTransType)
{
closeBTree();
goto Open_Btree;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Set the node position.
****************************************************************************/
RCODE FSCollectionCursor::setNodePosition(
F_Db * pDb,
FLMBOOL bGoingForward,
FLMUINT64 ui64NodeId,
FLMUINT64 * pui64FoundNodeId,
F_Btree * pBTree // BTree to use. NULL means use our
// internal one.
)
{
RCODE rc = NE_XFLM_OK;
FLMBYTE ucKey [FLM_MAX_NUM_BUF_SIZE];
FLMUINT uiKeyLen;
FLMBOOL bNeg;
FLMUINT uiBytesProcessed;
FLMUINT64 ui64TmpNodeId;
IF_DOMNode * pNode = NULL;
// 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;
}
uiKeyLen = sizeof( ucKey);
if (RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &uiKeyLen,
ucKey, FALSE, TRUE)))
{
goto Exit;
}
if (RC_BAD( rc = pBTree->btLocateEntry(
ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL)))
{
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_BOF_HIT || rc == NE_XFLM_EOF_HIT)
{
// Position to last key in tree.
if (RC_BAD( rc = pBTree->btLastEntry( ucKey, sizeof( ucKey),
&uiKeyLen,
NULL, NULL, NULL)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey,
&ui64TmpNodeId, &bNeg, &uiBytesProcessed)))
{
goto Exit;
}
if (ui64TmpNodeId > ui64NodeId)
{
// Position to the previous key.
if (RC_BAD( rc = pBTree->btPrevEntry( ucKey, sizeof( ucKey),
&uiKeyLen, NULL, NULL, NULL)))
{
goto Exit;
}
}
}
}
if (!m_bDocumentIds)
{
if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey,
pui64FoundNodeId, &bNeg, &uiBytesProcessed)))
{
goto Exit;
}
}
else
{
// Need to position to a document ID if we are looking only for document
// root nodes.
for (;;)
{
if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey,
&ui64TmpNodeId, &bNeg, &uiBytesProcessed)))
{
goto Exit;
}
// The following code tests to see if we have gone past the
// from or until node id. It is an optimization that isn't
// actually necessary for the code to work properly, because
// the outside code also makes this check. This just allows
// us to quit earlier than we otherwise might while looking
// for a document root node.
if (bGoingForward)
{
// If we have gone past the until node id, we are done.
if (ui64TmpNodeId > m_ui64UntilNodeId)
{
rc = RC_SET( NE_XFLM_EOF_HIT);
goto Exit;
}
}
else
{
// If we have gone past the from node id, we are done.
if (ui64TmpNodeId < m_ui64FromNodeId)
{
rc = RC_SET( NE_XFLM_BOF_HIT);
goto Exit;
}
}
if (RC_BAD( rc = pDb->getNode( m_uiCollection, ui64TmpNodeId, &pNode)))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
// Better be able to find the node at this point!
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
goto Exit;
}
}
// If the node is a root node, we have a document we can
// process.
if (((F_DOMNode *)pNode)->isRootNode())
{
*pui64FoundNodeId = ui64TmpNodeId;
break;
}
// Need to go to the next or previous node.
if (bGoingForward)
{
if (RC_BAD( rc = pBTree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = pBTree->btPrevEntry( ucKey, uiKeyLen, &uiKeyLen)))
{
goto Exit;
}
}
}
}
Exit:
if (RC_BAD( rc))
{
if (pBTree == m_pbTree)
{
closeBTree();
}
}
if (pNode)
{
pNode->Release();
}
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.
This code does not work with multiple key sets of FROM/UNTIL keys.
****************************************************************************/
RCODE FSCollectionCursor::setupRange(
F_Db * pDb,
FLMUINT uiCollection,
FLMBOOL bDocumentIds,
FLMUINT64 ui64LowNodeId,
FLMUINT64 ui64HighNodeId,
FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks
FLMUINT * puiTotalNodes, // [out]
FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating.
{
RCODE rc = NE_XFLM_OK;
F_Btree * pUntilBTree = NULL;
m_bAtBOF = TRUE;
m_bAtEOF = FALSE;
m_uiCollection = uiCollection;
m_bDocumentIds = bDocumentIds;
if (RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
m_ui64FromNodeId = ui64LowNodeId;
m_ui64UntilNodeId = ui64HighNodeId;
m_bSetup = TRUE;
m_ui64CurNodeId = 0;
// Want any of the counts back?
if (puiLeafBlocksBetween || puiTotalNodes)
{
if (puiLeafBlocksBetween)
{
*puiLeafBlocksBetween = 0;
}
if (puiTotalNodes)
{
*puiTotalNodes = 0;
}
if (pbTotalsEstimated)
{
*pbTotalsEstimated = FALSE;
}
// Position to the FROM and UNTIL key so we can get the stats.
if (RC_BAD( rc = setNodePosition( pDb, TRUE,
m_ui64FromNodeId, &m_ui64CurNodeId, NULL)))
{
if (rc == NE_XFLM_EOF_HIT)
{
rc = NE_XFLM_OK;
}
goto Exit;
}
// All nodes between FROM and UNTIL may be gone.
if (m_ui64CurNodeId < m_ui64UntilNodeId)
{
FLMUINT64 ui64TmpNodeId;
// Get a btree object
if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree(
&pUntilBTree)))
{
goto Exit;
}
if (RC_BAD( rc = pUntilBTree->btOpen( pDb, m_pLFile,
FALSE, FALSE)))
{
goto Exit;
}
// We better be able to at least find m_ui64CurNodeId going
// backward from m_ui64UntilNodeId.
if (RC_BAD( rc = setNodePosition( pDb, FALSE,
m_ui64UntilNodeId, &ui64TmpNodeId, pUntilBTree)))
{
goto Exit;
}
if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree,
puiLeafBlocksBetween, puiTotalNodes,
pbTotalsEstimated,
(pDb->m_pDatabase->m_uiBlockSize * 3) / 4)))
{
goto Exit;
}
}
}
Exit:
if (pUntilBTree)
{
gv_XFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree);
}
return( rc);
}
/****************************************************************************
Desc: Return the current node.
****************************************************************************/
RCODE FSCollectionCursor::currentNode(
F_Db * pDb,
IF_DOMNode ** ppNode,
FLMUINT64 * pui64NodeId
)
{
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;
}
flmAssert( m_ui64CurNodeId);
if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId)))
{
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Make sure the current node is positioned in the range for the cursor.
****************************************************************************/
RCODE FSCollectionCursor::checkIfNodeInRange(
FLMBOOL bPositionForward)
{
RCODE rc = NE_XFLM_OK;
if (bPositionForward)
{
if (m_ui64CurNodeId > m_ui64UntilNodeId)
{
m_bAtEOF = TRUE;
rc = RC_SET( NE_XFLM_EOF_HIT);
goto Exit;
}
}
else
{
if (m_ui64CurNodeId < m_ui64FromNodeId)
{
m_bAtBOF = TRUE;
rc = RC_SET( NE_XFLM_BOF_HIT);
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Position to and return the first node.
****************************************************************************/
RCODE FSCollectionCursor::firstNode(
F_Db * pDb,
IF_DOMNode ** ppNode,
FLMUINT64 * pui64NodeId
)
{
RCODE rc = NE_XFLM_OK;
if( RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
flmAssert( m_bSetup);
// If at BOF and we have a node, then we are positioned on the first
// node 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_ui64CurNodeId)
{
m_bAtBOF = FALSE;
}
else
{
m_bAtBOF = m_bAtEOF = FALSE;
if (RC_BAD( rc = setNodePosition( pDb, TRUE, m_ui64FromNodeId,
&m_ui64CurNodeId, NULL)))
{
if (rc == NE_XFLM_EOF_HIT)
{
m_bAtEOF = TRUE;
}
goto Exit;
}
}
// Make sure the current node ID is within the FROM/UNTIL range.
if (RC_BAD( rc = checkIfNodeInRange( TRUE)))
{
goto Exit;
}
if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId)))
{
goto Exit;
}
Exit:
if (RC_BAD( rc))
{
m_ui64CurNodeId = 0;
}
return( rc);
}
/****************************************************************************
Desc: Position to the next node.
****************************************************************************/
RCODE FSCollectionCursor::nextNode(
F_Db * pDb,
IF_DOMNode ** ppNode,
FLMUINT64 * pui64NodeId
)
{
RCODE rc = NE_XFLM_OK;
IF_DOMNode * pNode = NULL;
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_ui64CurNodeId)
{
rc = firstNode( pDb, ppNode, pui64NodeId);
goto Exit;
}
if (m_bDocumentIds)
{
if (RC_BAD( rc = pDb->getNode( m_uiCollection, m_ui64CurNodeId,
&pNode)))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_OK;
// Allow to just fall through to below and call setNodePosition.
}
else
{
goto Exit;
}
}
else if (((F_DOMNode *)pNode)->isRootNode())
{
if (RC_BAD( rc = pNode->getNextDocument( pDb, &pNode)))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_EOF_HIT;
m_bAtEOF = TRUE;
}
}
else
{
if( RC_BAD( rc = pNode->getNodeId( m_pDb, &m_ui64CurNodeId)))
{
goto Exit;
}
if (RC_OK( rc = checkIfNodeInRange( TRUE)))
{
if (pui64NodeId)
{
*pui64NodeId = m_ui64CurNodeId;
}
if (ppNode)
{
if (*ppNode)
{
(*ppNode)->Release();
}
*ppNode = pNode;
pNode = NULL;
}
}
}
goto Exit;
}
// Fall through to below to call setNodePosition.
}
// Get the next node, if any
if (m_ui64CurNodeId == ~((FLMUINT64)0))
{
rc = RC_SET( NE_XFLM_EOF_HIT);
m_bAtEOF = TRUE;
goto Exit;
}
// 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();
}
if (RC_BAD( rc = setNodePosition( pDb, TRUE,
m_ui64CurNodeId + 1, &m_ui64CurNodeId, NULL)))
{
if (rc == NE_XFLM_EOF_HIT)
{
m_bAtEOF = TRUE;
}
goto Exit;
}
// Make sure the current node ID is within the FROM/UNTIL range.
if (RC_BAD( rc = checkIfNodeInRange( TRUE)))
{
goto Exit;
}
if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId)))
{
goto Exit;
}
Exit:
if (pNode)
{
pNode->Release();
}
if (RC_BAD( rc))
{
m_ui64CurNodeId = 0;
}
return( rc);
}
/****************************************************************************
Desc: Position to and return the last node.
****************************************************************************/
RCODE FSCollectionCursor::lastNode(
F_Db * pDb,
IF_DOMNode ** ppNode,
FLMUINT64 * pui64NodeId
)
{
RCODE rc = NE_XFLM_OK;
if( RC_BAD( rc = checkTransaction( pDb)))
{
goto Exit;
}
flmAssert( m_bSetup);
// Position to the until node
m_bAtBOF = m_bAtEOF = FALSE;
if (RC_BAD( rc = setNodePosition( pDb, FALSE, m_ui64UntilNodeId,
&m_ui64CurNodeId, NULL)))
{
if (rc == NE_XFLM_BOF_HIT)
{
m_bAtBOF = TRUE;
}
goto Exit;
}
// Make sure the current node ID is within the FROM/UNTIL range.
if (RC_BAD( rc = checkIfNodeInRange( FALSE)))
{
goto Exit;
}
if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId)))
{
goto Exit;
}
Exit:
if (RC_BAD( rc))
{
m_ui64CurNodeId = 0;
}
return( rc);
}
/****************************************************************************
Desc: Position to the previous node.
****************************************************************************/
RCODE FSCollectionCursor::prevNode(
F_Db * pDb,
IF_DOMNode ** ppNode,
FLMUINT64 * pui64NodeId
)
{
RCODE rc = NE_XFLM_OK;
IF_DOMNode * pNode = NULL;
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_ui64CurNodeId)
{
rc = lastNode( pDb, ppNode, pui64NodeId);
goto Exit;
}
if (m_bDocumentIds)
{
if (RC_BAD( rc = pDb->getNode( m_uiCollection, m_ui64CurNodeId,
&pNode)))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_OK;
// Allow to just fall through to below and call setNodePosition.
}
else
{
goto Exit;
}
}
else if (((F_DOMNode *)pNode)->isRootNode())
{
if (RC_BAD( rc = pNode->getPreviousDocument( pDb, &pNode)))
{
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
{
rc = NE_XFLM_BOF_HIT;
m_bAtBOF = TRUE;
}
}
else
{
if( RC_BAD( rc = pNode->getNodeId( m_pDb, &m_ui64CurNodeId)))
{
goto Exit;
}
if (RC_OK( rc = checkIfNodeInRange( FALSE)))
{
if (pui64NodeId)
{
*pui64NodeId = m_ui64CurNodeId;
}
if (ppNode)
{
if (*ppNode)
{
(*ppNode)->Release();
}
*ppNode = pNode;
pNode = NULL;
}
}
}
goto Exit;
}
// Fall through to below to call setNodePosition.
}
// Get the previous node, if any
if (m_ui64CurNodeId == 1)
{
rc = RC_SET( NE_XFLM_BOF_HIT);
m_bAtBOF = TRUE;
goto Exit;
}
// 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();
}
if (RC_BAD( rc = setNodePosition( pDb, FALSE,
m_ui64CurNodeId - 1, &m_ui64CurNodeId, NULL)))
{
if (rc == NE_XFLM_BOF_HIT)
{
m_bAtBOF = TRUE;
}
goto Exit;
}
// Make sure the current node ID is within the FROM/UNTIL range.
if (RC_BAD( rc = checkIfNodeInRange( FALSE)))
{
goto Exit;
}
if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId)))
{
goto Exit;
}
Exit:
if (pNode)
{
pNode->Release();
}
if (RC_BAD( rc))
{
m_ui64CurNodeId = 0;
}
return( rc);
}