git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@528 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2661 lines
59 KiB
C++
2661 lines
59 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Various cursor/query functions
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1994-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: fqcur.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
#define FLM_WILD_MASK 0x03
|
|
#define FLM_CASE_MASK 0x0C
|
|
#define FLM_GRAN_MASK 0xF00
|
|
|
|
POOL_STATS g_SQPoolStats = {0,0};
|
|
POOL_STATS g_QueryPoolStats = {0,0};
|
|
|
|
// Local Function Prototypes
|
|
|
|
FSTATIC RCODE flmCurCopyQTInfo(
|
|
QTINFO * pSrc,
|
|
QTINFO * pDest,
|
|
F_Pool * pPool);
|
|
|
|
FSTATIC void flmCurClearSelect(
|
|
CURSOR * pCursor);
|
|
|
|
FSTATIC RCODE flmCurPosToEOF(
|
|
CURSOR * pCursor);
|
|
|
|
FSTATIC RCODE flmCurPosToBOF(
|
|
CURSOR * pCursor);
|
|
|
|
FSTATIC RCODE flmCurSetPos(
|
|
CURSOR * pDestCursor,
|
|
CURSOR * pSrcCursor );
|
|
|
|
FSTATIC RCODE flmCurSetAbsolutePos(
|
|
CURSOR * pCursor,
|
|
FLMUINT uiPosition,
|
|
FLMBOOL bFallForward,
|
|
FLMUINT * puiPosition);
|
|
|
|
FSTATIC RCODE flmCurPositionable(
|
|
CURSOR * pCursor,
|
|
FLMBOOL * pbPositionable);
|
|
|
|
FSTATIC RCODE flmCurAbsPositionable(
|
|
CURSOR * pCursor,
|
|
FLMBOOL * pbAbsPositionable);
|
|
|
|
FSTATIC RCODE flmCurGetAbsolutePos(
|
|
CURSOR * pCursor,
|
|
FLMUINT * puiPosition);
|
|
|
|
FSTATIC RCODE flmCurGetAbsoluteCount(
|
|
CURSOR * pCursor,
|
|
FLMUINT * puiCount);
|
|
|
|
FSTATIC FLMBOOL flmCurMatchIndexPath(
|
|
IFD * pIfd,
|
|
FLMUINT * puiField,
|
|
FLMUINT uiPos);
|
|
|
|
/****************************************************************************
|
|
Desc: Finishes a source's invisible transaction, if any.
|
|
****************************************************************************/
|
|
void flmCurFinishTrans(
|
|
CURSOR * pCursor)
|
|
{
|
|
FLMBOOL bIgnore;
|
|
|
|
if (pCursor->bInvTrans && pCursor->pDb)
|
|
{
|
|
if (RC_OK( fdbInit( pCursor->pDb, FLM_NO_TRANS, 0, 0,
|
|
&bIgnore)))
|
|
{
|
|
if (pCursor->pDb->uiTransType != FLM_NO_TRANS &&
|
|
pCursor->pDb->uiTransCount == pCursor->uiTransSeq)
|
|
{
|
|
|
|
// If the commit fails, then do the abort.
|
|
|
|
if (RC_BAD( flmCommitDbTrans( pCursor->pDb, 0, FALSE)))
|
|
{
|
|
(void)flmAbortDbTrans( pCursor->pDb);
|
|
}
|
|
}
|
|
}
|
|
fdbExit( pCursor->pDb);
|
|
pCursor->bInvTrans = FALSE;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Initializes an FDB for a source.
|
|
****************************************************************************/
|
|
RCODE flmCurDbInit(
|
|
CURSOR * pCursor)
|
|
{
|
|
RCODE rc;
|
|
FLMBOOL bStartedTrans;
|
|
|
|
if (RC_OK( rc = fdbInit( pCursor->pDb, FLM_READ_TRANS,
|
|
FDB_TRANS_GOING_OK | FDB_INVISIBLE_TRANS_OK, 0,
|
|
&bStartedTrans)))
|
|
{
|
|
if (bStartedTrans)
|
|
{
|
|
pCursor->pDb->uiFlags |= FDB_INVISIBLE_TRANS;
|
|
pCursor->uiTransSeq = pCursor->pDb->uiTransCount;
|
|
pCursor->bInvTrans = TRUE;
|
|
}
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Initializes a cursor for subsequent definition and navigation of
|
|
a record set. A cursor must be initialized before it can
|
|
be used.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmCursorInit(
|
|
HFDB hDb,
|
|
FLMUINT uiContainer,
|
|
HFCURSOR * phCursor)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
CURSOR * pCursor = NULL;
|
|
FDB * pDb = (FDB *)hDb;
|
|
|
|
flmAssert( hDb != HFDB_NULL);
|
|
flmAssert( uiContainer != 0);
|
|
|
|
// See if the database is being forced to close
|
|
|
|
if( RC_BAD( rc = flmCheckDatabaseState( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( CURSOR), &pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pCursor->QTInfo.uiMaxPredicates = MAX_USER_PREDICATES;
|
|
pCursor->QTInfo.ppPredicates = &pCursor->QTInfo.Predicates [0];
|
|
|
|
// Initialize cursor members
|
|
|
|
pCursor->QueryPool.smartPoolInit( &g_QueryPoolStats);
|
|
pCursor->SQPool.smartPoolInit( &g_SQPoolStats);
|
|
|
|
pCursor->pDb = pDb;
|
|
pCursor->uiContainer = uiContainer;
|
|
|
|
// Default is to have FLAIM select an index.
|
|
|
|
pCursor->uiIndexNum = FLM_SELECT_INDEX;
|
|
pCursor->pCSContext = pCursor->pDb->pCSContext;
|
|
pCursor->uiCursorId = FCS_INVALID_ID;
|
|
|
|
pCursor->QTInfo.uiExpecting = FLM_Q_OPERAND;
|
|
pCursor->QTInfo.uiFlags = FLM_COMP_CASE_INSENSITIVE | FLM_COMP_WILD;
|
|
|
|
Exit:
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (pCursor)
|
|
{
|
|
pCursor->QueryPool.poolFree();
|
|
pCursor->SQPool.poolFree();
|
|
f_free( &pCursor);
|
|
}
|
|
}
|
|
*phCursor = (HFCURSOR)pCursor;
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Copies a passed-in query tree into a new tree, using the passed-in
|
|
memory pool.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmCurCopyQTInfo(
|
|
QTINFO * pSrc,
|
|
QTINFO * pDest,
|
|
F_Pool * pPool)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FQNODE * pDestParentNode;
|
|
FQNODE * pSrcCurrNode;
|
|
FQNODE * pDestCurrNode;
|
|
FLMBOOL bGoingUp = FALSE;
|
|
FLMBOOL bTreeComplete;
|
|
|
|
// If the source query has been optimized, the query is found
|
|
// in pSrc->pSaveQuery. Otherwise, it is found in
|
|
// pSrc->pTopNode.
|
|
|
|
if (pSrc->pSaveQuery)
|
|
{
|
|
pSrcCurrNode = pSrc->pSaveQuery;
|
|
bTreeComplete = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pSrcCurrNode = pSrc->pTopNode;
|
|
bTreeComplete = FALSE;
|
|
pDest->pCurOpNode = NULL;
|
|
pDest->pCurAtomNode = NULL;
|
|
}
|
|
|
|
// If there is no query tree, don't need to copy.
|
|
|
|
if (pSrcCurrNode)
|
|
{
|
|
|
|
// Must not do a recursive copy, because tree may not
|
|
// have been flattened, and the recursion might go
|
|
// too deep.
|
|
|
|
if (RC_BAD( rc = flmCurCopyQNode( pSrcCurrNode, pDest,
|
|
&pDest->pTopNode, pPool)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pDestParentNode = NULL;
|
|
pDestCurrNode = pDest->pTopNode;
|
|
if (!bTreeComplete)
|
|
{
|
|
if (pSrcCurrNode == pSrc->pCurOpNode)
|
|
{
|
|
pDest->pCurOpNode = pDestCurrNode;
|
|
}
|
|
else if (pSrcCurrNode == pSrc->pCurAtomNode)
|
|
{
|
|
pDest->pCurAtomNode = pDestCurrNode;
|
|
}
|
|
}
|
|
for (;;)
|
|
{
|
|
if (bGoingUp)
|
|
{
|
|
if (pSrcCurrNode->pNextSib)
|
|
{
|
|
pSrcCurrNode = pSrcCurrNode->pNextSib;
|
|
bGoingUp = FALSE;
|
|
}
|
|
else if ((pSrcCurrNode = pSrcCurrNode->pParent) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pDestCurrNode = pDestCurrNode->pParent;
|
|
pDestParentNode = pDestCurrNode->pParent;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pSrcCurrNode->pChild)
|
|
{
|
|
pSrcCurrNode = pSrcCurrNode->pChild;
|
|
pDestParentNode = pDestCurrNode;
|
|
}
|
|
else
|
|
{
|
|
bGoingUp = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
if (RC_BAD( rc = flmCurCopyQNode( pSrcCurrNode, pDest,
|
|
&pDestCurrNode, pPool)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
flmCurLinkLastChild( pDestParentNode, pDestCurrNode);
|
|
if (!bTreeComplete)
|
|
{
|
|
if (pSrcCurrNode == pSrc->pCurOpNode)
|
|
{
|
|
pDest->pCurOpNode = pDestCurrNode;
|
|
}
|
|
else if( pSrcCurrNode == pSrc->pCurAtomNode)
|
|
{
|
|
pDest->pCurAtomNode = pDestCurrNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bTreeComplete)
|
|
{
|
|
pDest->pCurOpNode = pDest->pTopNode;
|
|
pDest->uiNestLvl = 0;
|
|
pDest->pCurAtomNode = NULL;
|
|
if (pDest->pTopNode)
|
|
{
|
|
pDest->uiExpecting = FLM_Q_OPERATOR;
|
|
}
|
|
else
|
|
{
|
|
pDest->uiExpecting = FLM_Q_OPERAND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pDest->uiNestLvl = pSrc->uiNestLvl;
|
|
pDest->uiExpecting = pSrc->uiExpecting;
|
|
|
|
// Defect #84610 -- Need to force pCurOpNode and pCurAtomNode into pDest
|
|
// in cases where the query has no operator (i.e., existence query).
|
|
|
|
if (!pDest->pTopNode)
|
|
{
|
|
if (pSrc->pCurOpNode)
|
|
{
|
|
if (RC_BAD( rc = flmCurCopyQNode( pSrc->pCurOpNode,
|
|
pDest, &pDest->pCurOpNode, pPool)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
if (pSrc->pCurAtomNode)
|
|
{
|
|
if (RC_BAD( rc = flmCurCopyQNode( pSrc->pCurAtomNode,
|
|
pDest, &pDest->pCurAtomNode, pPool)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pDest->uiFlags = pSrc->uiFlags;
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Initializes a new cursor and sets its selection criteria and record
|
|
sources to be the same as those of the passed-in cursor.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmCursorClone(
|
|
HFCURSOR hSource,
|
|
HFCURSOR * phCursor)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
CURSOR * pSrcCursor;
|
|
CURSOR * pDestCursor = NULL;
|
|
|
|
if ((pSrcCursor = (CURSOR *)hSource) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if (pSrcCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the database is being forced to close
|
|
|
|
if( RC_BAD( rc = flmCheckDatabaseState( pSrcCursor->pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( CURSOR), &pDestCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pDestCursor->QTInfo.uiMaxPredicates = MAX_USER_PREDICATES;
|
|
pDestCursor->QTInfo.ppPredicates = &pDestCursor->QTInfo.Predicates [0];
|
|
|
|
// Initialize cursor members
|
|
|
|
pDestCursor->QueryPool.smartPoolInit( &g_QueryPoolStats);
|
|
pDestCursor->SQPool.smartPoolInit( &g_SQPoolStats);
|
|
|
|
// Set up a tree info structure for query declaration.
|
|
|
|
if (RC_BAD( rc = flmCurCopyQTInfo( &pSrcCursor->QTInfo,
|
|
&pDestCursor->QTInfo,
|
|
&pDestCursor->QueryPool)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy source information.
|
|
|
|
pDestCursor->pDb = pSrcCursor->pDb;
|
|
pDestCursor->uiContainer = pSrcCursor->uiContainer;
|
|
pDestCursor->pCSContext = pSrcCursor->pDb->pCSContext;
|
|
pDestCursor->uiCursorId = FCS_INVALID_ID;
|
|
|
|
// Copy index information.
|
|
|
|
pDestCursor->uiIndexNum = pSrcCursor->uiIndexNum;
|
|
pDestCursor->uiRecType = pSrcCursor->uiRecType;
|
|
|
|
// Initialize various structure elements
|
|
|
|
pDestCursor->bOkToReturnKeys = pSrcCursor->bOkToReturnKeys;
|
|
pDestCursor->bOptimized = FALSE;
|
|
|
|
Exit:
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (pDestCursor)
|
|
{
|
|
(void)flmCurFree( pDestCursor, TRUE);
|
|
pDestCursor = NULL;
|
|
}
|
|
}
|
|
*phCursor = (HFCURSOR)pDestCursor;
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Frees up memory associated with a subquery structure.
|
|
****************************************************************************/
|
|
void flmSQFree(
|
|
SUBQUERY * pSubQuery,
|
|
FLMBOOL bFreeEverything)
|
|
{
|
|
if (!bFreeEverything)
|
|
{
|
|
if (pSubQuery->pFSIndexCursor)
|
|
{
|
|
pSubQuery->pFSIndexCursor->releaseBlocks();
|
|
}
|
|
if (pSubQuery->pFSDataCursor)
|
|
{
|
|
pSubQuery->pFSDataCursor->releaseBlocks();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FQNODE * pCurrNode = pSubQuery->pTree;
|
|
QTYPES eType;
|
|
|
|
// Free the memory associated with callbacks in the query tree.
|
|
|
|
while (pCurrNode)
|
|
{
|
|
eType = GET_QNODE_TYPE( pCurrNode);
|
|
if (IS_FLD_CB( eType, pCurrNode))
|
|
{
|
|
(void)pCurrNode->pQAtom->val.QueryFld.fnGetField(
|
|
pCurrNode->pQAtom->val.QueryFld.pvUserData, NULL, HFDB_NULL,
|
|
pCurrNode->pQAtom->val.QueryFld.puiFldPath, FLM_FLD_CLEANUP,
|
|
NULL, NULL, NULL);
|
|
}
|
|
|
|
// Find the next node to process
|
|
|
|
if (pCurrNode->pChild)
|
|
{
|
|
pCurrNode = pCurrNode->pChild;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Travel back up the tree until we find a node
|
|
// that has a sibling.
|
|
|
|
for (;;)
|
|
{
|
|
if (pCurrNode->pNextSib)
|
|
{
|
|
pCurrNode = pCurrNode->pNextSib;
|
|
break;
|
|
}
|
|
if ((pCurrNode = pCurrNode->pParent) == NULL)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSubQuery->OptPool.poolFree();
|
|
|
|
// Free up the file system cursors, if any.
|
|
|
|
if (pSubQuery->pFSIndexCursor)
|
|
{
|
|
pSubQuery->pFSIndexCursor->Release();
|
|
pSubQuery->pFSIndexCursor = NULL;
|
|
}
|
|
if (pSubQuery->pFSDataCursor)
|
|
{
|
|
pSubQuery->pFSDataCursor->Release();
|
|
pSubQuery->pFSDataCursor = NULL;
|
|
}
|
|
}
|
|
if (pSubQuery->pRec)
|
|
{
|
|
pSubQuery->pRec->Release();
|
|
pSubQuery->pRec = NULL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Frees up memory associated with a cursor.
|
|
****************************************************************************/
|
|
void flmCurFree(
|
|
CURSOR * pCursor,
|
|
FLMBOOL bFinishTrans)
|
|
{
|
|
FLMUINT uiCnt;
|
|
CS_CONTEXT * pCSContext;
|
|
|
|
if (bFinishTrans)
|
|
{
|
|
flmCurFinishTransactions( pCursor, TRUE);
|
|
}
|
|
|
|
// Free the memory associated with positioning keys.
|
|
|
|
flmCurFreePosKeys( pCursor);
|
|
|
|
// Free the memory associated with any subqueries.
|
|
|
|
flmCurFreeSQList( pCursor, TRUE);
|
|
pCursor->SQPool.poolFree();
|
|
|
|
// Free the memory associated with the pool structures
|
|
|
|
pCursor->QueryPool.poolFree();
|
|
if (pCursor->pDRNSet)
|
|
{
|
|
pCursor->pDRNSet->Release();
|
|
pCursor->pDRNSet = NULL;
|
|
}
|
|
|
|
for (uiCnt = 0; uiCnt < pCursor->QTInfo.uiNumPredicates; uiCnt++)
|
|
{
|
|
pCursor->QTInfo.ppPredicates [uiCnt]->Release();
|
|
pCursor->QTInfo.ppPredicates [uiCnt] = NULL;
|
|
}
|
|
if (pCursor->QTInfo.uiMaxPredicates > MAX_USER_PREDICATES)
|
|
{
|
|
f_free( &pCursor->QTInfo.ppPredicates);
|
|
}
|
|
f_memset( &pCursor->QTInfo, 0, sizeof( QTINFO));
|
|
pCursor->QTInfo.uiMaxPredicates = MAX_USER_PREDICATES;
|
|
pCursor->QTInfo.ppPredicates = &pCursor->QTInfo.Predicates [0];
|
|
|
|
if ((pCSContext = pCursor->pCSContext) != NULL)
|
|
{
|
|
// Send message to free the cursor - if one was allocated.
|
|
|
|
if ((pCursor->uiCursorId != FCS_INVALID_ID) &&
|
|
(pCSContext->bConnectionGood))
|
|
{
|
|
FCL_WIRE Wire( pCSContext);
|
|
|
|
// Send a request to free the cursor.
|
|
|
|
if (RC_BAD( Wire.sendOp(
|
|
FCS_OPCLASS_ITERATOR, FCS_OP_ITERATOR_FREE)))
|
|
{
|
|
goto CS_Exit;
|
|
}
|
|
|
|
if (RC_BAD( Wire.sendNumber(
|
|
WIRE_VALUE_ITERATOR_ID, pCursor->uiCursorId)))
|
|
{
|
|
pCSContext->bConnectionGood = FALSE;
|
|
goto CS_Exit;
|
|
}
|
|
|
|
if (RC_BAD( Wire.sendTerminate()))
|
|
{
|
|
pCSContext->bConnectionGood = FALSE;
|
|
goto CS_Exit;
|
|
}
|
|
|
|
// Read the response - just discard it.
|
|
|
|
if (RC_BAD( Wire.read()))
|
|
{
|
|
pCSContext->bConnectionGood = FALSE;
|
|
goto CS_Exit;
|
|
}
|
|
}
|
|
pCursor->pCSContext = NULL;
|
|
}
|
|
|
|
CS_Exit:
|
|
f_free( &pCursor);
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Frees resources of the cursor without actually freeing the cursor.
|
|
Keeps around the stuff that is needed to display the cursor
|
|
information for debugging purposes after the fact. At this point
|
|
the cursor is no longer usable.
|
|
****************************************************************************/
|
|
FLMEXP void FLMAPI FlmCursorReleaseResources(
|
|
HFCURSOR hCursor)
|
|
{
|
|
FLMUINT uiCnt;
|
|
CURSOR * pCursor = (CURSOR *)hCursor;
|
|
|
|
flmCurFinishTransactions( pCursor, TRUE);
|
|
flmCurFreeSQList( pCursor, FALSE);
|
|
|
|
for (uiCnt = 0; uiCnt < pCursor->QTInfo.uiNumPredicates; uiCnt++)
|
|
{
|
|
pCursor->QTInfo.ppPredicates [uiCnt]->releaseResources();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Frees memory allocated to an initialized cursor. The cursor handle
|
|
cannot be used for additional cursor operations unless it is
|
|
re-initialized.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmCursorFree(
|
|
HFCURSOR * phCursor)
|
|
{
|
|
CURSOR * pCursor = (CURSOR *)*phCursor;
|
|
IF_LogMessageClient * pLogMsg = NULL;
|
|
|
|
flmAssert( pCursor != NULL);
|
|
|
|
if( !gv_FlmSysData.pLogger)
|
|
{
|
|
return( FERR_OK);
|
|
}
|
|
|
|
if ((pLogMsg = gv_FlmSysData.pLogger->beginMessage( FLM_QUERY_MESSAGE,
|
|
F_DEBUG_MESSAGE)) != NULL)
|
|
{
|
|
flmLogQuery( pLogMsg, 0, pCursor);
|
|
f_endLogMessage( &pLogMsg);
|
|
}
|
|
|
|
if (!pCursor->pCSContext && gv_FlmSysData.uiMaxQueries)
|
|
{
|
|
FlmCursorReleaseResources( (HFCURSOR)pCursor);
|
|
flmSaveQuery( *phCursor);
|
|
*phCursor = HFCURSOR_NULL;
|
|
}
|
|
else
|
|
{
|
|
flmCurFree( pCursor, TRUE);
|
|
*phCursor = HFCURSOR_NULL;
|
|
}
|
|
|
|
return( FERR_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Sets flags in the query that determine text comparision modes
|
|
and the granularity for QuickFinder indexes.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmCursorSetMode(
|
|
HFCURSOR hCursor,
|
|
FLMUINT uiFlags)
|
|
{
|
|
CURSOR * pCursor = (CURSOR *)hCursor;
|
|
|
|
flmAssert( pCursor != NULL);
|
|
|
|
if ((pCursor->pCSContext) && (pCursor->uiCursorId != FCS_INVALID_ID))
|
|
{
|
|
return( RC_SET( FERR_NOT_IMPLEMENTED));
|
|
}
|
|
|
|
pCursor->QTInfo.uiFlags = uiFlags;
|
|
return( FERR_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Clears the selection criteria in a query.
|
|
****************************************************************************/
|
|
FSTATIC void flmCurClearSelect(
|
|
CURSOR * pCursor)
|
|
{
|
|
flmCurFreeSQList( pCursor, TRUE);
|
|
pCursor->pTree = NULL;
|
|
|
|
pCursor->bOptimized = FALSE;
|
|
|
|
flmCurFinishTransactions( pCursor, FALSE);
|
|
|
|
pCursor->QTInfo.pTopNode = NULL;
|
|
pCursor->QTInfo.pCurOpNode = NULL;
|
|
pCursor->QTInfo.pCurAtomNode = NULL;
|
|
pCursor->QTInfo.uiNestLvl = 0;
|
|
pCursor->QTInfo.uiExpecting = FLM_Q_OPERAND;
|
|
pCursor->QTInfo.uiFlags = FLM_COMP_CASE_INSENSITIVE | FLM_COMP_WILD;
|
|
|
|
pCursor->QueryPool.poolReset();
|
|
|
|
pCursor->uiLastRecID = 0;
|
|
pCursor->ReadRc = FERR_OK;
|
|
pCursor->rc = FERR_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Positions a cursor to EOF.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmCurPosToEOF(
|
|
CURSOR * pCursor)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FlmRecord * pRecord = NULL;
|
|
|
|
if (RC_BAD( rc = flmCurPerformRead( FLM_CURSOR_LAST,
|
|
(HFCURSOR)pCursor, FALSE, TRUE, 0, &pRecord, NULL)))
|
|
{
|
|
if (rc == FERR_BOF_HIT)
|
|
{
|
|
|
|
// BOF HIT means that all entries were rejected. Change ReadRc
|
|
// of cursor to be EOF hit.
|
|
|
|
pCursor->ReadRc = RC_SET( FERR_EOF_HIT);
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = flmCurPerformRead( FLM_CURSOR_NEXT, (HFCURSOR)pCursor,
|
|
TRUE, FALSE, 0, &pRecord, NULL);
|
|
if (rc == FERR_EOF_HIT)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
if (pRecord)
|
|
{
|
|
pRecord->Release();
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Positions a cursor to BOF.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmCurPosToBOF(
|
|
CURSOR * pCursor
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FlmRecord * pRecord = NULL;
|
|
|
|
if (RC_BAD( rc = flmCurPerformRead( FLM_CURSOR_FIRST,
|
|
(HFCURSOR)pCursor, TRUE, TRUE, 0, &pRecord, NULL)))
|
|
{
|
|
if (rc == FERR_EOF_HIT)
|
|
{
|
|
|
|
// EOF HIT means that all entries were rejected. Change ReadRc
|
|
// of destination to be BOF hit.
|
|
|
|
pCursor->ReadRc = RC_SET( FERR_BOF_HIT);
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = flmCurPerformRead( FLM_CURSOR_PREV, (HFCURSOR)pCursor,
|
|
FALSE, FALSE, 0, &pRecord, NULL);
|
|
if (rc == FERR_BOF_HIT)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
if (pRecord)
|
|
{
|
|
pRecord->Release();
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Sets the positioning information in a query to be the same as that of
|
|
another query.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmCurSetPos(
|
|
CURSOR * pDestCursor,
|
|
CURSOR * pSrcCursor)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = NULL;
|
|
FlmRecord * pRecord = NULL;
|
|
FLMUINT uiRecordDrn;
|
|
FLMUINT uiContainerNum;
|
|
SUBQUERY * pSrcSubQuery;
|
|
SUBQUERY * pDestSubQuery;
|
|
FLMUINT uiRecMatch;
|
|
FLMBYTE * pucKeyBuffer = NULL;
|
|
FLMUINT uiKeyLen;
|
|
IXD * pIxd;
|
|
void * pvMark = NULL;
|
|
FLMBOOL bSavedInvisTrans;
|
|
|
|
// Must be at most one index used for optimizing - which means that
|
|
// there will only be one sub-query.
|
|
|
|
if ((pSrcSubQuery = pSrcCursor->pSubQueryList) != NULL)
|
|
{
|
|
// If the source query has been optimized, but there is either
|
|
// no index or multiple sub-queries, we can't use it for
|
|
// positioning.
|
|
|
|
if (pSrcSubQuery->pNext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if ((!pDestCursor->bOptimized))
|
|
{
|
|
if( RC_BAD( rc = flmCurPrep( pDestCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Destination query has same restrictions as source query.
|
|
// Must be at most one index used for optimizing. This means
|
|
// there must be at most one sub-query.
|
|
|
|
if ((pDestSubQuery = pDestCursor->pSubQueryList) != NULL)
|
|
{
|
|
if (pDestSubQuery->pNext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// If there is no current record in the source cursor, it is for one of
|
|
// three reasons: the source cursor has not yet been positioned, it is at
|
|
// EOF, or it is at BOF. Position the destination cursor to reflect these
|
|
// cases.
|
|
|
|
if (!pSrcCursor->uiLastRecID || !pSrcCursor->bOptimized)
|
|
{
|
|
if (pSrcCursor->ReadRc == FERR_EOF_HIT && pSrcCursor->bOptimized)
|
|
{
|
|
rc = flmCurPosToEOF( pDestCursor);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Anything else we will just position it to FIRST - don't
|
|
// really know what else to do.
|
|
|
|
rc = flmCurPosToBOF( pDestCursor);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize an FDB structure for various and sundry operations.
|
|
|
|
pDb = pDestCursor->pDb;
|
|
pvMark = pDb->TempPool.poolMark();
|
|
|
|
if( RC_BAD( rc = flmCurDbInit( pDestCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// At this point, we know we have an optimized source cursor and
|
|
// an optimized destination cursor and both of them have exactly
|
|
// one sub-query. We also know that we are not positioned on
|
|
// EOF or BOF.
|
|
|
|
// Get the current record from the source sub-query.
|
|
|
|
switch (pSrcSubQuery->OptInfo.eOptType)
|
|
{
|
|
case QOPT_USING_INDEX:
|
|
|
|
// Get the current key and DRN.
|
|
|
|
if (RC_BAD( rc = pSrcSubQuery->pFSIndexCursor->currentKeyBuf( pDb,
|
|
&pDb->TempPool, &pucKeyBuffer, &uiKeyLen,
|
|
&uiRecordDrn, &uiContainerNum)))
|
|
{
|
|
|
|
// Although currentKeyBuf is coded to allow for returning
|
|
// EOF or BOF, by this point we should have already taken
|
|
// care of that case.
|
|
|
|
flmAssert( rc != FERR_BOF_HIT && rc != FERR_EOF_HIT);
|
|
goto Exit;
|
|
}
|
|
|
|
// Retrieve the full record - must have for evaluation.
|
|
|
|
flmAssert( uiContainerNum == pSrcCursor->uiContainer);
|
|
if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
|
|
uiContainerNum, uiRecordDrn,
|
|
TRUE, NULL, NULL, &pRecord)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case QOPT_USING_PREDICATE:
|
|
if (pDestSubQuery->OptInfo.eOptType != QOPT_USING_PREDICATE)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
rc = pDestSubQuery->pPredicate->positionTo( (HFDB)pDb,
|
|
pSrcSubQuery->pPredicate);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
goto Exit;
|
|
case QOPT_SINGLE_RECORD_READ:
|
|
|
|
// Retrieve the record.
|
|
|
|
if (!pSrcSubQuery->pRec)
|
|
{
|
|
if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
|
|
pSrcCursor->uiContainer,
|
|
pSrcSubQuery->OptInfo.uiDrn,
|
|
TRUE, NULL, NULL, &pSrcSubQuery->pRec)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pSrcSubQuery->uiDrn = pSrcSubQuery->OptInfo.uiDrn;
|
|
pSrcSubQuery->bRecIsAKey = FALSE;
|
|
}
|
|
pRecord = pSrcSubQuery->pRec;
|
|
pRecord->AddRef();
|
|
uiRecordDrn = pSrcSubQuery->uiDrn;
|
|
break;
|
|
case QOPT_PARTIAL_CONTAINER_SCAN:
|
|
case QOPT_FULL_CONTAINER_SCAN:
|
|
|
|
// Get the current record and DRN
|
|
|
|
if (!pSrcSubQuery->pRec)
|
|
{
|
|
if (RC_BAD( rc = pSrcSubQuery->pFSDataCursor->currentRec( pDb,
|
|
&pSrcSubQuery->pRec,
|
|
&pSrcSubQuery->uiDrn)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
pRecord = pSrcSubQuery->pRec;
|
|
pRecord->AddRef();
|
|
uiRecordDrn = pSrcSubQuery->uiDrn;
|
|
pSrcSubQuery->bRecIsAKey = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// If the destination sub-query is optimized with a user predicate,
|
|
// cannot position source into it at this point. If the source
|
|
// was a user predicate, the positioning will already have been
|
|
// taken care of up above.
|
|
// Also, source and destination cursors must be on the same
|
|
// container.
|
|
|
|
if ((pDestSubQuery->OptInfo.eOptType == QOPT_USING_PREDICATE) ||
|
|
(pDestCursor->uiContainer != pSrcCursor->uiContainer))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify that the record passes the destination criteria.
|
|
|
|
if (RC_BAD( rc = flmCurEvalCriteria( pDestCursor, pDestSubQuery,
|
|
pRecord, FALSE, &uiRecMatch)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiRecMatch != FLM_TRUE)
|
|
{
|
|
rc = RC_SET( FERR_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
|
|
// Record passes destination criteria - do the positioning.
|
|
|
|
switch (pDestSubQuery->OptInfo.eOptType)
|
|
{
|
|
case QOPT_USING_INDEX:
|
|
|
|
// VISIT: Should we insist that if the source is
|
|
// also using an index that it be using the same
|
|
// index as the destination?
|
|
|
|
if (RC_BAD( rc = fdictGetIndex(
|
|
pDb->pDict,
|
|
pDb->pFile->bInLimitedMode,
|
|
pDestSubQuery->OptInfo.uiIxNum,
|
|
NULL, &pIxd)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If the source is not using an index, we need to generate
|
|
// our key buffer from pRecord.
|
|
|
|
if (pSrcSubQuery->OptInfo.eOptType != QOPT_USING_INDEX)
|
|
{
|
|
if (RC_BAD( rc = flmCurMakeKeyFromRec( pDb, pIxd, &pDb->TempPool,
|
|
pRecord, &pucKeyBuffer, &uiKeyLen)))
|
|
{
|
|
|
|
// FERR_ILLEGAL_OP means the record had multiple keys. We
|
|
// don't know where to position to in that case.
|
|
|
|
if (rc == FERR_ILLEGAL_OP)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
goto Exit;
|
|
}
|
|
}
|
|
if (RC_BAD( rc = pDestSubQuery->pFSIndexCursor->positionTo( pDb,
|
|
pucKeyBuffer, uiKeyLen, uiRecordDrn)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = pDestSubQuery->pFSIndexCursor->currentKey( pDb,
|
|
&pDestSubQuery->pRec,
|
|
&pDestSubQuery->uiDrn)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// We already know this key matches the criteria because we
|
|
// tested it above.
|
|
|
|
pDestSubQuery->bFirstReference = FALSE;
|
|
|
|
// These should have been set by the call to currentKey.
|
|
|
|
flmAssert( pDestSubQuery->pRec->getContainerID() ==
|
|
pDestCursor->uiContainer);
|
|
flmAssert( pDestSubQuery->pRec->getID() ==
|
|
pDestSubQuery->uiDrn);
|
|
|
|
pDestSubQuery->bRecIsAKey = TRUE;
|
|
pDestSubQuery->uiCurrKeyMatch = FLM_UNK;
|
|
break;
|
|
case QOPT_SINGLE_RECORD_READ:
|
|
if (uiRecordDrn != pDestSubQuery->OptInfo.uiDrn)
|
|
{
|
|
rc = RC_SET( FERR_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case QOPT_PARTIAL_CONTAINER_SCAN:
|
|
case QOPT_FULL_CONTAINER_SCAN:
|
|
if (RC_BAD( rc = pDestSubQuery->pFSDataCursor->positionTo( pDb,
|
|
uiRecordDrn)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
default:
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
pDestCursor->pCurrSubQuery = pDestSubQuery;
|
|
|
|
Exit:
|
|
|
|
if (pRecord)
|
|
{
|
|
pRecord->Release();
|
|
}
|
|
|
|
if (pDb)
|
|
{
|
|
pDb->TempPool.poolReset( pvMark);
|
|
fdbExit( pDb);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Saves the current cursor position.
|
|
****************************************************************************/
|
|
RCODE flmCurSavePosition(
|
|
CURSOR * pCursor
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
SUBQUERY * pSaveSubQuery;
|
|
|
|
pCursor->pSaveSubQuery = pSaveSubQuery = pCursor->pCurrSubQuery;
|
|
if (pSaveSubQuery)
|
|
{
|
|
switch (pSaveSubQuery->OptInfo.eOptType)
|
|
{
|
|
case QOPT_USING_INDEX:
|
|
if (RC_BAD( rc = pSaveSubQuery->pFSIndexCursor->savePosition()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case QOPT_USING_PREDICATE:
|
|
if (RC_BAD( rc = pSaveSubQuery->pPredicate->savePosition()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case QOPT_SINGLE_RECORD_READ:
|
|
pSaveSubQuery->bSaveRecReturned = pSaveSubQuery->bRecReturned;
|
|
break;
|
|
case QOPT_PARTIAL_CONTAINER_SCAN:
|
|
case QOPT_FULL_CONTAINER_SCAN:
|
|
if (RC_BAD( rc = pSaveSubQuery->pFSDataCursor->savePosition()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Restores the last cursor position that was saved.
|
|
****************************************************************************/
|
|
RCODE flmCurRestorePosition(
|
|
CURSOR * pCursor
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
SUBQUERY * pSaveSubQuery = pCursor->pSaveSubQuery;
|
|
|
|
if ((pCursor->pCurrSubQuery =
|
|
pSaveSubQuery =
|
|
pCursor->pSaveSubQuery) != NULL)
|
|
{
|
|
switch (pSaveSubQuery->OptInfo.eOptType)
|
|
{
|
|
case QOPT_USING_INDEX:
|
|
if (RC_BAD( rc =
|
|
pSaveSubQuery->pFSIndexCursor->restorePosition()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case QOPT_USING_PREDICATE:
|
|
if (RC_BAD( rc = pSaveSubQuery->pPredicate->restorePosition()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case QOPT_SINGLE_RECORD_READ:
|
|
pSaveSubQuery->bRecReturned = pSaveSubQuery->bSaveRecReturned;
|
|
break;
|
|
case QOPT_PARTIAL_CONTAINER_SCAN:
|
|
case QOPT_FULL_CONTAINER_SCAN:
|
|
if (RC_BAD( rc =
|
|
pSaveSubQuery->pFSDataCursor->restorePosition()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Sets a cursor to an absolute position.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmCurSetAbsolutePos(
|
|
CURSOR * pCursor,
|
|
FLMUINT uiPosition,
|
|
FLMBOOL bFallForward,
|
|
FLMUINT * puiPosition
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = NULL;
|
|
FLMBOOL bAbsPositionable;
|
|
SUBQUERY * pSubQuery;
|
|
FSIndexCursor * pFSIndexCursor = NULL;
|
|
FLMBOOL bSavedInvisTrans;
|
|
FLMBOOL bPassedCriteria;
|
|
FLMBOOL bDoRecMatch;
|
|
FLMUINT uiDrn;
|
|
|
|
// Verify that this is an absolute positionable query.
|
|
|
|
if (RC_BAD( rc = flmCurAbsPositionable( pCursor, &bAbsPositionable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (!bAbsPositionable)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
pDb = pCursor->pDb;
|
|
if (RC_BAD(rc = flmCurDbInit( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if we are to position to EOF or BOF.
|
|
|
|
if (uiPosition == (FLMUINT)(-1))
|
|
{
|
|
if (RC_BAD( rc = flmCurPosToEOF( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
*puiPosition = (FLMUINT)(-1);
|
|
}
|
|
else if (uiPosition == 0)
|
|
{
|
|
if (RC_BAD( rc = flmCurPosToBOF( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
*puiPosition = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set absolute position
|
|
|
|
pSubQuery = pCursor->pSubQueryList;
|
|
if (pSubQuery->OptInfo.eOptType == QOPT_USING_INDEX)
|
|
{
|
|
pFSIndexCursor = pSubQuery->pFSIndexCursor;
|
|
|
|
if (RC_OK( rc = pFSIndexCursor->setAbsolutePosition( pDb, uiPosition)))
|
|
{
|
|
|
|
// Get the current key so we can test it below
|
|
|
|
if (RC_BAD( rc = pFSIndexCursor->currentKey( pDb, &pSubQuery->pRec,
|
|
&pSubQuery->uiDrn)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pSubQuery->bFirstReference = FALSE;
|
|
|
|
// These should have been set by the call to currentKey.
|
|
|
|
flmAssert( pSubQuery->pRec->getContainerID() ==
|
|
pCursor->uiContainer);
|
|
flmAssert( pSubQuery->pRec->getID() == pSubQuery->uiDrn);
|
|
|
|
pSubQuery->bRecIsAKey = TRUE;
|
|
pSubQuery->uiCurrKeyMatch = FLM_UNK;
|
|
}
|
|
else // RC_BAD( rc)
|
|
{
|
|
if (rc != FERR_EOF_HIT)
|
|
{
|
|
flmAssert( rc != FERR_BOF_HIT);
|
|
goto Exit;
|
|
}
|
|
|
|
// rc == FERR_EOF_HIT. If bFallForward is TRUE, we simply
|
|
// set our position to EOF and return. Otherwise, we must
|
|
// fall backward to find the last key that passes the query.
|
|
|
|
if (bFallForward)
|
|
{
|
|
*puiPosition = (FLMUINT)(-1);
|
|
pCursor->uiLastRecID = 0;
|
|
pCursor->ReadRc = rc;
|
|
rc = FERR_OK;
|
|
goto Exit;
|
|
}
|
|
|
|
// Position to last - use CB_ENTER and CB_EXIT so that
|
|
// transaction state of pDb is preserved. We need
|
|
// to pass pDb into the getAbsolutePosition call below, and
|
|
// if we had an invisible transaction going, it still needs
|
|
// to be going below.
|
|
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
rc = flmCurSearch( FLM_CURSOR_CONFIG, pCursor, TRUE,
|
|
FALSE, NULL, NULL, NULL, &uiDrn);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (rc == FERR_BOF_HIT)
|
|
{
|
|
*puiPosition = 0;
|
|
pCursor->uiLastRecID = 0;
|
|
pCursor->ReadRc = rc;
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pCursor->ReadRc = FERR_OK;
|
|
pCursor->uiLastRecID = uiDrn;
|
|
rc = pFSIndexCursor->getAbsolutePosition( pDb,
|
|
puiPosition);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Need to verify that the key we are on will pass the query criteria.
|
|
// If not, fall forward or backward until we find one that does.
|
|
|
|
bPassedCriteria = FALSE;
|
|
bDoRecMatch = TRUE;
|
|
if (pSubQuery->OptInfo.bDoKeyMatch)
|
|
{
|
|
if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery,
|
|
pSubQuery->pRec, TRUE,
|
|
&pSubQuery->uiCurrKeyMatch)))
|
|
{
|
|
if (rc == FERR_TRUNCATED_KEY)
|
|
{
|
|
pSubQuery->uiCurrKeyMatch = FLM_UNK;
|
|
rc = FERR_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (pSubQuery->uiCurrKeyMatch == FLM_TRUE)
|
|
{
|
|
bDoRecMatch = FALSE;
|
|
bPassedCriteria = TRUE;
|
|
}
|
|
else if (pSubQuery->uiCurrKeyMatch != FLM_UNK)
|
|
{
|
|
flmAssert( pSubQuery->uiCurrKeyMatch == FLM_FALSE);
|
|
bDoRecMatch = FALSE;
|
|
|
|
// If we must evaluate DRN fields, we need to go to the
|
|
// next reference, so we set bFirstReference to FALSE.
|
|
// Otherwise, we set it to TRUE so it will skip to the
|
|
// next key below.
|
|
|
|
pSubQuery->bFirstReference = !pSubQuery->bHaveDrnFlds;
|
|
}
|
|
}
|
|
}
|
|
else // eOptType == QOPT_USING_PREDICATE
|
|
{
|
|
FLMUINT uiSeconds;
|
|
|
|
flmAssert( pSubQuery->OptInfo.eOptType == QOPT_USING_PREDICATE);
|
|
|
|
if (pCursor->uiTimeLimit)
|
|
{
|
|
uiSeconds = FLM_TIMER_UNITS_TO_SECS( pCursor->uiTimeLimit);
|
|
if (!uiSeconds)
|
|
{
|
|
uiSeconds = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiSeconds = 0;
|
|
}
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
rc = pSubQuery->pPredicate->positionToAbs( (HFDB)pDb, uiPosition,
|
|
bFallForward, uiSeconds,
|
|
puiPosition, &pSubQuery->uiDrn);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If we get back EOF or BOF, set things up accordingly.
|
|
|
|
if (*puiPosition == 0)
|
|
{
|
|
pCursor->ReadRc = RC_SET( FERR_BOF_HIT);
|
|
pCursor->uiLastRecID = 0;
|
|
goto Exit;
|
|
}
|
|
if (*puiPosition == (FLMUINT)(-1))
|
|
{
|
|
pCursor->ReadRc = RC_SET( FERR_EOF_HIT);
|
|
pCursor->uiLastRecID = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify that the record passes the rest of criteria.
|
|
|
|
bPassedCriteria = FALSE;
|
|
bDoRecMatch = TRUE;
|
|
}
|
|
|
|
// Need to verify that the record we positioned to
|
|
// passes the query criteria, unless we were able to
|
|
// pass the key up above. If we are using a predicate,
|
|
// bDoRecMatch is always set to TRUE.
|
|
|
|
if (bDoRecMatch)
|
|
{
|
|
FLMUINT uiRecMatch;
|
|
|
|
// Retrieve the record
|
|
|
|
if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer,
|
|
pSubQuery->uiDrn, TRUE, NULL, NULL, &pSubQuery->pRec)))
|
|
{
|
|
if (rc == FERR_NOT_FOUND && pFSIndexCursor)
|
|
{
|
|
rc = RC_SET( FERR_NO_REC_FOR_KEY);
|
|
}
|
|
goto Exit;
|
|
}
|
|
if (pFSIndexCursor)
|
|
{
|
|
pSubQuery->bRecIsAKey = FALSE;
|
|
}
|
|
|
|
// Evaluate the record against the query criteria
|
|
|
|
if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery,
|
|
pSubQuery->pRec, FALSE, &uiRecMatch)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bPassedCriteria = (FLMBOOL)((uiRecMatch == FLM_TRUE)
|
|
? (FLMBOOL)TRUE
|
|
: (FLMBOOL)FALSE);
|
|
}
|
|
|
|
// At this point, we know that the key passed our
|
|
// query criteria. Verify that it passes the record
|
|
// validator callback, if any.
|
|
|
|
if (pCursor->fnRecValidator && bPassedCriteria)
|
|
{
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
bPassedCriteria = (pCursor->fnRecValidator)( FLM_CURSOR_CONFIG,
|
|
(HFDB)pDb, pCursor->uiContainer,
|
|
pSubQuery->pRec, NULL, pCursor->RecValData, &rc);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
}
|
|
|
|
if (bPassedCriteria)
|
|
{
|
|
pCursor->ReadRc = FERR_OK;
|
|
pCursor->uiLastRecID = pSubQuery->uiDrn;
|
|
*puiPosition = uiPosition;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Position to next or previous - use CB_ENTER and CB_EXIT
|
|
// so that transaction state of pDb is preserved. We need
|
|
// to pass pDb into the getAbsolutePosition call below, and
|
|
// if we had an invisible transaction going, it still needs
|
|
// to be going below.
|
|
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
rc = flmCurSearch( FLM_CURSOR_CONFIG, pCursor, FALSE,
|
|
bFallForward, NULL, NULL, NULL, &uiDrn);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (rc == FERR_BOF_HIT || rc == FERR_EOF_HIT)
|
|
{
|
|
*puiPosition = (FLMUINT)((rc == FERR_BOF_HIT)
|
|
? (FLMUINT)0
|
|
: (FLMUINT)(-1));
|
|
pCursor->uiLastRecID = 0;
|
|
pCursor->ReadRc = rc;
|
|
rc = FERR_OK;
|
|
}
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
pCursor->ReadRc = FERR_OK;
|
|
pCursor->uiLastRecID = uiDrn;
|
|
if (pFSIndexCursor)
|
|
{
|
|
if (RC_BAD( rc = pFSIndexCursor->getAbsolutePosition( pDb,
|
|
puiPosition)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
rc = pSubQuery->pPredicate->getAbsPosition( (HFDB)pDb,
|
|
puiPosition);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
if (pDb)
|
|
{
|
|
flmExit( FLM_CURSOR_CONFIG, pDb, rc);
|
|
}
|
|
return( pCursor->rc = rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Allows configuration of cursor attributes, including assignment of
|
|
QuickFinder strings, indexes and search records.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmCursorConfig(
|
|
HFCURSOR hCursor,
|
|
eCursorConfigType eConfigType,
|
|
void * Value1,
|
|
void * Value2
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
CURSOR * pCursor = (CURSOR *)hCursor;
|
|
|
|
if (!pCursor)
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_INVALID_PARM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = flmCheckDatabaseState( pCursor->pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
switch( eConfigType)
|
|
{
|
|
case FCURSOR_ALLOW_DUPS:
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
if (pCursor->pDRNSet)
|
|
{
|
|
pCursor->pDRNSet->Release();
|
|
}
|
|
pCursor->pDRNSet = NULL;
|
|
pCursor->bEliminateDups = FALSE;
|
|
break;
|
|
|
|
case FCURSOR_ELIMINATE_DUPS:
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
if (pCursor->pDRNSet)
|
|
{
|
|
pCursor->pDRNSet->Release();
|
|
}
|
|
pCursor->pDRNSet = NULL;
|
|
pCursor->bEliminateDups = TRUE;
|
|
break;
|
|
|
|
case FCURSOR_CLEAR_QUERY:
|
|
if ((pCursor->pCSContext) && (pCursor->uiCursorId != FCS_INVALID_ID))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
flmCurClearSelect( pCursor);
|
|
break;
|
|
|
|
case FCURSOR_GEN_POS_KEYS:
|
|
if ((pCursor->pCSContext) && (pCursor->uiCursorId != FCS_INVALID_ID))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
rc = flmCurSetupPosKeyArray( pCursor);
|
|
break;
|
|
|
|
case FCURSOR_SET_HDB:
|
|
if ((pCursor->pCSContext) && (pCursor->uiCursorId != FCS_INVALID_ID))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
flmCurFinishTrans( pCursor);
|
|
pCursor->pDb = (FDB *)Value1;
|
|
break;
|
|
|
|
case FCURSOR_DISCONNECT:
|
|
flmCurFinishTransactions( pCursor, TRUE);
|
|
break;
|
|
|
|
case FCURSOR_RETURN_KEYS_OK:
|
|
pCursor->bOkToReturnKeys = (FLMBOOL)((FLMBOOL)Value1 ? TRUE : FALSE);
|
|
break;
|
|
|
|
case FCURSOR_SET_FLM_IX:
|
|
if ((pCursor->pCSContext) && (pCursor->uiCursorId != FCS_INVALID_ID))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
if (pCursor->bOptimized)
|
|
{
|
|
rc = RC_SET( FERR_ILLEGAL_OP);
|
|
}
|
|
else
|
|
{
|
|
pCursor->uiIndexNum = (FLMUINT)Value1;
|
|
}
|
|
break;
|
|
|
|
case FCURSOR_SET_OP_TIME_LIMIT:
|
|
{
|
|
FLMUINT uiTimeLimit;
|
|
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
uiTimeLimit = pCursor->uiTimeLimit = (FLMUINT)Value1;
|
|
if (uiTimeLimit)
|
|
{
|
|
pCursor->uiTimeLimit = FLM_SECS_TO_TIMER_UNITS( uiTimeLimit);
|
|
}
|
|
break;
|
|
}
|
|
case FCURSOR_SET_PERCENT_POS:
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_OK( rc = flmCurSetPercentPos( pCursor, (FLMUINT)Value1)))
|
|
{
|
|
pCursor->ReadRc = FERR_OK;
|
|
}
|
|
break;
|
|
|
|
case FCURSOR_SET_ABS_POS:
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
rc = flmCurSetAbsolutePos( pCursor,
|
|
*((FLMUINT *)Value1),
|
|
(FLMBOOL)Value2,
|
|
(FLMUINT *)Value1);
|
|
break;
|
|
|
|
case FCURSOR_SET_POS:
|
|
{
|
|
CURSOR * pPosCursor;
|
|
|
|
if ((pPosCursor = (CURSOR *)Value1) == NULL)
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_INVALID_PARM);
|
|
goto Exit;
|
|
}
|
|
|
|
if ((pCursor->pCSContext) || (pPosCursor->pCSContext))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_OK( rc = flmCurSetPos( pCursor, pPosCursor)))
|
|
{
|
|
pCursor->uiLastRecID = pPosCursor->uiLastRecID;
|
|
}
|
|
break;
|
|
}
|
|
case FCURSOR_SET_POS_FROM_DRN:
|
|
rc = flmCurSetPosFromDRN( pCursor, (FLMUINT)Value1);
|
|
break;
|
|
|
|
case FCURSOR_SET_REC_TYPE:
|
|
if ((pCursor->pCSContext) && (pCursor->uiCursorId != FCS_INVALID_ID))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
if (pCursor->bOptimized)
|
|
{
|
|
rc = RC_SET( FERR_ILLEGAL_OP);
|
|
}
|
|
else
|
|
{
|
|
pCursor->uiRecType = (FLMUINT)Value1;
|
|
}
|
|
break;
|
|
|
|
case FCURSOR_SET_REC_VALIDATOR:
|
|
pCursor->fnRecValidator = (REC_VALIDATOR_HOOK)((FLMUINT)Value1);
|
|
pCursor->RecValData = Value2;
|
|
break;
|
|
|
|
case FCURSOR_SET_STATUS_HOOK:
|
|
pCursor->fnStatus = (STATUS_HOOK)((FLMUINT)Value1);
|
|
pCursor->StatusData = Value2;
|
|
pCursor->uiLastCBTime = FLM_GET_TIMER();
|
|
break;
|
|
|
|
case FCURSOR_SAVE_POSITION:
|
|
rc = flmCurSavePosition( pCursor);
|
|
break;
|
|
|
|
case FCURSOR_RESTORE_POSITION:
|
|
rc = flmCurRestorePosition( pCursor);
|
|
break;
|
|
|
|
default:
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
break;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/************************************************************************
|
|
Desc : Returns whether or not a query is positionable.
|
|
*************************************************************************/
|
|
FSTATIC RCODE flmCurPositionable(
|
|
CURSOR * pCursor,
|
|
FLMBOOL * pbPositionable)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
SUBQUERY * pSubQuery;
|
|
|
|
*pbPositionable = FALSE;
|
|
|
|
// Optimize if necessary.
|
|
|
|
if (!pCursor->bOptimized)
|
|
{
|
|
if( RC_BAD( rc = flmCurPrep( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Must use an index and only one index. Also, the bDoRecMatch
|
|
// flag must be FALSE, because we must be able to evaluate the
|
|
// query entirely using only the fields in the index. Finally,
|
|
// we must not need to evaluate DRN fields. Normally the
|
|
// bHaveDrnFlds would not be a problem for evaluating a criteria
|
|
// using only an index key, but we do not have DRNs at the higher
|
|
// levels of the B-Tree, so having a DRN in the selection criteria
|
|
// ruins positionability.
|
|
|
|
if (((pSubQuery = pCursor->pSubQueryList) != NULL) &&
|
|
(!pSubQuery->pNext) &&
|
|
(pSubQuery->OptInfo.eOptType == QOPT_USING_INDEX) &&
|
|
(!pSubQuery->OptInfo.bDoRecMatch) &&
|
|
(!pSubQuery->bHaveDrnFlds))
|
|
{
|
|
*pbPositionable = TRUE;
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/************************************************************************
|
|
Desc : Returns whether or not a query is absolute positionable.
|
|
*************************************************************************/
|
|
FSTATIC RCODE flmCurAbsPositionable(
|
|
CURSOR * pCursor,
|
|
FLMBOOL * pbAbsPositionable)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
SUBQUERY * pSubQuery;
|
|
FLMBOOL bSavedInvisTrans;
|
|
|
|
*pbAbsPositionable = FALSE;
|
|
|
|
// Optimize if necessary.
|
|
|
|
if (!pCursor->bOptimized)
|
|
{
|
|
if( RC_BAD( rc = flmCurPrep( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Must have only one subquery that either uses an index that
|
|
// supports absolute positioning or uses a predicate that
|
|
// supports it.
|
|
|
|
if (((pSubQuery = pCursor->pSubQueryList) != NULL) &&
|
|
!pSubQuery->pNext)
|
|
{
|
|
if (pSubQuery->OptInfo.eOptType == QOPT_USING_INDEX)
|
|
{
|
|
*pbAbsPositionable =
|
|
pSubQuery->pFSIndexCursor->isAbsolutePositionable();
|
|
}
|
|
else if (pSubQuery->OptInfo.eOptType == QOPT_USING_PREDICATE)
|
|
{
|
|
CB_ENTER( pCursor->pDb, &bSavedInvisTrans);
|
|
rc = pSubQuery->pPredicate->isAbsPositionable( (HFDB)pCursor->pDb,
|
|
pbAbsPositionable);
|
|
CB_EXIT( pCursor->pDb, bSavedInvisTrans);
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/************************************************************************
|
|
Desc : Returns absolute position of cursor.
|
|
*************************************************************************/
|
|
FSTATIC RCODE flmCurGetAbsolutePos(
|
|
CURSOR * pCursor,
|
|
FLMUINT * puiPosition
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = NULL;
|
|
FLMBOOL bAbsPositionable;
|
|
FLMBOOL bSavedInvisTrans;
|
|
|
|
// Verify that this is an absolute positionable query.
|
|
|
|
if (RC_BAD( rc = flmCurAbsPositionable( pCursor, &bAbsPositionable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (!bAbsPositionable)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
pDb = pCursor->pDb;
|
|
if (RC_BAD(rc = flmCurDbInit( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if we are positioned on EOF or BOF. Return special values
|
|
// for these.
|
|
|
|
if (!pCursor->uiLastRecID)
|
|
{
|
|
if (pCursor->ReadRc == FERR_EOF_HIT)
|
|
{
|
|
*puiPosition = (FLMUINT)(-1); // EOF
|
|
}
|
|
else
|
|
{
|
|
*puiPosition = 0; // BOF
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pCursor->pSubQueryList->OptInfo.eOptType == QOPT_USING_INDEX)
|
|
{
|
|
|
|
// Get absolute position from the first sub-query's index cursor.
|
|
|
|
if (RC_BAD( rc =
|
|
pCursor->pSubQueryList->pFSIndexCursor->getAbsolutePosition( pDb,
|
|
puiPosition)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else // eOptType == QOPT_USING_PREDICATE
|
|
{
|
|
flmAssert( pCursor->pSubQueryList->OptInfo.eOptType ==
|
|
QOPT_USING_PREDICATE);
|
|
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
rc = pCursor->pSubQueryList->pPredicate->getAbsPosition(
|
|
(HFDB)pDb, puiPosition);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
if (pDb)
|
|
{
|
|
flmExit( FLM_CURSOR_GET_CONFIG, pDb, rc);
|
|
}
|
|
return( pCursor->rc = rc);
|
|
}
|
|
|
|
/************************************************************************
|
|
Desc : Returns absolute count for the cursor's index. NOTE: This is
|
|
not necessarily the same thing as the number of records that
|
|
will pass the filter criteria.
|
|
*************************************************************************/
|
|
FSTATIC RCODE flmCurGetAbsoluteCount(
|
|
CURSOR * pCursor,
|
|
FLMUINT * puiCount
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = NULL;
|
|
FLMBOOL bAbsPositionable;
|
|
FLMBOOL bTotalEstimated;
|
|
FLMBOOL bSavedInvisTrans;
|
|
|
|
// Verify that this is an absolute positionable query.
|
|
|
|
if (RC_BAD( rc = flmCurAbsPositionable( pCursor, &bAbsPositionable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (!bAbsPositionable)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
pDb = pCursor->pDb;
|
|
if (RC_BAD(rc = flmCurDbInit( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (pCursor->pSubQueryList->OptInfo.eOptType == QOPT_USING_INDEX)
|
|
{
|
|
|
|
// Get absolute count from the first sub-query's index cursor.
|
|
|
|
if (RC_BAD( rc =
|
|
pCursor->pSubQueryList->pFSIndexCursor->getTotalReferences( pDb,
|
|
puiCount, &bTotalEstimated)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else // eOptType == QOPT_USING_PREDICATE
|
|
{
|
|
flmAssert( pCursor->pSubQueryList->OptInfo.eOptType ==
|
|
QOPT_USING_PREDICATE);
|
|
|
|
CB_ENTER( pDb, &bSavedInvisTrans);
|
|
rc = pCursor->pSubQueryList->pPredicate->getAbsCount( (HFDB)pDb,
|
|
puiCount);
|
|
CB_EXIT( pDb, bSavedInvisTrans);
|
|
}
|
|
|
|
Exit:
|
|
if (pDb)
|
|
{
|
|
flmExit( FLM_CURSOR_GET_CONFIG, pDb, rc);
|
|
}
|
|
return( pCursor->rc = rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns FLAIM cursor configuration values.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmCursorGetConfig(
|
|
HFCURSOR hCursor,
|
|
eCursorGetConfigType eGetConfigType,
|
|
void * Value1,
|
|
void * Value2)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
CURSOR * pCursor = (CURSOR *)hCursor;
|
|
|
|
if (!pCursor)
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_INVALID_PARM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = flmCheckDatabaseState( pCursor->pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
switch( eGetConfigType)
|
|
{
|
|
case FCURSOR_GET_PERCENT_POS:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
rc = flmCurGetPercentPos( pCursor, (FLMUINT *)Value1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_ABS_POS:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
rc = flmCurGetAbsolutePos( pCursor, (FLMUINT *)Value1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_ABS_COUNT:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
rc = flmCurGetAbsoluteCount( pCursor, (FLMUINT *)Value1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_OPT_INFO_LIST:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
OPT_INFO * pOptInfoArray = (OPT_INFO *)Value1;
|
|
FLMUINT * puiSubQueryCnt = (FLMUINT *)Value2;
|
|
FLMUINT uiSubQueryCnt = 0;
|
|
SUBQUERY * pSubQuery;
|
|
|
|
if (!pCursor->bOptimized)
|
|
{
|
|
if (RC_BAD( rc = flmCurPrep( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
for (pSubQuery = pCursor->pSubQueryList;
|
|
pSubQuery;
|
|
pSubQuery = pSubQuery->pNext)
|
|
{
|
|
if (pOptInfoArray)
|
|
{
|
|
f_memcpy( &pOptInfoArray[ uiSubQueryCnt],
|
|
&pSubQuery->OptInfo, sizeof( OPT_INFO));
|
|
}
|
|
|
|
uiSubQueryCnt++;
|
|
}
|
|
|
|
*puiSubQueryCnt = uiSubQueryCnt;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_OPT_INFO:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
if (!pCursor->bOptimized)
|
|
{
|
|
if (RC_BAD( rc = flmCurPrep( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (pCursor->pSubQueryList)
|
|
{
|
|
f_memcpy( (OPT_INFO *)Value2,
|
|
&pCursor->pSubQueryList->OptInfo, sizeof( OPT_INFO));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_FLM_IX:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
FLMUINT uiIxNum = 0;
|
|
FLMUINT uiIndexInfo = HAVE_NO_INDEX;
|
|
FLMUINT uiSubqueryCount = 0;
|
|
|
|
if (!pCursor->bOptimized)
|
|
{
|
|
if (RC_BAD( rc = flmCurPrep( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( pCursor->pSubQueryList)
|
|
{
|
|
SUBQUERY * pTmpSubQuery = pCursor->pSubQueryList;
|
|
FLMUINT uiSQIndex = 0;
|
|
|
|
while( pTmpSubQuery)
|
|
{
|
|
uiSubqueryCount++;
|
|
|
|
if (pTmpSubQuery->OptInfo.eOptType == QOPT_USING_INDEX)
|
|
{
|
|
uiSQIndex = pTmpSubQuery->OptInfo.uiIxNum;
|
|
}
|
|
else if (pTmpSubQuery->OptInfo.eOptType ==
|
|
QOPT_USING_PREDICATE)
|
|
{
|
|
FLMUINT uiTmpIndexInfo;
|
|
|
|
uiSQIndex = pTmpSubQuery->pPredicate->getIndex(
|
|
&uiTmpIndexInfo);
|
|
|
|
if (uiTmpIndexInfo == HAVE_MULTIPLE_INDEXES)
|
|
{
|
|
if (!uiIxNum)
|
|
{
|
|
uiIxNum = uiSQIndex;
|
|
}
|
|
uiIndexInfo = HAVE_MULTIPLE_INDEXES;
|
|
break;
|
|
}
|
|
else if (uiTmpIndexInfo == HAVE_ONE_INDEX_MULT_PARTS)
|
|
{
|
|
if (uiIxNum && uiSQIndex != uiIxNum)
|
|
{
|
|
uiIndexInfo = HAVE_MULTIPLE_INDEXES;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// NOTE: At this point we know that uiIxNum is either
|
|
// zero or equal to uiSQIndex, so by
|
|
// assigning uiIxNum to uiSQIndex it will cause us to
|
|
// assign uiIndexInfo to HAVE_ONE_INDEX_MULT_PARTS
|
|
// below.
|
|
|
|
flmAssert( uiSQIndex);
|
|
uiIxNum = uiSQIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uiSQIndex)
|
|
{
|
|
if( !uiIxNum)
|
|
{
|
|
uiIxNum = uiSQIndex;
|
|
if( uiSubqueryCount > 1)
|
|
{
|
|
uiIndexInfo = HAVE_ONE_INDEX_MULT_PARTS;
|
|
}
|
|
else
|
|
{
|
|
uiIndexInfo = HAVE_ONE_INDEX;
|
|
}
|
|
}
|
|
else if( uiIxNum != uiSQIndex)
|
|
{
|
|
uiIndexInfo = HAVE_MULTIPLE_INDEXES;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
uiIndexInfo = HAVE_ONE_INDEX_MULT_PARTS;
|
|
}
|
|
}
|
|
else if( uiIxNum)
|
|
{
|
|
uiIndexInfo = HAVE_ONE_INDEX_MULT_PARTS;
|
|
}
|
|
pTmpSubQuery = pTmpSubQuery->pNext;
|
|
}
|
|
}
|
|
|
|
if( Value1)
|
|
{
|
|
*((FLMUINT *)Value1) = uiIxNum;
|
|
}
|
|
|
|
if( Value2)
|
|
{
|
|
*((FLMUINT *)Value2) = uiIndexInfo;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_REC_TYPE:
|
|
{
|
|
*((FLMUINT *)Value1) = pCursor->uiRecType;
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_FLAGS:
|
|
{
|
|
*((FLMUINT *)Value1) = pCursor->QTInfo.uiFlags;
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_STATE:
|
|
{
|
|
*((FLMUINT *)Value1) = 0;
|
|
|
|
if (pCursor->QTInfo.pTopNode ||
|
|
pCursor->QTInfo.pCurOpNode ||
|
|
pCursor->QTInfo.pCurAtomNode)
|
|
{
|
|
*((FLMUINT *)Value1) |= FCURSOR_HAVE_CRITERIA;
|
|
}
|
|
|
|
if (pCursor->QTInfo.uiExpecting & FLM_Q_OPERATOR)
|
|
{
|
|
*((FLMUINT *)Value1) |= FCURSOR_EXPECTING_OPERATOR;
|
|
}
|
|
|
|
if ((pCursor->QTInfo.uiNestLvl == 0) ||
|
|
((pCursor->QTInfo.uiExpecting & FLM_Q_OPERATOR) &&
|
|
pCursor->QTInfo.pTopNode))
|
|
{
|
|
*((FLMUINT *)Value1) |= FCURSOR_QUERY_COMPLETE;
|
|
}
|
|
|
|
if (pCursor->bOptimized)
|
|
{
|
|
*((FLMUINT *)Value1) |=
|
|
(FCURSOR_QUERY_OPTIMIZED | FCURSOR_READ_PERFORMED);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_POSITIONABLE:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
rc = flmCurPositionable( pCursor, (FLMBOOL *)Value1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_GET_ABS_POSITIONABLE:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
rc = flmCurAbsPositionable( pCursor, (FLMBOOL *)Value1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_AT_BOF:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
if (!pCursor->bOptimized)
|
|
{
|
|
if (RC_BAD( rc = flmCurPrep( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
*((FLMBOOL *)Value1) = (FLMBOOL)((!pCursor->uiLastRecID &&
|
|
pCursor->ReadRc == FERR_BOF_HIT)
|
|
? (FLMBOOL)TRUE
|
|
: (FLMBOOL)FALSE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FCURSOR_AT_EOF:
|
|
{
|
|
if (pCursor->pCSContext)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
}
|
|
else
|
|
{
|
|
if (!pCursor->bOptimized)
|
|
{
|
|
if (RC_BAD( rc = flmCurPrep( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
*((FLMBOOL *)Value1) = (FLMBOOL)((!pCursor->uiLastRecID &&
|
|
pCursor->ReadRc == FERR_EOF_HIT)
|
|
? (FLMBOOL)TRUE
|
|
: (FLMBOOL)FALSE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Given a IFD this function will verify that the input path matches and
|
|
that it is in the same position (for compound keys)
|
|
****************************************************************************/
|
|
FSTATIC FLMBOOL flmCurMatchIndexPath(
|
|
IFD * pIfd,
|
|
FLMUINT * puiField,
|
|
FLMUINT uiPos)
|
|
{
|
|
FLMUINT * puiIndexFieldPath;
|
|
FLMBOOL bIsMatch;
|
|
|
|
// Are both fields at the same position within a compound key
|
|
|
|
if (pIfd->uiCompoundPos != uiPos)
|
|
{
|
|
bIsMatch = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Check in PARENT to CHILD order.
|
|
|
|
puiIndexFieldPath = pIfd->pFieldPathPToC;
|
|
while (*puiIndexFieldPath)
|
|
{
|
|
if (*puiField != *puiIndexFieldPath)
|
|
{
|
|
bIsMatch = FALSE;
|
|
goto Exit;
|
|
}
|
|
puiIndexFieldPath++;
|
|
puiField++;
|
|
}
|
|
bIsMatch = (*puiField == 0) ? TRUE : FALSE;
|
|
Exit:
|
|
return( bIsMatch);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Uses the specified field path[s] to find a matching ordering index.
|
|
Note: FlmCursorConfig( type == FCURSOR_SET_FLM_IX) and FlmCursorSetOrderIndex
|
|
cannot both be called for the same cursor (they will override each
|
|
other).
|
|
Warning: The index selected from this call will be used for query optimization
|
|
in addition to ordering the results. This could result in slower
|
|
query performance.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmCursorSetOrderIndex(
|
|
HFCURSOR hCursor,
|
|
FLMUINT * puiFieldPaths, /* List of field paths to match on. Each path
|
|
is terminated with a single 0, and the
|
|
entire list is terminated by two 0's */
|
|
FLMUINT * puiIndexRV) /* [optional] index id of matching index.
|
|
A value of 0 indicates that no match
|
|
was found. */
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
CURSOR * pCursor = (CURSOR *)hCursor;
|
|
FDB * pDb = NULL;
|
|
IFD * pIfd;
|
|
IFD * pIfd2;
|
|
IXD * pIxd;
|
|
FLMUINT * puiField = puiFieldPaths;
|
|
FLMUINT uiPos;
|
|
FLMUINT uiScore;
|
|
FLMUINT uiBestScore = 0;
|
|
FLMUINT uiBestIndex = 0;
|
|
|
|
if (!pCursor)
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_INVALID_PARM);
|
|
goto Exit;
|
|
}
|
|
|
|
flmAssert( puiFieldPaths != NULL);
|
|
flmAssert( puiFieldPaths[ 0] != 0); // must give at least one field
|
|
|
|
if (puiIndexRV)
|
|
{
|
|
*puiIndexRV = 0;
|
|
}
|
|
|
|
pDb = pCursor->pDb;
|
|
if (RC_BAD( rc = flmCurDbInit( pCursor)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Position to the last child field in the first path.
|
|
|
|
while (*puiField)
|
|
{
|
|
puiField++;
|
|
}
|
|
puiField--;
|
|
|
|
// Is the first field even indexed?
|
|
|
|
if (RC_BAD( rc = fdictGetField( pDb->pDict, *puiField, NULL,
|
|
&pIfd, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (!pIfd)
|
|
{
|
|
// First field was not indexed (at all) so return a index id of ZERO.
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Loop through all indexes on the first field.
|
|
|
|
for ( ; pIfd; pIfd = pIfd->pNextInChain)
|
|
{
|
|
uiScore = 0;
|
|
|
|
pIxd = pIfd->pIxd;
|
|
|
|
// If index is on a different container then skip this index.
|
|
// NOTE: If pIxd->uiContainerNum is zero, it covers all
|
|
// containers.
|
|
|
|
if (pIxd->uiContainerNum &&
|
|
pIxd->uiContainerNum != pCursor->uiContainer)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Verify that this index contains all requested field paths
|
|
|
|
pIfd2 = pIxd->pFirstIfd;
|
|
for (uiPos = 0, puiField = puiFieldPaths;
|
|
*puiField;
|
|
pIfd2++, uiPos++)
|
|
{
|
|
if (!flmCurMatchIndexPath( pIfd2, puiField, uiPos))
|
|
{
|
|
goto NextIndex;
|
|
}
|
|
|
|
// The path matching requirements has been meet now score this field
|
|
// Scoring Rules for each field path:
|
|
// Value Index 4 points
|
|
// SubSring/EachWord 2 points - index provides some ordering
|
|
// Context Index 0 points - index provides no ordering
|
|
// Scoring Boost 1 point - when # of field paths in index match
|
|
// # of field paths supplied.
|
|
|
|
if (pIfd2->uiFlags & IFD_CONTEXT)
|
|
{
|
|
; // Context index gets no score.
|
|
}
|
|
else if (pIfd2->uiFlags & IFD_SUBSTRING)
|
|
{
|
|
uiScore += 2;
|
|
}
|
|
else if (pIfd2->uiFlags & IFD_EACHWORD)
|
|
{
|
|
uiScore += 2;
|
|
}
|
|
else
|
|
{
|
|
uiScore += 4;
|
|
}
|
|
|
|
// Position to the the next field path
|
|
|
|
while (*puiField)
|
|
{
|
|
puiField++;
|
|
}
|
|
puiField++; // Skip single null terminator.
|
|
|
|
// See if all components for index have been visited.
|
|
|
|
if (pIfd2->uiFlags & IFD_LAST)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the number of requested fields equals the number of fields
|
|
// contained within the index then add in a exact match score.
|
|
|
|
if (pIfd2->uiFlags & IFD_LAST)
|
|
{
|
|
uiScore += 1;
|
|
}
|
|
|
|
// Always remember the highest index score
|
|
|
|
if (uiBestScore < uiScore)
|
|
{
|
|
uiBestScore = uiScore;
|
|
uiBestIndex = pIxd->uiIndexNum;
|
|
}
|
|
|
|
NextIndex:
|
|
;
|
|
}
|
|
|
|
Exit:
|
|
if (puiIndexRV)
|
|
{
|
|
*puiIndexRV = uiBestIndex;
|
|
if (uiBestIndex && RC_OK( rc))
|
|
{
|
|
rc = FlmCursorConfig( hCursor, FCURSOR_SET_FLM_IX,
|
|
(void *) uiBestIndex, (void *) 0);
|
|
}
|
|
}
|
|
|
|
if (pDb)
|
|
{
|
|
(void)fdbExit( pDb);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void flmCurFreeSQList(
|
|
CURSOR * pCursor,
|
|
FLMBOOL bFreeEverything)
|
|
{
|
|
SUBQUERY * pSubQuery;
|
|
|
|
for( pSubQuery = pCursor->pSubQueryList;
|
|
pSubQuery;
|
|
pSubQuery = pSubQuery->pNext)
|
|
{
|
|
flmSQFree( pSubQuery, bFreeEverything);
|
|
}
|
|
|
|
if (bFreeEverything)
|
|
{
|
|
pCursor->SQPool.poolReset();
|
|
pCursor->pSubQueryList = NULL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMUINT flmGetPathLen(
|
|
FLMUINT * pFldPath)
|
|
{
|
|
FLMUINT uiPathLen = 0;
|
|
|
|
if( pFldPath)
|
|
{
|
|
for( ; uiPathLen < GED_MAXLVLNUM + 1; uiPathLen++)
|
|
{
|
|
if( !pFldPath[ uiPathLen])
|
|
break;
|
|
}
|
|
}
|
|
return uiPathLen;
|
|
}
|