/****************************************************************************
Desc : Return the status of the index.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmIndexStatus(
	HFDB					hDb,
	FLMUINT				uiIndexNum,
	FINDEX_STATUS *	pIndexStatus)
{
	RCODE					rc = FERR_OK;
	FLMBOOL				bStartedAutoTrans = FALSE;
	FLMUINT 				uiLastDrnIndexed;
	FDB *					pDb = (FDB *)hDb;
	F_BKGND_IX *		pBackgroundIx;
	FLMBOOL				bSuspended;
	FLMBOOL				bMutexLocked = FALSE;

	flmAssert( pIndexStatus != NULL);

	if( IsInCSMode( hDb))
	{
		fdbInitCS( pDb);
		rc = flmIndexStatusCS( pDb, uiIndexNum, pIndexStatus);
		goto Exit;
	}

	if( RC_BAD( rc = fdbInit( (FDB *)hDb, FLM_READ_TRANS,
							FDB_TRANS_GOING_OK, 0, &bStartedAutoTrans)))
	{
		goto Exit;
	}

	f_mutexLock( gv_FlmSysData.hShareMutex);
	bMutexLocked = TRUE;

	pBackgroundIx = flmBackgroundIndexGet( pDb->pFile, uiIndexNum, TRUE);
	if( pBackgroundIx)
	{
		f_memcpy( pIndexStatus, &pBackgroundIx->indexStatus, 
			sizeof( FINDEX_STATUS));
		f_mutexUnlock( gv_FlmSysData.hShareMutex);
		bMutexLocked = FALSE;
		flmAssert( pIndexStatus->uiIndexNum == uiIndexNum);
	}
	else
	{
		IXD *		pIxd;
		FLMBOOL	bTrackerIxSuspended;

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

		bSuspended = (pIxd->uiFlags & IXD_SUSPENDED)
												? TRUE
												: FALSE;

		f_mutexUnlock( gv_FlmSysData.hShareMutex);
		bMutexLocked = FALSE;

		// Get the index state from the tracker

		if( RC_BAD( rc = flmGetIxTrackerInfo( pDb, uiIndexNum, 
			NULL, &uiLastDrnIndexed, NULL, &bTrackerIxSuspended)))
		{
			if( rc == FERR_NOT_FOUND)
			{
				rc = RC_SET( FERR_BAD_IX);
			}
			goto Exit;
		}

		// Sanity check

#ifdef FLM_DEBUG
		if( pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_51 &&
			bSuspended != bTrackerIxSuspended)
		{
			flmAssert( 0);
		}
#endif

		// Populate the index status structure.

		f_memset( pIndexStatus, 0, sizeof( FINDEX_STATUS));
		pIndexStatus->uiIndexNum = uiIndexNum;
		pIndexStatus->uiLastRecordIdIndexed = uiLastDrnIndexed;
		pIndexStatus->bSuspended = bSuspended;
	}

Exit:

	if( bMutexLocked)
	{
		f_mutexUnlock( gv_FlmSysData.hShareMutex);
	}

	if( bStartedAutoTrans)
	{
		rc = flmEndAutoTrans( pDb, rc);
	}
	flmExit( FLM_INDEX_STATUS, pDb, rc);
	return( rc);
}
/****************************************************************************
Desc : If the index was suspended, restart the background process that
		 will get the index up to date so that it will eventually be online.
		 Returns FERR_OK with no change if the index is already online.
Notes: An update transaction will be started if necessary.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmIndexResume(
	HFDB			hDb,
	FLMUINT		uiIndexNum)
{
	RCODE				rc = FERR_OK;
	FDB *				pDb = (FDB *)hDb;
	IXD *				pIxd;
	FLMUINT 			uiLastContainerIndexed;
	FLMUINT 			uiLastDrnIndexed;
	FLMUINT 			uiOnlineTransId;
	FLMBOOL			bStartedTrans = FALSE;

	if( IsInCSMode( hDb))
	{
		fdbInitCS( pDb);

		CS_CONTEXT *		pCSContext = pDb->pCSContext;
		FCL_WIRE				Wire( pCSContext, pDb);

		if( !pCSContext->bConnectionGood)
		{
			rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
			goto Transmission_Error;
		}

		if( RC_BAD( rc = Wire.sendOp(
			FCS_OPCLASS_INDEX, FCS_OP_INDEX_RESUME)))
		{
			goto Exit;
		}

		if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, uiIndexNum)))
		{
			goto Transmission_Error;
		}

		// Send the "auto-online" flag (only needed for 
		// backwards compatibility)
		
		if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_BOOLEAN, 1)))
		{
			goto Transmission_Error;
		}

		// Send a priority of high (only needed for
		// backwards compatibility)

		if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER1, 1)))
		{
			goto Transmission_Error;
		}

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

		// Read the response.
	
		if (RC_BAD( rc = Wire.read()))
		{
			goto Transmission_Error;
		}

		if( RC_BAD( rc = Wire.getRCode()))
		{
			goto Exit;
		}

		goto Exit;

Transmission_Error:
		pCSContext->bConnectionGood = FALSE;
		goto Exit;
	}

	if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
		FDB_TRANS_GOING_OK, FLM_AUTO_TRANS | FLM_NO_TIMEOUT, &bStartedTrans)))
	{
		goto Exit;
	}

	// See if the index is valid

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

	if( pIxd->uiFlags & IXD_UNIQUE)
	{
		// Can't suspend or resume unique indexes

		flmAssert( !(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE)));
		rc = RC_SET( FERR_ILLEGAL_OP);
		goto Exit;
	}

	if( !(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE)))
	{
		// Index is already on-line

		goto Exit;
	}

	// If we're in limited mode and this is an encrypted index,
	// it can't be resumed
	if (pDb->pFile->bInLimitedMode && pIxd->uiEncId)
	{
		rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
		goto Exit;
	}

	if( !(pIxd->uiFlags & IXD_SUSPENDED))
	{
		// Index is not suspended.  It is offline (see test
		// above), but a thread should already be building the 
		// index, or it better be in the start list.

#ifdef FLM_DEBUG
		if (flmBackgroundIndexGet( pDb->pFile, 
				uiIndexNum, FALSE) == NULL)
		{
			F_BKGND_IX *	pBackgroundIx;

			for( pBackgroundIx = pDb->pIxStartList;
					pBackgroundIx;
					pBackgroundIx = pBackgroundIx->pNext)
			{
				if( pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum)
				{
					break;
				}
			}
			flmAssert( pBackgroundIx);
		}
#endif

		goto Exit;
	}

	// Better not have a background thread running, or it better be
	// in the stop list - because its state shows suspended.

#ifdef FLM_DEBUG
	if (flmBackgroundIndexGet( pDb->pFile, uiIndexNum, FALSE) != NULL)
	{
		F_BKGND_IX *	pBackgroundIx;

		for( pBackgroundIx = pDb->pIxStopList;
				pBackgroundIx;
				pBackgroundIx = pBackgroundIx->pNext)
		{
			if( pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum)
			{
				break;
			}
		}
		flmAssert( pBackgroundIx);
	}
#endif

	// Get the tracker info

	if( RC_BAD( rc = flmGetIxTrackerInfo( pDb, uiIndexNum, 
		&uiLastContainerIndexed, &uiLastDrnIndexed, &uiOnlineTransId,
		NULL)))
	{
		goto Exit;
	}

	// Update the tracker info so that the index state will
	// be changed to "unsuspended."

	if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, uiIndexNum, 
		uiLastContainerIndexed, uiLastDrnIndexed, 
		uiOnlineTransId, FALSE)))
	{
		goto Exit;
	}

	// Add an entry to the start list so that an indexing thread
	// will be started when this transaction commits.

	if( !(pDb->uiFlags & FDB_REPLAYING_RFL))
	{
		if( RC_BAD( rc = flmAddToStartList( pDb, uiIndexNum)))
		{
			goto Exit;
		}
	}

	// Create a new dictionary.

	if( !(pDb->uiFlags & FDB_UPDATED_DICTIONARY))
	{
		if( RC_BAD( rc = fdictCloneDict( pDb)))
		{
			goto Exit;
		}

		// Get a pointer to the new IXD

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

	// Update the IXDs flags so that the current update
	// transaction will see the correct state of the index.
	// Old read transactions will continue to use a prior
	// version of the dictionary.

	pIxd->uiFlags &= ~IXD_SUSPENDED;
	pIxd->uiFlags |= IXD_OFFLINE;

	// Log the resume packet to the RFL

	if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSuspendOrResume( 
		uiIndexNum, RFL_INDEX_RESUME_PACKET)))
	{
		goto Exit;
	}

Exit:

	if( bStartedTrans)
	{
		rc = flmEndAutoTrans( pDb, rc);
	}

	flmExit( FLM_INDEX_RESUME, pDb, rc);
	return( rc);
}
/****************************************************************************
Desc : Suspend the selected index from doing any key updates on records
		 that are equal or higher than the next record ID value
		 in the container that the index references.  If the index is offline
		 then the background process will be suspended.  If the index is
		 online then it will be suspended.  If the index is already 
		 suspended FERR_OK will be returned.  A suspended index is not
		 persistant if the database goes down.  
Notes: An update transaction will be started if necessary.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmIndexSuspend(
	HFDB			hDb,
	FLMUINT		uiIndexNum)
{
	RCODE			rc = FERR_OK;
	FDB *			pDb = (FDB *)hDb;
	IXD *			pIxd;
	FLMUINT		uiHighestRecId;
	FLMUINT		uiContainerNum;
	FLMBOOL		bSuspended;
	FLMBOOL		bStartedTrans = FALSE;
	LFILE *		pLFile;

	if( IsInCSMode( hDb))
	{
		fdbInitCS( pDb);

		CS_CONTEXT *		pCSContext = pDb->pCSContext;
		FCL_WIRE				Wire( pCSContext, pDb);

		if( !pCSContext->bConnectionGood)
		{
			rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
			goto Transmission_Error;
		}

		if( RC_BAD( rc = Wire.sendOp(
			FCS_OPCLASS_INDEX, FCS_OP_INDEX_SUSPEND)))
		{
			goto Exit;
		}

		if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, uiIndexNum)))
		{
			goto Transmission_Error;
		}

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

		// Read the response
	
		if (RC_BAD( rc = Wire.read()))
		{
			goto Transmission_Error;
		}

		if( RC_BAD( rc = Wire.getRCode()))
		{
			goto Exit;
		}

		goto Exit;

Transmission_Error:
		pCSContext->bConnectionGood = FALSE;
		goto Exit;
	}

	if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
		FDB_TRANS_GOING_OK, FLM_AUTO_TRANS | FLM_NO_TIMEOUT, &bStartedTrans)))
	{
		goto Exit;
	}

	// See if the index is valid

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

	if( pIxd->uiFlags & IXD_UNIQUE)
	{
		// Can't suspend unique indexes
		rc = RC_SET( FERR_ILLEGAL_OP);
		goto Exit;
	}

	if( pIxd->uiFlags & IXD_SUSPENDED)
	{
		// Index is already suspended.
		goto Exit;
	}

	// Get the current index info from the tracker

	if( RC_BAD( rc = flmGetIxTrackerInfo( pDb,
		uiIndexNum, &uiContainerNum, &uiHighestRecId, NULL, &bSuspended)))
	{
		goto Exit;
	}
	flmAssert( !bSuspended);

	// Get information about the container(s) being indexed

	if( !(pIxd->uiFlags & IXD_OFFLINE))
	{
		if ((uiContainerNum = pIxd->uiContainerNum) == 0)
		{
			// The index was on-line and up-to-date.  For an index that
			// crosses all containers, we will suspend on the highest DRN of
			// the FLM_DATA_CONTAINER.

			uiContainerNum = FLM_DATA_CONTAINER;
		}

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

		uiHighestRecId = 0;
		if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiHighestRecId)))
		{
			goto Exit;
		}

		// Decrement uiHighestRecId by 1 to correctly reflect the
		// last record that was indexed.

		flmAssert( uiHighestRecId != 0);
		uiHighestRecId--;
	}

	// There may be a background thread still assigned to the
	// index even though the index may be "on-line."  This is because
	// the background thread may have just commited a transaction that
	// transitioned the index from off-line to on-line, but the thread
	// has not yet exited (even though it will not do any more work
	// to update the index).  We want to wait for the thread to terminate
	// before our transaction is allowed to commit.  This is so that if
	// we immediately call resume, it won't find the yet-to-terminate
	// thread still running in the background.

	if( !(pDb->uiFlags & FDB_REPLAYING_RFL))
	{
		if( RC_BAD( rc = flmAddToStopList( pDb, uiIndexNum)))
		{
			goto Exit;
		}
	}

	flmAssert( uiContainerNum != 0xFFFFFFFF);

	if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, 
		uiIndexNum, uiContainerNum, uiHighestRecId, 
		TRANS_ID_OFFLINE, TRUE)))
	{
		goto Exit;
	}

	// Create a new dictionary

	if( !(pDb->uiFlags & FDB_UPDATED_DICTIONARY))
	{
		if( RC_BAD( rc = fdictCloneDict( pDb)))
		{
			goto Exit;
		}

		// Get a pointer to the new IXD

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

	// Update the IXDs flags so that the current update
	// transaction will see the correct state of the index.
	// Old read transactions will continue to use a prior
	// version of the dictionary.

	pIxd->uiFlags |= (IXD_SUSPENDED | IXD_OFFLINE);

	// Log the suspend packet to the RFL

	if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSuspendOrResume( 
		uiIndexNum, RFL_INDEX_SUSPEND_PACKET)))
	{
		goto Exit;
	}

Exit:

	if( bStartedTrans)
	{
		rc = flmEndAutoTrans( pDb, rc);
	}

	flmExit( FLM_INDEX_SUSPEND, pDb, rc);
	return( rc);
}
/****************************************************************************
Desc:		Thread that will build an index in the background.
			Caller will create a pDb to use.  This pDb must be
			freed at the conclusion of the routine.
****************************************************************************/
FSTATIC RCODE FLMAPI flmBackgroundIndexBuildThrd(
	IF_Thread *			pThread)
{
	RCODE					rc = FERR_OK;
	IXD *					pIxd;
	F_BKGND_IX * 		pBackgroundIx = (F_BKGND_IX *)pThread->getParm1();
	FLMBOOL				bStartedTrans;
	FLMBOOL				bDbInitialized;
	FLMUINT				uiContainerNum;
	FLMUINT				uiFirstDrn;
	FLMUINT				uiIndexNum;
	FDB *					pDb = NULL;
	FLMBOOL				bForcedShutdown = FALSE;
	FLMBOOL				bHitEnd;
	FINDEX_STATUS		savedIxStatus;
	FlmRecord *			pReusableRec = NULL;
	char					szMsg[ 128];
	FLMINT				iErrorLine = 0;
	FLMBOOL				bLimitedMode = FALSE;

	pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING);

	if( (pReusableRec = f_new FlmRecord) != NULL)
	{
		if( RC_BAD( pReusableRec->preallocSpace( 512, 1024 * 64)))
		{
			pReusableRec->Release();
			pReusableRec = NULL;
		}
	}

Loop_Again:

	rc = FERR_OK;
	uiIndexNum = pBackgroundIx->indexStatus.uiIndexNum;
	flmAssert( pThread->getThreadAppId() == uiIndexNum);
	bDbInitialized = FALSE;
	bStartedTrans = FALSE;
	pDb = NULL;

	// We could loop forever on flmOpenFile errors, check if we should exit.

	if( pThread->getShutdownFlag())
	{
		bForcedShutdown = TRUE;
		goto Exit;
	}

	if( RC_BAD( rc = flmOpenFile( pBackgroundIx->pFile,
		NULL, NULL, NULL, 0, TRUE, NULL, NULL,
		pBackgroundIx->pFile->pszDbPassword, &pDb)))
	{

		// If the file is being closed, this is not an error.

		if( pBackgroundIx->pFile->uiFlags & DBF_BEING_CLOSED)
		{
			bForcedShutdown = TRUE;
			rc = FERR_OK;
		}
		else
		{
			iErrorLine = (FLMINT)__LINE__;
		}
		goto Exit;
	}

	flmAssert( pDb->pSFileHdl);

	bDbInitialized = TRUE;
	if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, 0, 0, &bStartedTrans)))
	{
		iErrorLine = (FLMINT)__LINE__;
		goto Exit;
	}
	flmAssert( !bStartedTrans);
	pDb->uiFlags |= FDB_BACKGROUND_INDEXING;

	for(;;)
	{
		// Set the thread's status

		pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);

		// See if we should shut down. 

		if( pThread->getShutdownFlag())
		{
			bForcedShutdown = TRUE;
			break;
		}

		// Obtain the file lock

		flmAssert( !(pDb->uiFlags & FDB_HAS_FILE_LOCK));
		if( RC_BAD( rc = pDb->pFile->pFileLockObj->lock( pDb->hWaitSem,  
			TRUE, FLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY,
			pDb->pDbStats ? &pDb->pDbStats->LockStats : NULL)))
		{
			if( rc == FERR_IO_FILE_LOCK_ERR)
			{
				// This would only happen if we were signaled to shut down.
				// So, it's ok to exit

				flmAssert( pThread->getShutdownFlag());
				bForcedShutdown = TRUE;
				rc = FERR_OK;
			}
			else
			{
				iErrorLine = (FLMINT)__LINE__;
			}
			goto Exit;
		}

		// The lock needs to be marked as implicit so that flmCommitDbTrans
		// will unlock the file and allow the next update transaction to
		// begin before all writes are complete.

		pDb->uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT);

		// If there are higher priority waiters in the lock queue,
		// or we are being told to shut down, we want to relinquish.

		if( pThread->getShutdownFlag() ||
			pDb->pFile->pFileLockObj->haveHigherPriorityWaiter( 
				FLM_BACKGROUND_LOCK_PRIORITY))
		{
			if (RC_BAD( rc = pDb->pFile->pFileLockObj->unlock()))
			{
				iErrorLine = (FLMINT)__LINE__;
				goto Exit;
			}

			pDb->uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT);
			continue;
		}

		// Start an update transaction 

		if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT, 
			FLM_DONT_POISON_CACHE)))
		{
			if( rc == FERR_IO_FILE_LOCK_ERR)
			{
				// This would only happen if we were signaled to shut down.
				// So, it's ok to exit

				flmAssert( pThread->getShutdownFlag());
				bForcedShutdown = TRUE;
				rc = FERR_OK;
			}
			else
			{
				iErrorLine = (FLMINT)__LINE__;
			}
			goto Exit;
		}
		bStartedTrans = TRUE;

		if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode,
			uiIndexNum, NULL, &pIxd, TRUE)))
		{
			// Index may have been deleted by another transaction, or
			// there may have been some other error.

			iErrorLine = (FLMINT)__LINE__;
			goto Exit;
		}

		// If we're running in limited mode, then we can't mess with encrypted
		// indexes.  On the other hand, since the index is marked as offline,
		// but not suspended, this thread has to exist, or else it will cause
		// all kinds of problems elsewhere.  So, in such a case, we will simply
		// sleep in an inifinite loop until the shutdown flag is set.
		// (We consider this acceptable becase running in limited mode is not
		// the norm, and Flaim probably won't be up for very long in this mode.)

		if( pDb->pFile->bInLimitedMode && pIxd->uiEncId)
		{
			bLimitedMode = TRUE;
			goto Exit;
		}

		// Look up the tracker info to determine where we need to being indexing

		if (RC_BAD( rc = flmGetIxTrackerInfo( pDb,
			pBackgroundIx->indexStatus.uiIndexNum, &uiContainerNum,
			&uiFirstDrn, NULL, &pBackgroundIx->indexStatus.bSuspended)))
		{
			iErrorLine = (FLMINT)__LINE__;
			goto Exit;
		}

		// If the index is suspended, this thread should have been
		// shut down.  The suspending thread will keep the database
		// locked until we exit.  So, if we have the database locked,
		// the index better not be suspended.

		flmAssert( !pBackgroundIx->indexStatus.bSuspended &&
			!(pIxd->uiFlags & IXD_SUSPENDED));

		if (pIxd->uiContainerNum)
		{
			uiContainerNum = pIxd->uiContainerNum;
			if( uiFirstDrn == DRN_LAST_MARKER)
			{
				goto Exit;
			}
		}
		else
		{
			if( uiFirstDrn == DRN_LAST_MARKER && uiContainerNum == 0xFFFFFFFF)
			{
				goto Exit;
			}
			else
			{
				// The container number from the tracker record
				// may not be a real container.
				// Determine what the next actual container number is.

				if (uiContainerNum != FLM_DATA_CONTAINER)
				{
					while( uiContainerNum < pDb->pDict->uiIttCnt)
					{
						ITT *	pItt = &pDb->pDict->pIttTbl [uiContainerNum];
						if (ITT_IS_CONTAINER( pItt))
						{
							break;
						}
						else
						{
							uiContainerNum++;
						}
					}

					if (uiContainerNum >= pDb->pDict->uiIttCnt)
					{
						uiContainerNum = FLM_DATA_CONTAINER;
					}
				}
			}
		}

		// Setup the DRN range we want to index.

		uiFirstDrn++;
		flmAssert( pIxd->uiLastDrnIndexed == uiFirstDrn - 1);

		// Set the thread's status

		pThread->setThreadStatus( "Indexing %u:%u", 
			(unsigned)uiContainerNum, (unsigned)uiFirstDrn);

		// Read and index up to the highest drn (or record higher than uiEndDrn)
		// or until time runs out.  The 500 is millisecs to take for the transaction.

		f_memcpy( &savedIxStatus, 
			&pBackgroundIx->indexStatus, sizeof( FINDEX_STATUS));

		if( RC_BAD( rc = flmIndexSetOfRecords( pDb,
			uiIndexNum, uiContainerNum, uiFirstDrn, DRN_LAST_MARKER,
			NULL, NULL, NULL, NULL,
			&pBackgroundIx->indexStatus, &bHitEnd, pThread, pReusableRec)))
		{
			// Lock the mutex while copying the saved index status back to
			// the main index status so that someone requesting the index status
			// won't see the status while the memcpy is in progress.

			f_mutexLock( gv_FlmSysData.hShareMutex);
			f_memcpy( &pBackgroundIx->indexStatus, 
				&savedIxStatus, sizeof( FINDEX_STATUS));
			f_mutexUnlock( gv_FlmSysData.hShareMutex);
			iErrorLine = (FLMINT)__LINE__;
			goto Exit;
		}

		if( pBackgroundIx->indexStatus.uiRecordsProcessed - 
			savedIxStatus.uiRecordsProcessed)
		{
			if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSet( uiIndexNum,
						uiContainerNum, uiFirstDrn, 
						pBackgroundIx->indexStatus.uiLastRecordIdIndexed)))
			{
				iErrorLine = (FLMINT)__LINE__;
				goto Exit;
			}
		}

		// Commit the transaction (even if we didn't do any indexing work).

		if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE)))
		{
			iErrorLine = (FLMINT)__LINE__;
			goto Exit;
		}

		bStartedTrans = FALSE;
		pBackgroundIx->indexStatus.uiTransactions++;

		if( bHitEnd)
		{
			// flmIndexSetOfRecords brought the index on-line

			if( gv_FlmSysData.UpdateEvents.pEventCBList)
			{
				flmDoEventCallback( F_EVENT_UPDATES, 
						F_EVENT_INDEXING_COMPLETE, (void *)uiIndexNum, 
						(void *)0);
			}

			// Log a message

			flmLogIndexingProgress( uiIndexNum, 0);
			break;
		}
	}

Exit:

	pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);

	if( bStartedTrans)
	{
		(void)flmAbortDbTrans( pDb);
		bStartedTrans = FALSE;
	}

	if( pDb && pDb->uiFlags & FDB_HAS_FILE_LOCK)
	{
		(void)pDb->pFile->pFileLockObj->unlock();
		pDb->uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT);
	}

	if( bDbInitialized)
	{
		fdbExit( pDb);
		bDbInitialized = FALSE;
	}

	if( pDb)
	{
		(void)FlmDbClose( (HFDB *) &pDb);
	}

	if( RC_BAD(rc) && !bForcedShutdown)
	{
		if (rc == FERR_MEM || rc == FERR_IO_DISK_FULL ||
			rc == FERR_MUST_WAIT_CHECKPOINT)
		{
			// Log the error

			f_sprintf( (char *)szMsg,
				"Background indexing thread %u (index %u)",
				(unsigned)pThread->getThreadId(), (unsigned)uiIndexNum);
			flmLogError( rc, (char *)szMsg, __FILE__, iErrorLine);

			// Sleep a half second and try again.

			pThread->sleep( 500);
			goto Loop_Again;
		}
		else
		{
			f_sprintf( (char *)szMsg,
				"Background indexing thread %u (index %u) -- unrecoverable error.",
				(unsigned)pThread->getThreadId(), (unsigned)uiIndexNum);
			flmLogError( rc, (char *)szMsg, __FILE__, iErrorLine);
		}
	}

	if( pReusableRec)
	{
		flmAssert( pReusableRec->getRefCount() == 1);
		pReusableRec->Release();
	}

	if( bLimitedMode)
	{
		flmAssert( RC_OK( rc));

		for (;;)
		{
			if( pThread->getShutdownFlag())
			{
				break;
			}

			pThread->sleep( 1000);
		}
	}

	// Set the thread's app ID to 0, so that it will not
	// be found now that the thread is terminating (we don't
	// want flmBackgroundIndexGet to find the thread).

	pThread->setThreadAppId( 0);

	// Free the background index structure

	f_mutexLock( gv_FlmSysData.hShareMutex);
	f_free( &pBackgroundIx);
	pThread->setParm1( NULL);
	f_mutexUnlock( gv_FlmSysData.hShareMutex);

	return( rc);
}
/****************************************************************************
Desc:		Thread that will build an index in the background.
			Caller will create a pDb to use.  This pDb must be
			freed at the conclusion of the routine.
****************************************************************************/
RCODE flmStartIndexBuild(
	FDB *				pDb,
	FLMUINT			uiIndexNum)
{
	RCODE				rc = FERR_OK;
	FLMUINT			uiGMT;
	IXD *				pIxd;
	F_BKGND_IX *	pBackgroundIx = NULL;
	char				szThreadName[ F_PATH_MAX_SIZE];
	char				szBaseName[ F_FILENAME_SIZE];

	f_timeGetSeconds( &uiGMT );
	
	if( flmBackgroundIndexGet( pDb->pFile, uiIndexNum, FALSE) != NULL)
	{
		// There is already a background thread running on this index.

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

	// Allocate the background thread and index status strucutures.

	if( RC_BAD( rc = f_calloc(
		(FLMUINT)sizeof( F_BKGND_IX), &pBackgroundIx)))
	{
		goto Exit;
	}

	pBackgroundIx->pFile = pDb->pFile;
	pBackgroundIx->indexStatus.bSuspended = FALSE;
	pBackgroundIx->indexStatus.uiIndexNum = uiIndexNum;
	pBackgroundIx->indexStatus.uiStartTime = uiGMT;
	pBackgroundIx->indexStatus.uiLastRecordIdIndexed = pIxd->uiLastDrnIndexed;
	pBackgroundIx->indexStatus.uiKeysProcessed = 0;
	pBackgroundIx->indexStatus.uiRecordsProcessed = 0;
	pBackgroundIx->indexStatus.uiTransactions = 0;

	pBackgroundIx->uiIndexingAction = FTHREAD_ACTION_INDEX_OFFLINE;
	pBackgroundIx->pPrev = NULL;
	pBackgroundIx->pNext = NULL;

	// Generate the thread name

	if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( 
		pDb->pFile->pszDbPath, szThreadName, szBaseName)))
	{
		goto Exit;
	}

	f_sprintf( (char *)szThreadName, "BldIX %u (%s)", 
		(unsigned)uiIndexNum, szBaseName);

	// Start the thread in the background indexing thread group.
	// The new thread will cleanup pBackgroundIx on termination.

	if( RC_BAD( rc = f_threadCreate( NULL,
						flmBackgroundIndexBuildThrd, szThreadName,
						gv_uiBackIxThrdGroup, uiIndexNum,
						(void *)pBackgroundIx, NULL, 24000)))
	{
		goto Exit;
	}

Exit:

	if( RC_BAD( rc) && pBackgroundIx)
	{
		f_free( &pBackgroundIx);
	}

	return( rc);
}	
Exemple #6
0
/****************************************************************************
Desc:	This routine commits an active transaction for a particular
		database.  If the database is open via a server, a message is
		sent to the server to commit the transaction.  Otherwise, the
		transaction is committed locally.
****************************************************************************/
RCODE flmCommitDbTrans(
	FDB *				pDb,
	FLMUINT			uiNewLogicalEOF,
	FLMBOOL			bForceCheckpoint,
	FLMBOOL *		pbEmpty)
{
	RCODE	  			rc = FERR_OK;
	FLMBYTE *		pucUncommittedLogHdr;
	FFILE *			pFile = pDb->pFile;
	FLMUINT			uiCPFileNum = 0;
	FLMUINT			uiCPOffset = 0;
	FLMUINT			uiTransId = 0;
	FLMBOOL			bTransEndLogged;
	FLMBOOL			bForceCloseOnError = FALSE;
	FLMBOOL			bOkToLogAbort = TRUE;
	DB_STATS *		pDbStats = pDb->pDbStats;
	FLMUINT			uiTransType;
	FLMBOOL			bInvisibleTrans = FALSE;
	FLMBOOL			bIndexAfterCommit = FALSE;

	pDb->uiFlags |= FDB_COMMITTING_TRANS;

	// See if we even have a transaction going.

	if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS)
	{
		goto Exit;	// Will return FERR_OK.
	}

	// See if we have a transaction going which should be aborted.

	if (flmCheckBadTrans( pDb))
	{
		rc = RC_SET( FERR_ABORT_TRANS);
		goto Exit;
	}

	// If we are in a read transaction we can skip most of the stuff
	// below because no updates would have occurred.  This will help
	// improve performance.

	if (uiTransType == FLM_READ_TRANS)
	{

		if( pDb->KrefCntrl.bKrefSetup)
		{
			// KrefCntrlFree could be called w/o checking bKrefSetup because
			// it checks the flag, but it is more optimal to check the
			// flag before making the call because most of the time it will
			// be false.

			KrefCntrlFree( pDb);
		}
		goto Exit1;
	}

	// At this point, we know we have an update transaction.

	pFile->pRfl->clearLogHdrs();

#ifdef FLM_DBG_LOG
	flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID,
			0, 0, FERR_OK, "TCmit");
#endif
	uiTransId = pDb->LogHdr.uiCurrTransID;

	// If the transaction had no update operations, restore it
	// to its pre-transaction state - make it appear that no
	// transaction ever happened.

	if (!pDb->bHadUpdOper)
	{
		bOkToLogAbort = FALSE;
		rc = pFile->pRfl->logEndTransaction( RFL_TRNS_COMMIT_PACKET, TRUE);

		// Even though we didn't have any update operations, there may have
		// been operations during the transaction (i.e., query operations)
		// that initialized the KREF in order to generate keys.

		KrefCntrlFree( pDb);

		// Restore everything as if the transaction never happened.

		f_mutexLock( gv_FlmSysData.hShareMutex);
		pFile->uiUpdateTransID = 0;
		f_mutexUnlock( gv_FlmSysData.hShareMutex);
		if (pbEmpty)
		{
			*pbEmpty = TRUE;
		}
		goto Exit1;
	}

	// Log commit record to roll-forward log

	bOkToLogAbort = FALSE;
	if (RC_BAD( rc = pFile->pRfl->logEndTransaction(
								RFL_TRNS_COMMIT_PACKET, FALSE, &bTransEndLogged)))
	{
		goto Exit1;
	}
	bForceCloseOnError = TRUE;

	// Commit any keys in the KREF buffers.

	if (RC_BAD( rc = KYKeysCommit( pDb, TRUE)))
	{
		flmLogError( rc, "calling KYKeysCommit from flmCommitDbTrans");
		goto Exit1;
	}

	if (RC_BAD( rc = FSCommitIxCounts( pDb)))
	{
		flmLogError( rc, "calling FSCommitIxCounts from flmCommitDbTrans");
		goto Exit1;
	}

	// Reinitialize the log header.  If the local dictionary was updated
	// during the transaction, increment the local dictionary ID so that
	// other concurrent users will know that it has been modified and
	// that they need to re-read it into memory.

	// If we are in recovery mode, see if we need to force
	// a checkpoint with what we have so far.  We force a
	// checkpoint on one of two conditions:
	
	// 1. If it appears that we have a buildup of dirty cache
	//		blocks.  We force a checkpoint on this condition
	//		because it will be more efficient than replacing
	//		cache blocks one at a time.
	//		We check for this condition by looking to see if
	//		our LRU block is not used and it is dirty.  That is
	//		a pretty good indicator that we have a buildup
	//		of dirty cache blocks.
	// 2.	We are at the end of the roll-forward log.  We
	//		want to force a checkpoint here to complete the
	//		recovery phase.

	if ( pDb->uiFlags & FDB_REPLAYING_RFL)
	{
		// If we are in the middle of upgrading, and are forcing
		// a checkpoint, use the file number and offset that were
		// set in the FDB.

		if ((pDb->uiFlags & FDB_UPGRADING) && bForceCheckpoint)
		{
			uiCPFileNum = pDb->uiUpgradeCPFileNum;
			uiCPOffset = pDb->uiUpgradeCPOffset;
		}
		else
		{
			SCACHE *		pTmpSCache;
			F_Rfl *		pRfl = pFile->pRfl;

			f_mutexLock( gv_FlmSysData.hShareMutex);
			pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUCache;

			// Test for buildup of dirty cache blocks.

			if( (pTmpSCache && !pTmpSCache->uiUseCount &&
				  (pTmpSCache->ui16Flags & 
						(CA_DIRTY | CA_LOG_FOR_CP | CA_WRITE_TO_LOG))) ||
				pRfl->atEndOfLog() || bForceCheckpoint)
			{
				bForceCheckpoint = TRUE;
				uiCPFileNum = pRfl->getCurrFileNum();
				uiCPOffset = pRfl->getCurrReadOffset();
			}
			
			f_mutexUnlock( gv_FlmSysData.hShareMutex);
		}
	}

	// Move information collected in the pDb->LogHdr into the
	// uncommitted log header.  Other things that need to be
	// set have already been set in the uncommitted log header
	// at various places in the code.

	// Mutex does not have to be locked while we do this because
	// the update transaction is the only one that ever accesses
	// the uncommitted log header buffer.

	pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0];

	// Set the new logical EOF if passed in.

	if( uiNewLogicalEOF)
	{
		pDb->LogHdr.uiLogicalEOF = uiNewLogicalEOF;
	}
	
	UD2FBA( (FLMUINT32)pDb->LogHdr.uiLogicalEOF,
		&pucUncommittedLogHdr [LOG_LOGICAL_EOF]);

	// Increment the commit counter.

	flmIncrUint( &pucUncommittedLogHdr [LOG_COMMIT_COUNT], 1);

	// Set the last committed transaction ID

	if( (bTransEndLogged || (pDb->uiFlags & FDB_REPLAYING_COMMIT)) &&
		pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31)
	{
		UD2FBA( (FLMUINT32)uiTransId, 
			&pucUncommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]);
	}

	// Write the header

	pFile->pRfl->commitLogHdrs( pucUncommittedLogHdr,
							pFile->ucCheckpointLogHdr);

	// Commit any record cache.

	flmRcaCommitTrans( pDb);

	// Push the IXD_FIXUP values back into the IXD

	if (pDb->pIxdFixups)
	{
		IXD_FIXUP *	pIxdFixup;
		IXD_FIXUP *	pDeleteIxdFixup;
		IXD *			pIxd;

		pIxdFixup = pDb->pIxdFixups;
		while (pIxdFixup)
		{
			if( RC_BAD( fdictGetIndex(
				pDb->pDict, pDb->pFile->bInLimitedMode,
				pIxdFixup->uiIndexNum, NULL, &pIxd, TRUE)))
			{
				flmAssert( 0);
				pIxd = NULL;
			}

			if( pIxd)
			{
				pIxd->uiLastContainerIndexed = pIxdFixup->uiLastContainerIndexed;
				pIxd->uiLastDrnIndexed = pIxdFixup->uiLastDrnIndexed;
			}
			pDeleteIxdFixup = pIxdFixup;
			pIxdFixup = pIxdFixup->pNext;
			f_free( &pDeleteIxdFixup);
		}
		pDb->pIxdFixups = NULL;
	}

	// Set the update transaction ID back to zero only 
	// AFTER we know the transaction has safely committed.

	f_mutexLock( gv_FlmSysData.hShareMutex);
	f_memcpy( pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr,
					LOG_HEADER_SIZE);
	pFile->uiUpdateTransID = 0;
	ScaReleaseLogBlocks( pFile);
	if (pDb->uiFlags & FDB_UPDATED_DICTIONARY)
	{
		// Link the new local dictionary to its file.
		// Since the new local dictionary will be linked at the head
		// of the list of FDICT structures, see if the FDICT currently
		// at the head of the list is unused and can be unlinked.

		if ((pFile->pDictList) && (!pFile->pDictList->uiUseCount))
		{
			flmUnlinkDict( pFile->pDictList);
		}
		flmLinkDictToFile( pFile, pDb->pDict);
	}

	f_mutexUnlock( gv_FlmSysData.hShareMutex);

Exit1:

	// If the local dictionary was updated during this transaction,
	// link the new local dictionary structures to their file - or free
	// them if there was an error.

	if (pDb->uiFlags & FDB_UPDATED_DICTIONARY)
	{
		if( RC_BAD( rc) && pDb->pDict)
		{
			// Unlink the FDB from the FDICT. - Shouldn't have
			// to lock semaphore, because the DICT is NOT linked
			// to the FFILE.

			flmAssert( pDb->pDict->pFile == NULL);
			flmUnlinkFdbFromDict( pDb);
		}
	}

	if (RC_BAD( rc))
	{

		// Since we failed to commit, do an abort.  We are purposely not
		// checking the return code from flmAbortDbTrans because we already
		// have an error return code.  If we attempted to log the transaction
		// to the RFL and failed, we don't want to try to log an abort packet.
		// The RFL code has already reset the log back to the starting point 
		// of the transaction, thereby discarding all operations.

		pDb->uiFlags &= ~FDB_COMMITTING_TRANS;
		(void)flmAbortDbTrans( pDb, bOkToLogAbort);
		uiTransType = FLM_NO_TRANS;

		// Do we need to force all handles to close?

		if( bForceCloseOnError)
		{

			// Since the commit packet has already been logged to the RFL,
			// we must have failed when trying to write the log header.  The
			// database is in a bad state and must be closed.

			// Set the "must close" flag on all FDBs linked to the FFILE
			// and set the FFILE's "must close" flag.  This will cause any
			// subsequent operations on the database to fail until all
			// handles have been closed.

			flmSetMustCloseFlags( pFile, rc, FALSE);
		}
	}
	else
	{
		bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE;
		if (uiTransType == FLM_UPDATE_TRANS)
		{
			if (gv_FlmSysData.UpdateEvents.pEventCBList)
			{
				flmTransEventCallback( F_EVENT_COMMIT_TRANS, (HFDB)pDb, rc,
							uiTransId);
			}

			// Do the BLOB and indexing work before we unlock the db.

			FBListAfterCommit( pDb);
			
			if (pDb->pIxStopList || pDb->pIxStartList)
			{
				
				// Must not call flmIndexingAfterCommit until after
				// completeTransWrites.  Otherwise, there is a potential
				// deadlock condition where flmIndexingAfterCommit is
				// waiting on an indexing thread to quit, but that
				// thread is waiting to be signaled by this thread that
				// writes are completed.  However, flmIndexingAfterCommit
				// also must only be called while the database is still
				// locked.  If we were to leave the database locked for
				// every call to completeTransWrites, however, we would
				// lose the group commit capability.  Hence, we opt to
				// only lose it when there are actual indexing operations
				// to start or stop - which should be very few transactions.
				// That is what the bIndexAfterCommit flag is for.
				
				bIndexAfterCommit = TRUE;
			}
		}
	}

	// Unlock the database, if the update transaction is still going.
	// NOTE: We check uiTransType because it may have been reset
	// to FLM_NO_TRANS up above if flmAbortDbTrans was called.

	if (uiTransType == FLM_UPDATE_TRANS)
	{
		if (RC_BAD( rc))
		{

			// SHOULD NEVER HAPPEN - because it would have been taken
			// care of above - flmAbortDbTrans would have been called and
			// uiTransType would no longer be FLM_UPDATE_TRANS.

			flmAssert( 0);
			(void)pFile->pRfl->completeTransWrites( pDb, FALSE, TRUE);
		}
		else if( !bForceCheckpoint)
		{
			if( bIndexAfterCommit)
			{
				rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE);
				flmIndexingAfterCommit( pDb);
				flmUnlinkDbFromTrans( pDb, TRUE);
			}
			else
			{
				rc = pFile->pRfl->completeTransWrites( pDb, TRUE, TRUE);
			}
		}
		else
		{

			// Do checkpoint, if forcing.  Before doing the checkpoint
			// we have to make sure the roll-forward log writes
			// complete.  We don't want to unlock the DB while the
			// writes are happening in this case - thus, the FALSE
			// parameter to completeTransWrites.

			if (RC_OK( rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE)))
			{
				bForceCloseOnError = FALSE;
				rc = ScaDoCheckpoint( pDbStats, pDb->pSFileHdl, pFile,
						(pDb->uiFlags & FDB_DO_TRUNCATE) ? TRUE : FALSE,
						TRUE, CP_TIME_INTERVAL_REASON,
						uiCPFileNum, uiCPOffset);
			}
			
			if (bIndexAfterCommit)
			{
				flmIndexingAfterCommit( pDb);
			}
			
			flmUnlinkDbFromTrans( pDb, TRUE);
		}

		if (RC_BAD( rc) && bForceCloseOnError)
		{

			// Since the commit packet has already been logged to the RFL,
			// we must have failed when trying to write the log header.  The
			// database is in a bad state and must be closed.

			// Set the "must close" flag on all FDBs linked to the FFILE
			// and set the FFILE's "must close" flag.  This will cause any
			// subsequent operations on the database to fail until all
			// handles have been closed.

			flmSetMustCloseFlags( pFile, rc, FALSE);
		}
	}
	else
	{

		// Unlink the database from the transaction
		// structure as well as from the FDICT structure.

		flmUnlinkDbFromTrans( pDb, FALSE);
	}

	if (pDbStats && uiTransType != FLM_NO_TRANS)
	{
		FLMUINT64	ui64ElapMilli = 0;

		flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli);
		pDbStats->bHaveStats = TRUE;
		if (uiTransType == FLM_READ_TRANS)
		{
			pDbStats->ReadTransStats.CommittedTrans.ui64Count++;
			pDbStats->ReadTransStats.CommittedTrans.ui64ElapMilli +=
					ui64ElapMilli;
			if (bInvisibleTrans)
			{
				pDbStats->ReadTransStats.InvisibleTrans.ui64Count++;
				pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli +=
					ui64ElapMilli;
			}
		}
		else
		{
			pDbStats->UpdateTransStats.CommittedTrans.ui64Count++;
			pDbStats->UpdateTransStats.CommittedTrans.ui64ElapMilli +=
					ui64ElapMilli;
		}
	}

	// Update stats

	if (pDb->pStats)
	{
		(void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats);
	}

Exit:

	pDb->uiFlags &= ~FDB_COMMITTING_TRANS;
	return( rc);
}
/********************************************************************
Desc: ?
*********************************************************************/
FLMINT ViewGetKey( void)
{
	RCODE				rc = FERR_OK;
	FlmRecord *		pKey = NULL;
	void *			pvFld;
	char				Prompt [80];
	FLMUINT			Num;
	FLMUINT			ValEntered;
	FLMUINT			Len;
	char				TempBuf [80];
	FLMUINT			NumFields;
	FLMUINT			i;
	FLMINT			GetOK;
	FLMBYTE			FieldName [80];
	FLMBYTE			FieldType [16];
	FLMINT			KeyEntered = FALSE;
	FLMBYTE			LFH[ LFH_SIZE];
	FLMUINT			FileOffset;
	LFILE *			pLFile = NULL;
	IXD *				pIxd;
	IFD *				pIfd;
	FLMUINT			uiRootBlkAddress;
	FLMBOOL			bTruncated;

	if (!gv_bViewHdrRead)
		ViewReadHdr();

	/* See if we can get dictionary information. */

	ViewGetDictInfo();
	if (gv_bViewHaveDictInfo)
	{

		/* Find the logical file */

		if ((RC_BAD( fdictGetIndex( ((FDB *)gv_hViewDb)->pDict,
				((FDB *)gv_hViewDb)->pFile->bInLimitedMode,
				gv_uiViewSearchLfNum, &pLFile, NULL))) &&
				(RC_BAD( fdictGetContainer( ((FDB *)gv_hViewDb)->pDict, 
				gv_uiViewSearchLfNum, &pLFile))))
		{
			pLFile = NULL;
		}
	}

	/* See if we have a valid logical file */

	if ((gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) ||
			(gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) ||
			(pLFile))
	{

		/* Get the LFH information for the logical file */

		if (!ViewGetLFH( gv_uiViewSearchLfNum, LFH, &FileOffset))
		{
			ViewShowError( "Could not get LFH for logical file");
			return( FALSE);
		}
		uiRootBlkAddress = FB2UD( &LFH [LFH_ROOT_BLK_OFFSET]);

		if (uiRootBlkAddress == 0xFFFFFFFF)
		{
			ViewShowError( "Logical file is empty");
			return( FALSE);
		}
	}
	else
	{
		ViewShowError( "Logical file not defined");
		return( FALSE);
	}

	if ((gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) ||
		 (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) ||
		 ((pLFile) &&
		  (pLFile->uiLfType == LF_CONTAINER)))
	{
		if (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER)
			f_strcpy( TempBuf, "Enter Dictionary Record Number: ");
		else if (gv_uiViewSearchLfNum == FLM_DATA_CONTAINER)
			f_strcpy( TempBuf, "Enter Data Container Record Number: ");
		else
			f_sprintf( (char *)TempBuf, 
				"Enter Record Number For Container %u: ", 
				(unsigned)gv_uiViewSearchLfNum);
		if ((!ViewGetNum( TempBuf, &Num, FALSE, 4,
										 0xFFFFFFFF, &ValEntered)) ||
				(!ValEntered))
			return( FALSE);
		f_UINT32ToBigEndian( (FLMUINT32)Num, gv_ucViewSearchKey);
		gv_uiViewSearchKeyLen = 4;
		return( TRUE);
	}

	/* At this point, we are dealing with an index. */

	if (gv_uiViewSearchLfNum == FLM_DICT_INDEX)
	{
		FLMUINT	 wTagType = 0;
		FLMUINT	 wElmLen;

		while (!wTagType)
		{
			if ((!ViewEditText( "Enter Type:", 
									TempBuf, sizeof( TempBuf), &ValEntered)) ||
				 (!ValEntered))
				return( FALSE);
			else if ((f_stricmp( TempBuf, "F") == 0) ||
						(f_stricmp( TempBuf, "FIELD") == 0))
			{
				wTagType = FLM_FIELD_TAG;
			}
			else if ((f_stricmp( TempBuf, "I") == 0) ||
						(f_stricmp( TempBuf, "INDEX") == 0))
			{
				wTagType = FLM_INDEX_TAG;
			}
			else if ((f_stricmp( TempBuf, "C") == 0) ||
						(f_stricmp( TempBuf, "CONTAINER") == 0))
			{
				wTagType = FLM_CONTAINER_TAG;
			}
			else if ((f_stricmp( TempBuf, "A") == 0) ||
						(f_stricmp( TempBuf, "AREA") == 0))
			{
				wTagType = FLM_AREA_TAG;
			}
			else
			{
				ViewShowError( "Illegal type, must be F)ield, I)ndex, C)ontainer, R)ecord, or A)rea");
				wTagType = 0;
			}
		}
		gv_ucViewSearchKey [0] = KY_CONTEXT_PREFIX;
		f_UINT16ToBigEndian( (FLMUINT16)wTagType, &gv_ucViewSearchKey [1]);
		gv_uiViewSearchKeyLen += KY_CONTEXT_LEN;
		gv_ucViewSearchKey [gv_uiViewSearchKeyLen++] = COMPOUND_MARKER;

		if (!ViewEditText( "Enter Name:", TempBuf, sizeof( TempBuf), &ValEntered))
			return( FALSE);

		/* Collate the name. */

		wElmLen = MAX_KEY_SIZ - gv_uiViewSearchKeyLen;
		if (RC_BAD( rc = KYCollateValue( &gv_ucViewSearchKey [gv_uiViewSearchKeyLen],
									&wElmLen,
									(const FLMBYTE *)TempBuf,
									(FLMUINT)f_strlen( TempBuf), FLM_TEXT_TYPE,
									MAX_KEY_SIZ,
									NULL, NULL,
									gv_ViewHdrInfo.FileHdr.uiDefaultLanguage,
									FALSE, FALSE, FALSE, &bTruncated)))
		{
			ViewShowRCError( "collating name", rc);
			return( FALSE);
		}
		gv_uiViewSearchKeyLen += wElmLen;
		return( TRUE);
	}
	else if (!pLFile)
	{
		ViewShowError( "Cannot get logical file information");
		return( FALSE);
	}
	else if (RC_BAD( fdictGetIndex( ((FDB *)gv_hViewDb)->pDict,
			((FDB *)gv_hViewDb)->pFile->bInLimitedMode,
			gv_uiViewSearchLfNum, &pLFile, &pIxd)))
	{
		ViewShowError( "Cannot get index field information");
		return( FALSE);
	}
	else
	{
		pIfd = pIxd->pFirstIfd;
		NumFields = pIxd->uiNumFlds;

		if (!(pIfd->uiFlags & IFD_COMPOUND))
		{
			NumFields = 1;
		}

		if( (pKey = f_new FlmRecord) == NULL)
		{
			rc = RC_SET( FERR_MEM);
			ViewShowRCError( "creating key", rc);
			goto Exit_False;
		}

		if (RC_BAD( rc = pKey->insertLast( 0, FLM_KEY_TAG,
										FLM_CONTEXT_TYPE, &pvFld)))
		{
			ViewShowRCError( "adding key tag", rc);
			goto Exit_False;
		}

		/* Ask for data for each field and link into key tree */

		i = 0;
		while (i < NumFields)
		{

			/* Get the name of the field and its type */

			f_sprintf( (char *)FieldName, "FIELD %u", (unsigned)pIfd->uiFldNum);
			switch( IFD_GET_FIELD_TYPE( pIfd))
			{
				case FLM_TEXT_TYPE:
					f_strcpy( (char *)FieldType, "TEXT");
					break;
				case FLM_NUMBER_TYPE:
					f_strcpy( (char *)FieldType, "NUMBER");
					break;
				case FLM_BINARY_TYPE:
					f_strcpy( (char *)FieldType, "BINARY");
					break;
				case FLM_CONTEXT_TYPE:
					f_strcpy( (char *)FieldType, "CONTEXT");
					break;
				default:
					f_sprintf( (char *)FieldType, "UNK: %u!",
						(unsigned)IFD_GET_FIELD_TYPE( pIfd));
					break;
			}
			if (pIfd->uiFlags & IFD_OPTIONAL)
				f_sprintf( (char *)Prompt, "%s (%s-OPTIONAL): ", FieldName, FieldType);
			else
				f_sprintf( (char *)Prompt, "%s (%s-REQUIRED): ", FieldName, FieldType);

			switch( IFD_GET_FIELD_TYPE( pIfd))
			{
				case FLM_TEXT_TYPE:
					if (!ViewEditText( Prompt, TempBuf, sizeof( TempBuf),
								&ValEntered))
						goto Exit_False;
					break;
				case FLM_NUMBER_TYPE:
				case FLM_CONTEXT_TYPE:
					if (!ViewGetNum( Prompt, &Num, FALSE, 4, 0xFFFFFFFF,
									&ValEntered))
						goto Exit_False;
					break;
				case FLM_BINARY_TYPE:
					Len = sizeof( TempBuf);
					if (!ViewEditBinary( Prompt, TempBuf, &Len, &ValEntered))
						goto Exit_False;
					break;
			}
			if (!ValEntered)
			{
				i++;
			}
			else
			{
				FLMUINT	uiDataType;

				/* See if the entered data can be converted to the */
				/* correct type */

				uiDataType = IFD_GET_FIELD_TYPE( pIfd);
				if (RC_BAD( rc = pKey->insertLast( 1, pIfd->uiFldNum,
													uiDataType, &pvFld)))
				{
					ViewShowRCError( "creating field", rc);
				}
				else
				{
					switch( IFD_GET_FIELD_TYPE( pIfd))
					{
						case FLM_TEXT_TYPE:
							rc = pKey->setNative( pvFld, TempBuf);
							break;
						case FLM_NUMBER_TYPE:
							rc = pKey->setUINT( pvFld, Num);
							break;
						case FLM_CONTEXT_TYPE:
							rc = pKey->setRecPointer( pvFld, Num);
							break;
						case FLM_BINARY_TYPE:
							rc = pKey->setBinary( pvFld, TempBuf, Len);
							break;
					}
					if (RC_BAD( rc))
					{
						ViewShowRCError( "putting data in field", rc);
					}
				}
				if (RC_OK(rc))
				{
					i++;
					pIfd++;
					KeyEntered = TRUE;
				}
			}
		}

		// If index is on all containers, prompt for container number.

		if (!pIxd->uiContainerNum)
		{
			f_strcpy( Prompt, "CONTAINER: ");
			if (!ViewGetNum( Prompt, &Num, FALSE, sizeof( Num), 0xFFFF,
									&ValEntered))
			{
				goto Exit_False;
			}
			if (ValEntered)
			{
				pKey->setContainerID( Num);
				KeyEntered = TRUE;
			}
		}

		/* Convert the key to binary format */

		if (!KeyEntered)
			goto Exit_False;

		if ((rc = FlmKeyBuild( gv_hViewDb, gv_uiViewSearchLfNum,
									pKey->getContainerID(), pKey, 0,
									gv_ucViewSearchKey, &gv_uiViewSearchKeyLen)) != FERR_OK)
			ViewShowRCError( "building key", rc);
		else
		{
			GetOK = TRUE;
			goto Exit_GetKey;
		}
	}

Exit_False:
	GetOK = FALSE;
Exit_GetKey:
	if (pKey)
	{
		pKey->Release();
	}
	return( GetOK);
}