//-------------------------------------------------------------------------
// Desc:	Query record retrieval
// Tabs:	3
//
//		Copyright (c) 1996-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: fqget.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------

#include "flaimsys.h"

FSTATIC RCODE flmCurCSTestRec(
	CURSOR *			pCursor,
	FLMUINT			uiDrn,
	FlmRecord *		pTestRec,
	FLMBOOL *		pbIsMatchRV);

/****************************************************************************
Desc: Makes a SET_DEL from a record.
****************************************************************************/
RCODE flmCurMakeKeyFromRec(
	FDB *				pDb,
	IXD *				pIxd,
	F_Pool *			pPool,
	FlmRecord *		pRec,
	FLMBYTE **		ppucKeyBuffer,
	FLMUINT *		puiKeyLen)
{
	REC_KEY *	pKey = NULL;
	RCODE			rc = FERR_OK;

	// Set up CDL table and other KREF stuff in FDB.

	if (RC_BAD( rc = KrefCntrlCheck( pDb)))
	{
		goto Exit;
	}

	// Parse the keys from the record, and verify that the record contains
	// only one key.

	rc = flmGetRecKeys( pDb, pIxd, pRec, pRec->getContainerID(),
								TRUE, pPool, &pKey);

	KYAbortCurrentRecord( pDb);

	if (RC_BAD( rc))
	{
		goto Exit;
	}
	else if (!pKey)
	{
		rc = RC_SET( FERR_NOT_FOUND);
		goto Exit;
	}
	if (pKey->pNextKey)
	{
		rc = RC_SET( FERR_ILLEGAL_OP);
		goto Exit;
	}

	// Now allocate a key buffer to hold the key.

	if (!(*ppucKeyBuffer))
	{
		if( RC_BAD( rc = pPool->poolCalloc( MAX_KEY_SIZ, (void **)ppucKeyBuffer)))
		{
			goto Exit;
		}
	}

	// We pass in pRec->getContainerID(), because every key we generate
	// came from pRec, and we want to use its container number.

	if (RC_BAD( rc = KYTreeToKey( pDb, pIxd, pKey->pKey,
											pRec->getContainerID(),
											*ppucKeyBuffer,
											puiKeyLen, 0)))
	{
		goto Exit;
	}
Exit:

	while (pKey)
	{
		pKey->pKey->Release();
		pKey->pKey = NULL;
		pKey = pKey->pNextKey;
	}
	return( rc);
}

/****************************************************************************
Desc:	Sets a query's position from a passed-in DRN.
****************************************************************************/
RCODE flmCurSetPosFromDRN(
	CURSOR *			pCursor,
	FLMUINT			uiDRN
	)
{
	RCODE				rc = FERR_OK;
	FlmRecord *		pRec = NULL;
	F_Pool *			pTempPool;
	FDB *				pDb = NULL;
	IXD *				pIxd;
	LFILE *			pLFile;
	SUBQUERY *		pSubQuery;
	FLMBOOL			bPositioned;
	FLMBYTE *		pucKeyBuffer = NULL;
	FLMUINT			uiKeyLen;

	pDb = pCursor->pDb;
	if (RC_BAD( rc = flmCurDbInit( pCursor)))
	{
		goto Exit;
	}
	pTempPool = &pDb->TempPool;

	// Read the record associated with the DRN, and construct an index key

	rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer, uiDRN,
						FALSE, NULL, NULL, &pRec);

	if (rc == FERR_NOT_FOUND)
	{
		if (RC_BAD( rc = fdictGetContainer( pDb->pDict,
										pCursor->uiContainer, &pLFile)))
		{
			goto Exit;
		}

		if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDRN,
				&pRec, NULL, NULL)))
		{
			goto Exit;
		}
	}
	else if (RC_BAD( rc))
	{
		goto Exit;
	}
	
	// Optimize the subqueries as necessary

	if (!pCursor->bOptimized)
	{
		if (RC_BAD( rc = flmCurPrep( pCursor)))
		{
			goto Exit;
		}
	}

	// Go through the subquery list to find the sub-query into which the
	// DRN falls.

	bPositioned = FALSE;
	for( pSubQuery = pCursor->pSubQueryList;
			pSubQuery && !bPositioned;
			pSubQuery = pSubQuery->pNext)
	{
		FLMUINT	uiResult;

		// See if the record satisifies this sub-query's criteria.
		// If not, we cannot position into this sub-query.

		if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pRec,
											FALSE, &uiResult)))
		{
			goto Exit;
		}

		// Found a sub-query where this DRN passes.  Set this sub-query to
		// be the current sub-query on certain conditions.

		if (uiResult == FLM_TRUE)
		{
			switch (pSubQuery->OptInfo.eOptType)
			{
				case QOPT_USING_INDEX:
					if (RC_BAD( rc = fdictGetIndex( pDb->pDict,
												pDb->pFile->bInLimitedMode,
												pSubQuery->OptInfo.uiIxNum, 
												NULL, &pIxd)))
					{
						goto Exit;
					}

					// If we cannot create a key for this index, go to the next
					// subquery.

					if (RC_BAD( rc = flmCurMakeKeyFromRec(	pDb, pIxd, pTempPool,
												pRec, &pucKeyBuffer, &uiKeyLen)))
					{
						if (rc == FERR_NOT_FOUND || rc == FERR_ILLEGAL_OP)
						{
							rc = FERR_OK;
							break;
						}
						goto Exit;
					}

					// Position to the key and drn - If we can't just go to the
					// next subquery.

					if (RC_BAD( rc = pSubQuery->pFSIndexCursor->positionTo( pDb,
												pucKeyBuffer, uiKeyLen, uiDRN)))
					{
						if (rc == FERR_NOT_FOUND ||
							 rc == FERR_EOF_HIT ||
							 rc == FERR_BOF_HIT)
						{
							rc = FERR_OK;
							break;
						}
						goto Exit;
					}

					// Retrieve the current key and DRN from the index cursor.

					rc = pSubQuery->pFSIndexCursor->currentKey( pDb,
											&pSubQuery->pRec, &pSubQuery->uiDrn);
					if (RC_BAD( rc))
					{
						goto Exit;
					}
					bPositioned = TRUE;
					pSubQuery->uiCurrKeyMatch = FLM_UNK;
					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;
					break;
				case QOPT_USING_PREDICATE:
					// Can't position in a predicate - go to next sub-query.
					break;
				case QOPT_SINGLE_RECORD_READ:
					bPositioned = TRUE;
					pSubQuery->uiDrn = uiDRN;
					break;
				case QOPT_PARTIAL_CONTAINER_SCAN:
				case QOPT_FULL_CONTAINER_SCAN:
					rc = pSubQuery->pFSDataCursor->positionTo( pDb, uiDRN);
					if (RC_BAD( rc))
					{
						if (rc == FERR_NOT_FOUND ||
							 rc == FERR_EOF_HIT ||
							 rc == FERR_BOF_HIT)
						{
							rc = FERR_OK;
							break;
						}
						goto Exit;
					}
					bPositioned = TRUE;
					break;
				default:
					flmAssert( 0);
					break;
			}
			if (bPositioned)
			{
				pCursor->uiLastRecID = uiDRN;
				pCursor->pCurrSubQuery = pSubQuery;
				break;
			}
		}
	}
	if (!bPositioned)
	{
		rc = RC_SET( FERR_NOT_FOUND);
	}

Exit:

	if (pRec)
	{
		pRec->Release();
	}
	flmExit( FLM_CURSOR_CONFIG, pDb, rc);

	return( pCursor->rc = rc);
}

/****************************************************************************
Desc:	Given a cursor and two DRNs, this API does the following:
****************************************************************************/
FLMEXP RCODE FLMAPI FlmCursorCompareDRNs(
	HFCURSOR		hCursor,
	FLMUINT		uiDRN1,
	FLMUINT		uiDRN2,
	FLMUINT		uiTimeLimit,
	FLMINT *		piCmpResult,
	FLMBOOL *	pbTimedOut,
	FLMUINT *	puiCount)
{
	RCODE					rc = FERR_OK;
	CURSOR *				pCursor = (CURSOR *)hCursor;
	FDB *					pDb = NULL;
	F_Pool *				pTempPool;
	FLMBYTE *			pucKey1;
	FLMUINT				uiKey1Len;
	FLMBYTE *			pucKey2;
	FLMUINT				uiKey2Len;
	FlmRecord *			pRec1 = NULL;
	FlmRecord *			pRec2 = NULL;
	IXD *					pIxd;
	FSIndexCursor *	pTmpFSIndexCursor = NULL;
	FSIndexCursor *	pSaveFSIndexCursor = NULL;
	FLMUINT				uiSaveTimeLimit = 0;
	LFILE *				pLFile;
	FLMUINT				uiIndexNum;
	FLMINT				iCmp;

	// Verify that return params are non-NULL.
	
	flmAssert( piCmpResult != NULL);
	flmAssert( pbTimedOut != NULL);
	flmAssert( puiCount != NULL);
	flmAssert( pCursor != NULL);

	if (!pCursor)
	{
		flmAssert( 0);
		rc = RC_SET( FERR_INVALID_PARM);
		goto Exit;
	}

	if (pCursor->pCSContext)
	{
		rc = RC_SET( FERR_NOT_IMPLEMENTED);
		goto Exit;
	}

	// If the two DRNs are equal, there is no need to read the records to compare
	// their keys.  Furthermore, if they are in the cursor's result set, the
	// return count will be 2, otherwise it will be 0.
	
	if (uiDRN1 == uiDRN2)
	{
		*piCmpResult = 0;
		if (uiTimeLimit)
		{
			FLMBOOL	bInRSet;
			
			if (RC_BAD( rc = FlmCursorTestDRN( hCursor, uiDRN1, &bInRSet)))
			{
				goto Exit;
			}
			if (bInRSet)
			{
				*puiCount = 2;
			}
			else
			{
				*puiCount = 0;
			}
			*pbTimedOut = FALSE;
		}
		else
		{
			*pbTimedOut = TRUE;
			rc = FERR_OK;
		}
		goto Exit;
	}
	
	*puiCount = 0;
	*pbTimedOut = (FLMBOOL)((!uiTimeLimit)
									? TRUE
									: FALSE);
	
	// Read the records associated with the two DRNs, and construct index keys
	// from them to determine the directional relationship between them.
	
	pDb = pCursor->pDb;
	if (RC_BAD( rc = flmCurDbInit( pCursor)))
	{
		goto Exit;
	}
	pTempPool = &pDb->TempPool;

	// Get the records corresponding to the two DRNs.
			
	rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer, uiDRN1,
					FALSE, NULL, NULL, &pRec1);
	if (rc == FERR_NOT_FOUND)
	{
		if (RC_BAD( rc = fdictGetContainer( pDb->pDict,
									pCursor->uiContainer, &pLFile)))
		{
			goto Exit;
		}

		if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDRN1,
						&pRec1, NULL, NULL)))
		{
			goto Exit;
		}
	}
	else if (RC_BAD( rc))
	{
		goto Exit;
	}

	// Retrieve the 2nd record.

	rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer, uiDRN2,
		FALSE, NULL, NULL, &pRec2);
	if (rc == FERR_NOT_FOUND)
	{
		if (RC_BAD( rc = fdictGetContainer( pDb->pDict,
									pCursor->uiContainer, &pLFile)))
		{
			goto Exit;
		}

		if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDRN2,
									&pRec2, NULL, NULL)))
		{
			goto Exit;
		}
	}
	else if (RC_BAD( rc))
	{
		goto Exit;
	}

	// At this point, both DRNs have been found.  Now generate keys from each
	// of the records and perform the following actions:
	// 1) Verify that only one key exists in each record.  If there is more
	//		than one key, no clear comparison can be made.
	// 2)  Compare the two keys and set *piCmpResult as follows:
	//		  <0		First Key < Second Key
	//			0		First Key = Second Key
	//		  >0		First Key > Second Key

	// If necessary, optimize the query here. NOTE: this should be done only if
	// FLAIM is to choose an index for the query.

	if (!pCursor->bOptimized && pCursor->uiIndexNum == FLM_SELECT_INDEX)
	{
		if (RC_BAD( rc = flmCurPrep( pCursor)))
		{
			goto Exit;
		}
	}

	if (pCursor->bOptimized)
	{

		// If there are multiple subqueries, that means there are
		// multiple indexes.  Set uiIndexNum to zero in this
		// case so that we will return FERR_NOT_IMPLEMENTED
		// below.

		if (!pCursor->pSubQueryList ||
			 pCursor->pSubQueryList->pNext ||
			 pCursor->pSubQueryList->OptInfo.eOptType != QOPT_USING_INDEX)
		{
			uiIndexNum = 0;
		}
		else
		{
			uiIndexNum = pCursor->pSubQueryList->OptInfo.uiIxNum;
		}
	}
	else
	{
		uiIndexNum = pCursor->uiIndexNum;
	}

	// Index number of zero means that the user either did not set an
	// index, or no index was selected, or multiple indexes were
	// selected - all of which disqualify this query from being able
	// to compare to DRNs.

	if (!uiIndexNum)
	{
		rc = RC_SET( FERR_NOT_IMPLEMENTED);
		goto Exit;
	}

	if( RC_BAD( rc = fdictGetIndex(
				pDb->pDict, pDb->pFile->bInLimitedMode,
				uiIndexNum, NULL, &pIxd)))
	{
		goto Exit;
	}

	pucKey1 = NULL;
	if (RC_BAD( rc = flmCurMakeKeyFromRec(	pDb, pIxd, pTempPool,
											pRec1, &pucKey1, &uiKey1Len)))
	{
		goto Exit;
	}

	pucKey2 = NULL;
	if (RC_BAD( rc = flmCurMakeKeyFromRec(	pDb, pIxd, pTempPool,
											pRec2, &pucKey2, &uiKey2Len)))
	{
		goto Exit;
	}

	if (uiKey1Len > uiKey2Len)
	{
		if ((iCmp = f_memcmp( pucKey1, pucKey2, uiKey2Len)) == 0)
		{
			iCmp = 1;
		}
		else
		{
			iCmp = ( (iCmp > 0) ? 1 : -1);
		}
	}
	else if (uiKey1Len < uiKey2Len)
	{
		if ((iCmp = f_memcmp( pucKey1, pucKey2, uiKey1Len)) == 0)
		{
			iCmp = -1;
		}
		else
		{
			iCmp = ( (iCmp > 0) ? 1 : -1);
		}
	}
	else
	{
		if ((iCmp = f_memcmp( pucKey1, pucKey2, uiKey2Len)) != 0)
		{
			iCmp = ( (iCmp > 0) ? 1 : -1);
		}
		else
		{

			// Keys are equal, compare DRNs.

			iCmp = (FLMINT)((uiDRN1 > uiDRN2)
								 ? (FLMINT)-1
									: (FLMINT)((uiDRN1 < uiDRN2)
												  ? (FLMINT)1
												  : (FLMINT)0));
		}
	}
	*piCmpResult = iCmp;

	// If time limit is zero, don't need to do the count.

	if (!uiTimeLimit)
	{
		goto Exit;
	}

	// If keys are equal, simply return a count of two.

	if (!iCmp)
	{
		*puiCount = 2;
		goto Exit;
	}

	// Optimize the subqueries as necessary

	if (!pCursor->bOptimized)
	{
		if (RC_BAD( rc = flmCurPrep( pCursor)))
		{
			goto Exit;
		}
	}

	// Create a new temporary index cursor.

	if ((pTmpFSIndexCursor = f_new FSIndexCursor) == NULL)
	{
		rc = RC_SET( FERR_MEM);
		goto Exit;
	}

	// Count the entries between the two keys/references.

	if (iCmp > 0)
	{
		if (RC_BAD( rc = pTmpFSIndexCursor->setupKeys( pDb, pIxd,
									pucKey1, uiKey1Len, uiDRN1,
									pucKey2, uiKey2Len, uiDRN2, FALSE)))
		{
			goto Exit;
		}
	}
	else
	{
		if (RC_BAD( rc = pTmpFSIndexCursor->setupKeys( pDb, pIxd,
									pucKey1, uiKey1Len, uiDRN1,
									pucKey2, uiKey2Len, uiDRN2, FALSE)))
		{
			goto Exit;
		}
	}

	// Intersect the temporary index cursor's key range with the
	// cursor's key range(es).

	pSaveFSIndexCursor = pCursor->pSubQueryList->pFSIndexCursor;
	uiSaveTimeLimit = pCursor->uiTimeLimit;
	if (RC_BAD( rc = pTmpFSIndexCursor->intersectKeys( pDb, pSaveFSIndexCursor)))
	{
		goto Exit;
	}

	// Set the query's index cursor to the temporary index cursor and count
	// the entries between them.

	if (uiTimeLimit == FLM_NO_LIMIT)
	{
		pCursor->uiTimeLimit = 0;
	}
	else
	{
		pCursor->uiTimeLimit = FLM_SECS_TO_TIMER_UNITS( uiTimeLimit);
	}

	// Perform the count operation.

	pCursor->pSubQueryList->pFSIndexCursor = pTmpFSIndexCursor;
	if (RC_BAD( rc = flmCurSearch( FLM_CURSOR_REC_COUNT, pCursor, TRUE,
								TRUE, puiCount, NULL, NULL, NULL)))
	{
		if (rc == FERR_EOF_HIT)
		{
			rc = FERR_OK;
		}
		else if (rc == FERR_TIMEOUT)
		{
			rc = FERR_OK;
			*pbTimedOut = TRUE;
		}
		goto Exit;
	}
Exit:

	if (pRec1)
	{
		pRec1->Release();
	}

	if (pRec2)
	{
		pRec2->Release();
	}

	if (pTmpFSIndexCursor)
	{
		pTmpFSIndexCursor->Release();
		pTmpFSIndexCursor = NULL;
	}

	// Restore saved index cursor if necessary.

	if (pSaveFSIndexCursor)
	{
		pCursor->pSubQueryList->pFSIndexCursor = pSaveFSIndexCursor;
		pCursor->uiTimeLimit = uiSaveTimeLimit;
	}
	flmExit( FLM_CURSOR_COMPARE_DRNS, pDb, rc);

	return( pCursor->rc = rc);
}

/****************************************************************************
Desc:	Does FlmCursorTestRec and FlmCursorTestDRN over the client/server
		line.
****************************************************************************/
FSTATIC RCODE flmCurCSTestRec(
	CURSOR *		pCursor,
	FLMUINT		uiDrn,
	FlmRecord *	pTestRec,
	FLMBOOL *	pbIsMatchRV
	)
{
	RCODE				rc = FERR_OK;
	CS_CONTEXT *	pCSContext = pCursor->pCSContext;
	FCL_WIRE			Wire( pCSContext);

	// If there is no VALID id for the cursor, get one.

	if (pCursor->uiCursorId == FCS_INVALID_ID)
	{
		if (RC_BAD( rc = flmInitCurCS( pCursor)))
		{
			goto Exit;
		}
	}

	// Send a request to test the record or DRN.

	if (RC_BAD( rc = Wire.sendOp(
		FCS_OPCLASS_ITERATOR, FCS_OP_ITERATOR_TEST_REC)))
	{
		goto Exit;
	}

	if (RC_BAD( rc = Wire.sendNumber(
		WIRE_VALUE_ITERATOR_ID, pCursor->uiCursorId)))
	{
		goto Transmission_Error;
	}

	if (pTestRec)
	{
		if (RC_BAD( rc = Wire.sendRecord( WIRE_VALUE_RECORD, pTestRec)))
		{
			goto Transmission_Error;
		}
	}
	else
	{
		if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_DRN, uiDrn)))
		{
			goto Transmission_Error;
		}
	}

	if (RC_BAD( rc = Wire.sendTerminate()))
	{
		goto Transmission_Error;
	}

	// Read the response.

	if (RC_BAD( rc = Wire.read()))
	{
		goto Transmission_Error;
	}

	*pbIsMatchRV = Wire.getBoolean();
	rc = Wire.getRCode();

Exit:

	return( rc);

Transmission_Error:

	pCSContext->bConnectionGood = FALSE;
	goto Exit;
}


/****************************************************************************
Desc:	Checks a record that has been retrieved from the database to see if
		satisfies the cursor selection criteria.
		IMPORTANT NOTE: pRec's containerID better be set to the container it
		came from, because flmCurEvalCriteria verifies that the record's
		container number matches the cursor's container number.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmCursorTestRec(
	HFCURSOR			hCursor,
	FlmRecord * 	pRec,
	FLMBOOL * 		pbIsMatch)
{
	RCODE				rc = FERR_OK;
	FDB *				pDb = NULL;
	CURSOR *			pCursor = (CURSOR *)hCursor;
	SUBQUERY *		pSubQuery;

	flmAssert( pCursor != NULL);
	*pbIsMatch = FALSE;

	if (pCursor->pCSContext)
	{
		rc = flmCurCSTestRec( pCursor, 0, pRec, pbIsMatch);
		goto Exit2;
	}

	// Make sure that we don't have partially finished
	// query criteria.

	if (pCursor->QTInfo.uiNestLvl ||
		 ((pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND) &&
			 pCursor->QTInfo.pTopNode))
	{
		rc = RC_SET( FERR_CURSOR_SYNTAX);
		goto Exit;
	}

	// Optimize the subqueries as necessary

	if (!pCursor->bOptimized)
	{
		if (RC_BAD( rc = flmCurPrep( pCursor)))
		{
			goto Exit;
		}
	}

	flmAssert( pCursor->pDb != NULL);

	pDb = pCursor->pDb;
	if (RC_BAD( rc = flmCurDbInit( pCursor)))
	{
		goto Exit;
	}

	// Evaluate the record against all sub-queries until
	// we find a TRUE.
	// IMPORTANT NOTE: pRec's containerID better be set to the container it
	// came from, because flmCurEvalCriteria verifies that the record's
	// container number matches the cursor's container number.

	flmAssert( pRec->getContainerID() != 0);

	pSubQuery = pCursor->pSubQueryList;
	while (pSubQuery)
	{
		FLMUINT	uiResult;

		if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery,
									pRec, FALSE, &uiResult)))
		{
			goto Exit;
		}
		if (uiResult == FLM_TRUE)
		{
			*pbIsMatch = TRUE;
			break;
		}
		pSubQuery = pSubQuery->pNext;
	}

Exit:

	if (pDb)
	{
		fdbExit( pDb);
	}

Exit2:

	return( rc);
}

/****************************************************************************
Desc:		Retrieves the record identified by the passed-in DRN and checks it
			to see if it satisfies the cursor selection criteria.
Notes: 	This function is designed for use with cursors having only one
		 	associated source.  Multiple sources are not supported.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmCursorTestDRN(
	HFCURSOR			hCursor,
	FLMUINT 			uiDrn,
	FLMBOOL * 		pbIsMatch)
{
	RCODE				rc = FERR_OK;
	FDB *				pDb = NULL;
	CURSOR *			pCursor = (CURSOR *)hCursor;
	SUBQUERY *		pSubQuery;
	FlmRecord *		pRec = NULL;

	flmAssert( pCursor != NULL);
	*pbIsMatch = FALSE;
	if (pCursor->pCSContext)
	{
		rc = flmCurCSTestRec( pCursor, uiDrn, NULL, pbIsMatch);
		goto Exit2;
	}

	flmAssert( pCursor->pDb != NULL);
	if (RC_OK( rc = FlmRecordRetrieve( (HFDB)pCursor->pDb,
							pCursor->uiContainer,
							uiDrn, FO_EXACT, &pRec, NULL)))
	{

		// Optimize the subqueries as necessary

		if (!pCursor->bOptimized)
		{
			if (RC_BAD( rc = flmCurPrep( pCursor)))
			{
				goto Exit;
			}
		}

		pDb = pCursor->pDb;
		if (RC_BAD(rc = flmCurDbInit( pCursor)))
		{
			goto Exit;
		}

		// Evaluate the record against all sub-queries until
		// we find a TRUE.

		pSubQuery = pCursor->pSubQueryList;
		while (pSubQuery)
		{
			FLMUINT	uiResult;

			if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery,
										pRec, FALSE, &uiResult)))
			{
				goto Exit;
			}
			if (uiResult == FLM_TRUE)
			{
				*pbIsMatch = TRUE;
				break;
			}
			pSubQuery = pSubQuery->pNext;
		}

	}

Exit:

	if (pDb)
	{
		fdbExit( pDb);
	}

	if (pRec)
	{
		pRec->Release();
	}

Exit2:
	return( rc);
}
