/**************************************************************************** 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); }
/**************************************************************************** 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); }