//------------------------------------------------------------------------- // Desc: Dictionary updates. // Tabs: 3 // // Copyright (c) 1991-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: fslfileu.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE FDDBuildIndex( FDB * pDb, FLMUINT uiIndexNum, FLMBOOL bDoInBackground, FLMBOOL bCreateSuspended, FLMBOOL * pbLogCompleteIndexSet); FSTATIC RCODE flmFreeBlockChain( FDB * pDb, FLMUINT uiStartAddr, FLMUINT * puiCount, FLMUINT * puiEndAddr); FSTATIC RCODE flmFreeLFileBlocks( FDB * pDb, LFILE * pLFile); FSTATIC RCODE flmFreeContainerBlocks( FDB * pDb, LFILE * pLFile); FSTATIC RCODE flmFreeIndexBlocks( FDB_p pDb, LFILE * pLFile, FLMBOOL bInvalidateLFile); FSTATIC RCODE flmRetrieveTrackerRec( FDB * pDb, FLMUINT uiDrn, FLMBOOL bExact, FlmRecord ** ppRecord); FSTATIC RCODE flmDeleteTrackerRec( FDB * pDb, FLMUINT uiDrn); FSTATIC RCODE flmModifyTrackerRec( FDB * pDb, FLMUINT uiDrn, FlmRecord * pRecord); FSTATIC RCODE flmMaintThread( F_Thread * pThread); FSTATIC RCODE fdictRemoveIndexes( FDB * pDb, FLMUINT uiContainer); FSTATIC RCODE DDVerifyModField( FDB * pDb, FlmRecord * pOldRecord, FlmRecord * pNewRecord); FSTATIC RCODE DDVerifyModEncDef( FDB * pDb, FlmRecord * pOldRecord, FlmRecord * pNewRecord); FSTATIC RCODE flmAddIndexFixup( FDB * pDb, FLMUINT uiIndexNum, FLMUINT uiLastDrnIndexed, FLMUINT uiLastContainerIndexed); /*************************************************************************** Desc: Verify that the action being attempted in a dictionary update is valid. Caller should check if container in valid range. ****************************************************************************/ RCODE flmLFileDictUpdate( FDB * pDb, LFILE * pDictLFile, FLMUINT * puiDrnRV, FlmRecord * pNewRecord, FlmRecord * pOldRecord, FLMBOOL bDoInBackground, FLMBOOL bCreateSuspended, FLMBOOL * pbLogCompleteIndexSet, FLMBOOL bRebuildOp) { RCODE rc = FERR_OK; LFILE * pLFile; LFILE * pDictIxLFile = NULL; LFILE LFile; FLMUINT uiNewRecordType; FLMUINT uiOldRecordType; char szNativeBuf1[ 32]; FLMUINT uiLen; void * pvField; // Flush any index keys before making any changes to dictionary items. // Dictionary changes may change index definitions, etc. if( RC_BAD(rc = KYKeysCommit( pDb, FALSE))) { goto Exit; } // Also need to flush out any index counts before making changes to // dictionary items - due to the fact that index definitions may get // dropped, added, etc. if (RC_BAD( rc = FSCommitIxCounts( pDb))) { goto Exit; } // Clear out the cdl table in case it changes KrefCntrlFree( pDb); uiNewRecordType = uiOldRecordType = 0; if( pNewRecord) { pvField = pNewRecord->root(); uiNewRecordType = pNewRecord->getFieldID( pvField); } if( pOldRecord) { pvField = pOldRecord->root(); uiOldRecordType = pOldRecord->getFieldID(pvField); } if( !pNewRecord) { // Cannot delete a field if it is referenced from an index. if( uiOldRecordType == FLM_FIELD_TAG ) { if( RC_BAD( rc = flmCheckDictFldRefs( pDb->pDict, *puiDrnRV))) { goto Exit; } // Determine if this field is qualified to be deleted. // Those rules are: // // 1) it must have a state value of 'unused' // 2) or a state of 'purge' (FlmDbSweep or recover only) if( (pvField = pOldRecord->find( pOldRecord->root(), FLM_STATE_TAG)) == NULL) { rc = RC_SET( FERR_CANNOT_DEL_ITEM); goto Exit; } uiLen = sizeof( szNativeBuf1); if( RC_BAD( rc = pOldRecord->getNative( pvField, szNativeBuf1, &uiLen))) { goto Exit; } if( f_strnicmp( szNativeBuf1, "unus", 4) == 0 || (f_strnicmp( szNativeBuf1, "purg", 4) == 0 && pDb->bFldStateUpdOk)) { ; // It's okay to delete this field or record template. } else { rc = RC_SET( FERR_CANNOT_DEL_ITEM); goto Exit; } } else if ( uiOldRecordType == FLM_ENCDEF_TAG ) { if( RC_BAD( rc = flmCheckDictEncDefRefs( pDb->pDict, *puiDrnRV))) { goto Exit; } // Determine if this record is qualified to be deleted. // Those rules are: // // 1) it must have a state value of 'unused' // 2) or a state of 'purge' (FlmDbSweep or recover only) if( (pvField = pOldRecord->find( pOldRecord->root(), FLM_STATE_TAG)) == NULL) { rc = RC_SET( FERR_CANNOT_DEL_ITEM); goto Exit; } uiLen = sizeof( szNativeBuf1); if( RC_BAD( rc = pOldRecord->getNative( pvField, szNativeBuf1, &uiLen))) { goto Exit; } if( f_strnicmp( szNativeBuf1, "unus", 4) == 0 || (f_strnicmp( szNativeBuf1, "purg", 4) == 0 && pDb->bFldStateUpdOk)) { ; // It's okay to delete this field or record template. } else { rc = RC_SET( FERR_CANNOT_DEL_ITEM); goto Exit; } } } else if( pNewRecord && pOldRecord) { // We cannot allow changing the type of dictionary definition if( uiOldRecordType != uiNewRecordType) { rc = RC_SET( FERR_CANNOT_MOD_DICT_REC_TYPE); goto Exit; } // If modifying a field or encryption definition record, // cannot change the type. if ( uiOldRecordType == FLM_FIELD_TAG) { if (RC_BAD( rc = DDVerifyModField( pDb, pOldRecord, pNewRecord))) { goto Exit; } } else if ( uiOldRecordType == FLM_ENCDEF_TAG) { if (RC_BAD( rc = DDVerifyModEncDef( pDb, pOldRecord, pNewRecord))) { } } } if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, FLM_DICT_INDEX, &pDictIxLFile, NULL))) { goto Exit; } // Make sure new records do not contain a key. if( !pOldRecord ) { if (uiNewRecordType == FLM_ENCDEF_TAG && !bRebuildOp && !(pDb->uiFlags & FDB_REPLAYING_RFL)) { // Cannot add a record with a key field already present. if( (pNewRecord->find( pNewRecord->root(), FLM_KEY_TAG)) != NULL) { rc = RC_SET( FERR_CANNOT_SET_KEY); goto Exit; } } } // fdictRecUpdate checks for duplicates (invalid ADD). // It also makes sure we are not trying to change the key on EncDef records. // It will make sure new keys are generated for new EncDef records. if( RC_BAD( rc = fdictRecUpdate( pDb, pDictLFile, pDictIxLFile, puiDrnRV, pNewRecord, pOldRecord, bRebuildOp))) { goto Exit; } // Now take care of adding, deleting or rebuilding indexes & containers. if( !pOldRecord) { FLMBOOL bRereadLFiles = FALSE; // Create a NEW LFILE for indexes and containers. if( (uiNewRecordType == FLM_INDEX_TAG ) || (uiNewRecordType == FLM_CONTAINER_TAG )) { if( RC_BAD(rc = flmLFileCreate( pDb, &LFile, *puiDrnRV, (uiNewRecordType == FLM_INDEX_TAG) ? LF_INDEX : LF_CONTAINER))) { goto Exit; } pLFile = &LFile; bRereadLFiles = TRUE; } // Special optimization just for adding a new object to the dictionary. if( RC_BAD( rc = flmAddRecordToDict( pDb, pNewRecord, *puiDrnRV, bRereadLFiles))) { goto Exit; } if( uiNewRecordType == FLM_INDEX_TAG) { // Build the indexes if( RC_BAD( rc = FDDBuildIndex( pDb, *puiDrnRV, bDoInBackground, bCreateSuspended, pbLogCompleteIndexSet))) { goto Exit; } } } else { // Delete Record if( !pNewRecord) { if( uiOldRecordType == FLM_INDEX_TAG ) { if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, *puiDrnRV, &pLFile, NULL, TRUE))) { goto Exit; } if( RC_BAD( rc = flmFreeIndexBlocks( pDb, pLFile, TRUE))) { goto Exit; } // Remove from index fixup list if we are deleting an index. // It is impossible to be deleting an index in a background // thread, so if there is a fixup, it is here because the // index was added during this transaction (in the background). // If the transaction aborts the IXD will simply go away, and // there will be no need to fix it up. if (pDb->pIxdFixups) { IXD_FIXUP * pIxdFixup = pDb->pIxdFixups; IXD_FIXUP * pPrevIxdFixup = NULL; while (pIxdFixup && pIxdFixup->uiIndexNum != *puiDrnRV) { pPrevIxdFixup = pIxdFixup; pIxdFixup = pIxdFixup->pNext; } if (pIxdFixup) { if (pPrevIxdFixup) { pPrevIxdFixup->pNext = pIxdFixup->pNext; } else { pDb->pIxdFixups = pIxdFixup->pNext; } f_free( &pIxdFixup); } } } else if( uiOldRecordType == FLM_CONTAINER_TAG ) { if( RC_BAD( rc = fdictGetContainer( pDb->pDict, *puiDrnRV, &pLFile))) { goto Exit; } // Remove indexes associated with this container. if (RC_BAD( rc = fdictRemoveIndexes( pDb, *puiDrnRV))) { goto Exit; } // Need to remove all records from record cache // for this container! flmRcaRemoveContainerRecs( pDb, *puiDrnRV); if( RC_BAD( rc = flmFreeContainerBlocks( pDb, pLFile))) { goto Exit; } } } else { // Modify Record if( uiOldRecordType == FLM_INDEX_TAG ) { if( RC_BAD(rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, *puiDrnRV, &pLFile, NULL, TRUE))) { goto Exit; } if( RC_BAD( rc = flmFreeIndexBlocks( pDb, pLFile, FALSE))) { goto Exit; } } } // On delete or modify index make sure something is in the stop list. if( uiOldRecordType == FLM_INDEX_TAG && !(pDb->uiFlags & FDB_REPLAYING_RFL)) { if( RC_BAD( rc = flmAddToStopList( pDb, pOldRecord->getID()))) { goto Exit; } } // Create a whole new internal dictionary. if( RC_BAD( rc = fdictCreateNewDict( pDb))) { goto Exit; } if( pNewRecord && uiOldRecordType == FLM_INDEX_TAG) { // Rebuild the index if( RC_BAD( rc = FDDBuildIndex( pDb, *puiDrnRV, bDoInBackground, bCreateSuspended, pbLogCompleteIndexSet))) { goto Exit; } } } Exit: return( rc ); } /*************************************************************************** Desc: This routine checks to see if a field is referenced in an index or record template definitions. ****************************************************************************/ RCODE flmCheckDictFldRefs( FDICT * pDict, FLMUINT uiFieldNum) { RCODE rc = FERR_OK; IFD * pIfd; FLMUINT * pFieldPathTbl; FLMUINT uiFldPathsCnt; // Does the field have an IFD reference. if( RC_BAD( rc = fdictGetField( pDict, uiFieldNum, NULL, &pIfd, NULL))) { goto Exit; } if( pIfd) { rc = RC_SET( FERR_CANNOT_DEL_ITEM); goto Exit; } // Look through all of the field paths. pFieldPathTbl = pDict->pFldPathsTbl; uiFldPathsCnt = pDict->uiFldPathsCnt; for( ; uiFldPathsCnt--; pFieldPathTbl++) { if( *pFieldPathTbl == uiFieldNum) { rc = RC_SET( FERR_CANNOT_DEL_ITEM); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: This routine checks to see if an encryption definition is referenced in an index. ****************************************************************************/ RCODE flmCheckDictEncDefRefs( FDICT * pDict, FLMUINT uiEncId) { RCODE rc = FERR_OK; IXD * pIxd; FLMUINT uiIxdCnt; // Is the Encryption Definition referenced in any index? for( pIxd = pDict->pIxdTbl, uiIxdCnt = pDict->uiIxdCnt; uiIxdCnt--; pIxd++) { if( pIxd->uiEncId && pIxd->uiEncId == uiEncId) { rc = RC_SET( FERR_CANNOT_DEL_ITEM); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Build an index. ****************************************************************************/ FSTATIC RCODE FDDBuildIndex( FDB * pDb, FLMUINT uiIndexNum, FLMBOOL bDoInBackground, FLMBOOL bCreateSuspended, FLMBOOL * pbLogCompleteIndexSet) { RCODE rc = FERR_OK; LFILE * pLFile; IXD * pIxd; // Flush any keys and free the tables because they may grow! if( RC_BAD( rc = KYKeysCommit( pDb, TRUE))) { goto Exit; } if( RC_BAD( rc = KrefCntrlCheck( pDb))) { goto Exit; } if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, &pLFile, &pIxd))) { goto Exit; } if( RC_BAD( rc = flmLFileIndexBuild( pDb, pLFile, pIxd, bDoInBackground, bCreateSuspended, pbLogCompleteIndexSet))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Read all records from a container building all active indexes ****************************************************************************/ RCODE flmLFileIndexBuild( FDB * pDb, LFILE * pIxLFile, IXD * pIxd, FLMBOOL bDoInBackground, FLMBOOL bCreateSuspended, FLMBOOL * pbLogCompleteIndexSet) { RCODE rc = FERR_OK; if( pDb->uiFlags & FDB_REPLAYING_RFL) { // Don't index now. The RFL function INDEX_SET will // generate index keys. if( pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_3_02 && pDb->pFile->FileHdr.uiVersionNum <= FLM_VER_4_51) { if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, pIxd->uiIndexNum, 1, 0, TRANS_ID_OFFLINE, FALSE))) { goto Exit; } goto Exit; } } // Start the indexing thread if building in the background // and NOT a unique index. if( bDoInBackground && !(pIxd->uiFlags & IXD_UNIQUE)) { if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, pIxd->uiIndexNum, 1, 0, TRANS_ID_OFFLINE, bCreateSuspended))) { goto Exit; } if( RC_BAD( rc = flmLFileWrite( pDb, pIxLFile))) { goto Exit; } pIxd->uiFlags |= IXD_OFFLINE; if( bCreateSuspended) { pIxd->uiFlags |= IXD_SUSPENDED; } else if( !(pDb->uiFlags & FDB_REPLAYING_RFL)) { // Don't add the index to the start list if we are replaying // the RFL. The indexing threads will be started once // recovery is complete. if( RC_BAD( rc = flmAddToStartList( pDb, pIxd->uiIndexNum))) { goto Exit; } } goto Exit; } // uiIndexToBeUpdated better be zero at this point since we // are not working in the background. if( RC_BAD( rc = flmIndexSetOfRecords( pDb, pIxd->uiIndexNum, 0, 1, DRN_LAST_MARKER, pDb->fnStatus, pDb->StatusData, pDb->fnIxCallback, pDb->IxCallbackData, NULL, NULL))) { goto Exit; } if( pbLogCompleteIndexSet) { *pbLogCompleteIndexSet = TRUE; } Exit: // Need to convert FERR_NOT_UNIQUE to FERR_IX_FAILURE so that we // can force the transaction to abort. Normally, FERR_NOT_UNIQUE is // an error that does not require the transaction to abort. However, // in this case, we are generating an index and it is not possible // to continue the transaction - because we may have modified disk // blocks. if( rc == FERR_NOT_UNIQUE) { rc = RC_SET( FERR_IX_FAILURE); } return( rc); } /*************************************************************************** Desc: Index a set of records. Called when recovering or restoring from roll-forward log. ****************************************************************************/ RCODE flmDbIndexSetOfRecords( HFDB hDb, FLMUINT uiIxNum, FLMUINT uiContainerNum, FLMUINT uiStartDrn, FLMUINT uiEndDrn) { FLMBOOL bStartedAutoTrans = FALSE; FDB * pDb = (FDB *)hDb; RCODE rc = FERR_OK; if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK, 0, &bStartedAutoTrans))) { goto Exit; } if( RC_BAD( rc = flmIndexSetOfRecords( pDb, uiIxNum, uiContainerNum, uiStartDrn, uiEndDrn, NULL, NULL, NULL, NULL, NULL, NULL))) { goto Exit; } Exit: if( bStartedAutoTrans) { rc = flmEndAutoTrans( pDb, rc); } // Unlock all memory that has been locked by FLAIM (void)fdbExit( pDb); return( rc); } /*************************************************************************** Desc: Logs information about an index being built ****************************************************************************/ void flmLogIndexingProgress( FLMUINT uiIndexNum, FLMUINT uiLastDrn) { flmLogMessage( FLM_DEBUG_MESSAGE, FLM_YELLOW, FLM_BLACK, uiLastDrn ? "Indexing progress: Index %u is offline. Last record processed = %u." : "Indexing progress: Index %u is online.", (unsigned)uiIndexNum, (unsigned)uiLastDrn); } /*************************************************************************** Desc: Index a set of records or until time runs out. ****************************************************************************/ RCODE flmIndexSetOfRecords( FDB * pDb, FLMUINT uiIxNum, FLMUINT uiContainerNum, // 0 is passed in when doing all containers. // It will only be 0 when adding an index in // the foreground. FLMUINT uiStartDrn, FLMUINT uiEndDrn, STATUS_HOOK fnStatus, void * StatusData, IX_CALLBACK fnIxCallback, void * IxCallbackData, FINDEX_STATUS * pIndexStatus, FLMBOOL * pbHitEnd, F_Thread * pThread, FlmRecord * pReusableRec) { RCODE rc = FERR_OK; FLMUINT uiDrn; FLMUINT uiLastDrn = 0; IXD * pIxd = NULL; LFILE * pDataLFile; FlmRecord * pRecord = NULL; FlmRecord * pModRecord = NULL; ServerLockObject * pFileLockObj = pDb->pFile->pFileLockObj; FLMBOOL bHitEnd = FALSE; FLMBOOL bDataRecordRead; FLMUINT uiCurrTime; FLMUINT uiLastStatusTime = 0; FLMUINT uiStartTime; FLMUINT uiMinTU; FLMUINT uiStatusIntervalTU; FLMUINT uiRecsProcessed = 0; FLMBOOL bUpdateTracker = FALSE; FLMBOOL bHadUniqueKeys; FLMBOOL bRelinquish = FALSE; BTSK stackBuf [BH_MAX_LEVELS]; BTSK * pStack = &stackBuf [0]; FLMBYTE ucKeyBuf [DIN_KEY_SIZ]; FLMBYTE ucSearchKey [DIN_KEY_SIZ]; FLMBOOL bDoAllContainers = FALSE; ITT * pItt; POOL ReadPool; void * pvTmpPoolMark = GedPoolMark( &pDb->TempPool); GedPoolInit( &ReadPool, 8192); FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); FLM_MILLI_TO_TIMER_UNITS( 500, uiMinTU); FLM_SECS_TO_TIMER_UNITS( 10, uiStatusIntervalTU); uiStartTime = FLM_GET_TIMER(); if( !pReusableRec) { if( (pReusableRec = f_new FlmRecord) != NULL) { if( RC_BAD( pReusableRec->preallocSpace( 512, 1024 * 64))) { pReusableRec->Release(); pReusableRec = NULL; } } } else { pReusableRec->AddRef(); } if( RC_BAD( rc = KrefCntrlCheck( pDb))) { goto Exit; } if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIxNum, NULL, &pIxd, TRUE))) { goto Exit; } flmAssert( (pIxd->uiFlags & IXD_SUSPENDED) == 0); if (!pIxd->uiContainerNum && !uiContainerNum) { flmAssert( pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_50); bDoAllContainers = TRUE; } for (;;) { if( !bDoAllContainers) { // NOTE: Background indexing always comes through this // code path, because a non-zero index number is passed // in, even for cross-container indexes. // NOTE ALSO: We always take this path for single-container // indexes too, because bDoAllContainers is always FALSE // for them. #ifdef FLM_DEBUG if( !pIxd->uiContainerNum) { flmAssert( pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_50); } #endif uiContainerNum = (FLMUINT)(pIxd->uiContainerNum ? pIxd->uiContainerNum : uiContainerNum); if (RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pDataLFile))) { goto Exit; } } else { uiStartDrn = 1; uiEndDrn = 0xFFFFFFFF; if (uiContainerNum == FLM_DATA_CONTAINER) { bHitEnd = TRUE; break; } uiContainerNum++; while (uiContainerNum < pDb->pDict->uiIttCnt) { pItt = &pDb->pDict->pIttTbl [uiContainerNum]; if (ITT_IS_CONTAINER( pItt)) { pDataLFile = (LFILE *)pItt->pvItem; break; } else { uiContainerNum++; } } if (uiContainerNum >= pDb->pDict->uiIttCnt) { uiContainerNum = FLM_DATA_CONTAINER; pDataLFile = &pDb->pDict->pLFileTbl[ LFILE_DATA_CONTAINER_OFFSET]; } } uiLastDrn = 0; pStack->pKeyBuf = ucKeyBuf; longToByte( uiStartDrn, ucSearchKey); if (RC_BAD( rc = FSBtSearch( pDb, pDataLFile, &pStack, ucSearchKey, 4, 0))) { goto Exit; } if( pStack->uiCmpStatus == BT_END_OF_DATA || pStack->uiBlkAddr == BT_END) { bHitEnd = TRUE; goto Next_Container; } pStack->uiFlags = NO_STACK; // Stack points to lowest leaf element (not root) for (;;) { if ((uiDrn = byteToLong( ucKeyBuf)) == DRN_LAST_MARKER) { bHitEnd = TRUE; break; } // NOTE: uiEndDrn will be either DRN_LAST_MARKER, which means index // as far as we can, or it will be some value that we are to index // up to - set only when recovering or restoring from the RFL. if( uiDrn > uiEndDrn) { break; } // Check first if the record is in cache. #ifdef FLM_DEBUG if( pReusableRec) { flmAssert( !pReusableRec->isCached()); } #endif bDataRecordRead = FALSE; if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, uiContainerNum, uiDrn, FALSE, NULL, NULL, &pRecord))) { if( rc != FERR_NOT_FOUND) { goto Exit; } // If records are equal, no need to do anything. // In fact the code would not work properly because the call // to Release might free the record, in which case m_pRec would // be pointing to freed space. The AddRef would then be done on a // freed record. if( pRecord != pReusableRec) { // NOTE: If not found in cache, we deliberately don't bring it // in to cache. Read the data record into memory, then build // and commit its keys. if( pRecord) { pRecord->Release(); } pRecord = pReusableRec; if( pRecord) { pRecord->AddRef(); } } if( RC_BAD( rc = FSReadElement( pDb, &ReadPool, pDataLFile, uiDrn, pStack, FALSE, &pRecord, NULL, NULL))) { goto Exit; } flmAssert( !pRecord->isCached()); bDataRecordRead = TRUE; } bHadUniqueKeys = FALSE; if( RC_BAD(rc = flmProcessRecFlds( pDb, pIxd, uiContainerNum, uiDrn, pRecord, KREF_ADD_KEYS | KREF_INDEXING_ONLY, TRUE, &bHadUniqueKeys))) { goto Exit; } if( RC_BAD( rc = KYProcessDupKeys( pDb, bHadUniqueKeys))) { goto Exit; } if( pIndexStatus) { pIndexStatus->uiKeysProcessed += (FLMUINT)(pDb->KrefCntrl.uiCount - pDb->KrefCntrl.uiLastRecEnd); } KYFinishCurrentRecord( pDb); if (RC_BAD( rc = KYFlushKeys( pDb))) { goto Exit; } // The indexing code uses the temporary pool to allocate CDL table // entries. Now that we are finished indexing the current record, // we need to reset the pool to free the CDL allocations. GedPoolReset( &pDb->TempPool, pvTmpPoolMark); // See if there is an indexing callback if (fnIxCallback) { if (pModRecord) { pModRecord->Release(); pModRecord = NULL; } if (RC_BAD( rc = (*fnIxCallback)( (HFDB)pDb, uiIxNum, uiContainerNum, uiDrn, pRecord, &pModRecord, IxCallbackData))) { goto Exit; } // If the callback function sent back a changed record, we need // to do the modification to the record. NOTE: This can only be // done AFTER adding the keys for the new index on the old record. // This call to FlmRecordModify will then change the keys if // necessary. if (pModRecord) { if (RC_BAD( rc = FlmRecordModify( (HFDB)pDb, uiContainerNum, uiDrn, pModRecord, 0))) { goto Exit; } // Need to readjust stack after modifying record. FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); pStack = stackBuf; pStack->pKeyBuf = ucKeyBuf; longToByte( uiDrn, ucSearchKey); if (RC_BAD( rc = FSBtSearch( pDb, pDataLFile, &pStack, ucSearchKey, 4, 0))) { goto Exit; } // Should have found the record! We just modified it! if (pStack->uiCmpStatus != BT_EQ_KEY || pStack->uiBlkAddr == BT_END) { flmAssert( 0); rc = RC_SET( FERR_BTREE_ERROR); goto Exit; } pStack->uiFlags = NO_STACK; bDataRecordRead = FALSE; } } uiLastDrn = uiDrn; uiRecsProcessed++; if( pIndexStatus) { pIndexStatus->uiRecordsProcessed++; pIndexStatus->uiLastRecordIdIndexed = uiLastDrn; } // Get the current time uiCurrTime = FLM_GET_TIMER(); // Break out if someone is waiting for an update transaction. if( pThread) { if( pThread->getShutdownFlag()) { bRelinquish = TRUE; break; } if( pFileLockObj->ThreadWaitingLock()) { // See if our minimum run time has elapsed if( FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >= uiMinTU) { if( uiRecsProcessed < 50) { // If there are higher priority waiters in the lock queue, // we want to relinquish. if( pDb->pFile->pFileLockObj->haveHigherPriorityWaiter( FLM_BACKGROUND_LOCK_PRIORITY)) { bRelinquish = TRUE; break; } } else { bRelinquish = TRUE; break; } } } else { // Even if no one has requested a lock for a long time, we // still want to periodically commit our transaction so // we won't lose more than uiMaxCPInterval timer units worth // of work if we crash. We will run until we exceed the checkpoint // interval and we see that someone (the checkpoint thread) is // waiting for the write lock. if( FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) > gv_FlmSysData.uiMaxCPInterval && pDb->pFile->pWriteLockObj->ThreadWaitingLock()) { bRelinquish = TRUE; break; } } } if( FLM_ELAPSED_TIME( uiCurrTime, uiLastStatusTime) >= uiStatusIntervalTU) { uiLastStatusTime = uiCurrTime; if( fnStatus) { if( RC_BAD( rc = (*fnStatus)( FLM_INDEXING_STATUS, (void *) uiLastDrn, (void *) 0, (void *) StatusData))) { goto Exit; } } // Send indexing completed event notification if( gv_FlmSysData.EventHdrs[ F_EVENT_UPDATES].pEventCBList) { flmDoEventCallback( F_EVENT_UPDATES, F_EVENT_INDEXING_COMPLETE, (void *)uiIxNum, (void *)uiLastDrn); } // Log a progress message flmLogIndexingProgress( uiIxNum, uiLastDrn); } if (bDataRecordRead) { if (RC_BAD( rc = FSBtNextElm( pDb, pDataLFile, pStack))) { if (rc != FERR_BT_END_OF_DATA) { goto Exit; } rc = FERR_OK; bHitEnd = TRUE; break; } } else { // Need to go to the next record. if (RC_BAD( rc = FSNextRecord( pDb, pDataLFile, pStack))) { if (rc != FERR_BT_END_OF_DATA) { goto Exit; } rc = FERR_OK; bHitEnd = TRUE; break; } } } Next_Container: if( !bDoAllContainers || bRelinquish) { break; } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); pStack = stackBuf; } if (RC_BAD( rc = KYKeysCommit( pDb, TRUE))) { goto Exit; } if (bHitEnd && !pIxd->uiContainerNum && !bDoAllContainers) { if (uiContainerNum != FLM_DATA_CONTAINER) { bHitEnd = FALSE; // Increment container number. NOTE: This may not be // a valid container number, but we will determine the // next valid container number, the next time this // function is called by the background indexing // thread. uiContainerNum++; // Write a DRN of zero into the tracker so that the // next time in, we will start at the beginning // of this container. uiLastDrn = 0; bUpdateTracker = TRUE; } } // If at the end, change trans ID to the current transaction. if( bHitEnd) { // Create a new dictionary that will correctly reflect the // on-line status of the index once the transaction commits. if( (pDb->uiFlags & FDB_UPDATED_DICTIONARY) == 0) { if( RC_BAD( rc = fdictCloneDict( pDb))) { goto Exit; } } // NOTE: Always re-get the IXD prior to updating it, since the // dictionary may have been updated either by our thread (see // above call to flmCloneDict) or by another thread participating // in a shared transaction. if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIxNum, NULL, &pIxd, TRUE))) { goto Exit; } pIxd->uiFlags &= ~(IXD_SUSPENDED | IXD_OFFLINE); pIxd = NULL; if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, uiIxNum, 0xFFFFFFFF, DRN_LAST_MARKER, pDb->LogHdr.uiCurrTransID, FALSE))) { goto Exit; } } else if( uiRecsProcessed || bUpdateTracker) { if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, uiIxNum, uiContainerNum, uiLastDrn, TRANS_ID_OFFLINE, FALSE))) { goto Exit; } } Exit: // We want to make one last call if we are in the foreground or if // we actually did some indexing. if( !pThread) { if( gv_FlmSysData.EventHdrs[ F_EVENT_UPDATES].pEventCBList) { flmDoEventCallback( F_EVENT_UPDATES, F_EVENT_INDEXING_COMPLETE, (void *)uiIxNum, (void *)(bHitEnd ? 0 : uiLastDrn)); } flmLogIndexingProgress( uiIxNum, bHitEnd ? 0 : uiLastDrn); } else if( uiLastDrn) { if( gv_FlmSysData.EventHdrs[ F_EVENT_UPDATES].pEventCBList) { flmDoEventCallback( F_EVENT_UPDATES, F_EVENT_INDEXING_COMPLETE, (void *)uiIxNum, (void *)uiLastDrn); } flmLogIndexingProgress( uiIxNum, uiLastDrn); } if( fnStatus) { (void) (*fnStatus)( FLM_INDEXING_STATUS, (void *) uiLastDrn, (void *) 0, (void *) StatusData); } if( pbHitEnd) { *pbHitEnd = bHitEnd; } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); KrefCntrlFree( pDb); GedPoolReset( &pDb->TempPool, pvTmpPoolMark); GedPoolFree( &ReadPool); if( pReusableRec) { flmAssert( !pReusableRec->isCached()); pReusableRec->Release(); } if( pRecord) { pRecord->Release(); } if (pModRecord) { pModRecord->Release(); } return( rc); } /*************************************************************************** Desc: Add fixups for indexes that are created or indexed during the transaction. ****************************************************************************/ FSTATIC RCODE flmAddIndexFixup( FDB * pDb, FLMUINT uiIndexNum, FLMUINT uiLastDrnIndexed, FLMUINT uiLastContainerIndexed) { RCODE rc = FERR_OK; IXD_FIXUP * pIxdFixup; // See if this index is in our fixup list. pIxdFixup = pDb->pIxdFixups; while (pIxdFixup && pIxdFixup->uiIndexNum != uiIndexNum) { pIxdFixup = pIxdFixup->pNext; } if (!pIxdFixup) { if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( IXD_FIXUP), &pIxdFixup))) { goto Exit; } pIxdFixup->pNext = pDb->pIxdFixups; pDb->pIxdFixups = pIxdFixup; pIxdFixup->uiIndexNum = uiIndexNum; } pIxdFixup->uiLastContainerIndexed = uiLastContainerIndexed; pIxdFixup->uiLastDrnIndexed = uiLastDrnIndexed; Exit: return( rc); } /*************************************************************************** Desc: Set information in the tracker record for the index. ****************************************************************************/ RCODE flmSetIxTrackerInfo( FDB * pDb, FLMUINT uiIndexNum, FLMUINT uiLastContainerIndexed, FLMUINT uiLastDrnIndexed, FLMUINT uiOnlineTransId, FLMBOOL bSuspended) { RCODE rc = FERR_OK; LFILE * pLFile; FlmRecord * pRecord = NULL; FLMUINT32 ui32LastDrnIndexed = (FLMUINT32)uiLastDrnIndexed; FLMUINT32 ui32LastContainerIndexed = (FLMUINT32)uiLastContainerIndexed; FLMUINT32 ui32OnlineTransId = (FLMUINT32)uiOnlineTransId; if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pLFile))) { goto Exit; } if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_TRACKER_CONTAINER, uiIndexNum, TRUE, NULL, pLFile, &pRecord))) { if( rc != FERR_NOT_FOUND ) { goto Exit; } // Create the record for the first time. if( (pRecord = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pRecord->insertLast( 0, FLM_INDEX_TAG, FLM_CONTEXT_TYPE, NULL))) { goto Exit; } } // Make writable. if( pRecord && pRecord->isReadOnly()) { FlmRecord * pTmpRec; if( (pTmpRec = pRecord->copy()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } pRecord->Release(); pRecord = pTmpRec; } if( RC_BAD( rc = flmModField( pRecord, FLM_LAST_DRN_INDEXED_TAG, &ui32LastDrnIndexed, 4, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = flmModField( pRecord, FLM_LAST_CONTAINER_INDEXED_TAG, &ui32LastContainerIndexed, 4, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = flmModField( pRecord, FLM_ONLINE_TRANS_ID_TAG, &ui32OnlineTransId, 4, FLM_NUMBER_TYPE))) { goto Exit; } if( pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_51) { FLMUINT32 ui32IndexSuspended = bSuspended ? 1 : 0; if( RC_BAD( rc = flmModField( pRecord, FLM_STATE_TAG, &ui32IndexSuspended, 4, FLM_NUMBER_TYPE))) { goto Exit; } } // Update the record. if( RC_BAD( rc = FSRecUpdate( pDb, pLFile, pRecord, uiIndexNum, 0))) { goto Exit; } pRecord->setID( uiIndexNum); pRecord->setContainerID( FLM_TRACKER_CONTAINER); if( RC_BAD( rc = flmRcaInsertRec( pDb, FLM_TRACKER_CONTAINER, uiIndexNum, pRecord))) { goto Exit; } // Update the uiLastDrnIndexed and uiLastContainerIndexed // in the IXD structure, but be sure to save // the fields in a FIXUP structure so that the IXD can be // fixed up when we commit. if (RC_BAD( rc = flmAddIndexFixup( pDb, uiIndexNum, uiLastDrnIndexed, uiLastContainerIndexed))) { goto Exit; } Exit: if( pRecord) { pRecord->Release(); } return( rc); } /*************************************************************************** Desc: Get the last drn indexed value in an index tracker record. Returns DRN_LAST_MARKER if the tracker record is not there or the last drn indexed field is not there. ****************************************************************************/ RCODE flmGetIxTrackerInfo( FDB * pDb, FLMUINT uiIndexNum, FLMUINT * puiLastContainerIndexed, FLMUINT * puiLastDrnIndexed, FLMUINT * puiOnlineTransId, FLMBOOL * pbSuspended) { RCODE rc = FERR_OK; FLMUINT uiLastContainerIndexed = 0xFFFFFFFF; FLMUINT uiLastDrnIndexed = DRN_LAST_MARKER; FLMUINT uiOnlineTransId = TRANS_ID_ALWAYS_ONLINE; FLMUINT uiIndexSuspended = 0; LFILE * pLFile; FlmRecord * pRecord = NULL; void * pvField; if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pLFile))) { goto Exit; } if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiIndexNum, &pRecord, NULL, NULL))) { if( rc != FERR_NOT_FOUND) { goto Exit; } rc = FERR_OK; } else { if( (pvField = pRecord->find( pRecord->root(), FLM_LAST_CONTAINER_INDEXED_TAG, 1)) != NULL) { if( RC_BAD( rc = pRecord->getUINT( pvField, &uiLastContainerIndexed))) { goto Exit; } } if( (pvField = pRecord->find( pRecord->root(), FLM_LAST_DRN_INDEXED_TAG, 1)) != NULL) { if( RC_BAD( rc = pRecord->getUINT( pvField, &uiLastDrnIndexed))) { goto Exit; } } if( (pvField = pRecord->find( pRecord->root(), FLM_ONLINE_TRANS_ID_TAG, 1)) != NULL) { if( RC_BAD( rc = pRecord->getUINT( pvField, &uiOnlineTransId))) { goto Exit; } } if( (pvField = pRecord->find( pRecord->root(), FLM_STATE_TAG, 1)) != NULL) { if( RC_BAD( rc = pRecord->getUINT( pvField, &uiIndexSuspended))) { goto Exit; } } } Exit: if( pRecord) { pRecord->Release(); } if( puiLastContainerIndexed) { *puiLastContainerIndexed = uiLastContainerIndexed; } if( puiLastDrnIndexed) { *puiLastDrnIndexed = uiLastDrnIndexed; } if( puiOnlineTransId) { *puiOnlineTransId = uiOnlineTransId; } if( pbSuspended) { *pbSuspended = uiIndexSuspended ? TRUE : FALSE; } return( rc); } /*************************************************************************** Desc: See if any IXD structures need indexing in the background. ****************************************************************************/ RCODE flmStartBackgrndIxThrds( FDB * pDb) { RCODE rc = FERR_OK; FLMBOOL bStartedAutoTrans; FLMUINT uiLoop; IXD * pIxd; bStartedAutoTrans = FALSE; if( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, &bStartedAutoTrans))) { goto Exit; } for( uiLoop = 0, pIxd = pDb->pDict->pIxdTbl; uiLoop < pDb->pDict->uiIxdCnt; uiLoop++, pIxd++) { // Restart any indexes that are off-line but not suspended if( (pIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) == IXD_OFFLINE) { flmAssert( flmBackgroundIndexGet( pDb->pFile, pIxd->uiIndexNum, FALSE) == NULL); if( RC_BAD( rc = flmStartIndexBuild( pDb, pIxd->uiIndexNum))) { goto Exit; } } } Exit: if( bStartedAutoTrans) { (void)flmAbortDbTrans( pDb); } fdbExit( pDb); return( rc); } /*************************************************************************** Desc: Frees blocks of a container ****************************************************************************/ FSTATIC RCODE flmFreeLFileBlocks( FDB * pDb, LFILE * pLFile) { RCODE rc = FERR_OK; BTSK * pStack; BTSK * pOrigStack; SCACHE * pSCache = NULL; FLMUINT uiBlkAddr; BTSK stackBuf[ BH_MAX_LEVELS]; FLMBYTE ucKeyBuf[ MAX_KEY_SIZ]; FLMUINT uiBlocksDeleted = 0; FLMUINT uiBlockSize = pDb->pFile->FileHdr.uiBlockSize; FLMUINT uiLoopCounter = 0; FSInitStackCache( &stackBuf[ 0], BH_MAX_LEVELS); pStack = pOrigStack = stackBuf; pStack->pKeyBuf = ucKeyBuf; ucKeyBuf[ 0] = 0; if( RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, ucKeyBuf, 1, 0))) { goto Exit; } // After calling FSBtSearch, the bottom block in the stack will still be // held. We release it here, because it will be read again in the // loops below. FSReleaseBlock( pStack, FALSE); // Only need to delete blocks if there is something in the tree if( pStack->uiCmpStatus != BT_END_OF_DATA) { // Stack points to lowest leaf element (not root) // Increment for do-while loop and then post decrement. // This is a tricky algorithm! pStack++; do { pStack--; uiBlkAddr = pStack->uiBlkAddr; do { f_yieldCPU(); // Read block so we can free it if( RC_BAD(rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pSCache))) { goto Exit; } // Get next block address before blkFree creams it out uiBlkAddr = FB2UD( &pSCache->pucBlk[ BH_NEXT_BLK]); // The call to FSBlockFree also releases the cache whether // it succeeds or not. That is why we set pSCache to NULL after // calling it. rc = FSBlockFree( pDb, pSCache); pSCache = NULL; if( RC_BAD( rc)) { goto Exit; } // See if we should do a callback uiBlocksDeleted++; uiLoopCounter++; if( pDb->fnStatus && uiLoopCounter == 50) { uiLoopCounter = 0; if( RC_BAD( rc = pDb->fnStatus( FLM_DELETING_STATUS, (void *)uiBlocksDeleted, (void *)uiBlockSize, pDb->StatusData))) { goto Exit; } } } while( uiBlkAddr != BT_END); } while( pStack != pOrigStack); } Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Removes all keys in the index that refer to the specified container. ****************************************************************************/ RCODE flmRemoveContainerKeys( FDB * pDb, FLMUINT uiIndexNum, FLMUINT uiContainerNum) { RCODE rc = FERR_OK; BTSK stack[ BH_MAX_LEVELS]; BTSK * pStack; FLMBYTE * pucKeyBufs = NULL; LFILE * pIxLFile; FLMUINT uiLoopCounter = 0; FLMUINT uiElementsTraversed = 0; FLMUINT uiContainerPartLen; IXD * pIxd; FSInitStackCache( &stack[0], BH_MAX_LEVELS); if( RC_BAD( rc = f_alloc( MAX_KEY_SIZ * 2, &pucKeyBufs))) { goto Exit; } // Get the index LFILE. if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, &pIxLFile, &pIxd, TRUE))) { goto Exit; } pStack = stack; pStack->pKeyBuf = &pucKeyBufs [MAX_KEY_SIZ]; pucKeyBufs [0] = 0; if (RC_BAD( rc = FSBtSearch( pDb, pIxLFile, &pStack, pucKeyBufs, 1, 0))) { goto Exit; } // If the B-Tree was empty, we are done. if( pStack->uiBlkAddr == BT_END) { goto Exit; // Should return FERR_OK } uiContainerPartLen = getIxContainerPartLen( pIxd); // Traverse through the entire index, deleting elements that have this // container in their key. for (;;) { // Key must have at least the number of bytes for the container // at the end of the key. if (pStack->uiKeyLen <= uiContainerPartLen) { flmAssert( 0); rc = RC_SET( FERR_BTREE_ERROR); goto Exit; } if( uiContainerNum == getContainerFromKey( pStack->pKeyBuf, pStack->uiKeyLen)) { FLMBYTE * pucCurElm; if (RC_BAD( rc = FSBtDelete( pDb, pIxLFile, &pStack))) { goto Exit; } // Delete all continuation elements as well. pucCurElm = CURRENT_ELM( pStack); while (!BBE_IS_FIRST( pucCurElm)) { if (RC_BAD( rc = FSBtDelete( pDb, pIxLFile, &pStack))) { goto Exit; } pucCurElm = CURRENT_ELM( pStack); uiElementsTraversed++; } // See if we are at the end of the index. if (pStack->uiBlkAddr == BT_END) { goto Exit; // Should return FERR_OK } // Scan to current position to rebuild the key. if (RC_BAD(rc = FSBtScanTo( pStack, NULL, 0, 0))) { goto Exit; } } else { // Go to the next element in the B-Tree. if( RC_BAD( rc = FSBtNextElm( pDb, pIxLFile, pStack))) { if( rc != FERR_BT_END_OF_DATA) { goto Exit; } // rc was FERR_BT_END_OF_DATA, we are at end of index. rc = FERR_OK; break; } } uiElementsTraversed++; uiLoopCounter++; if (pDb->fnStatus && uiLoopCounter == 50) { uiLoopCounter = 0; if (RC_BAD( rc = pDb->fnStatus( FLM_DELETING_KEYS, (void *)uiIndexNum, (void *)uiElementsTraversed, pDb->StatusData))) { goto Exit; } } } Exit: FSReleaseStackCache( stack, BH_MAX_LEVELS, FALSE); if (pucKeyBufs) { f_free( &pucKeyBufs); } return( rc); } /*************************************************************************** Desc: Remove indexes associated with a container. Also, for indexes that span all containers, remove any keys/references in the index that refer to this container. ****************************************************************************/ FSTATIC RCODE fdictRemoveIndexes( FDB * pDb, FLMUINT uiContainer) { RCODE rc = FERR_OK; IXD * pIxd; FLMUINT uiCnt; // Go through all of the IXDs for( pIxd = pDb->pDict->pIxdTbl, uiCnt = 0; uiCnt < pDb->pDict->uiIxdCnt; uiCnt++, pIxd++) { if( pIxd->uiContainerNum == uiContainer) { // Index is on this container, remove the index. // NOTE: For now, this case is not allowed. Indexes on a container // must be deleted prior to deleting the container. rc = RC_SET( FERR_MUST_DELETE_INDEXES); goto Exit; } else if( !pIxd->uiContainerNum) { // Index is on all containers, must traverse through the index and // remove all keys that refer to this container. if( RC_BAD( rc = flmRemoveContainerKeys( pDb, pIxd->uiIndexNum, uiContainer))) { goto Exit; } } } Exit: return( rc); } /**************************************************************************** Desc: Check that the field type is not changed. ****************************************************************************/ FSTATIC RCODE DDVerifyModField( FDB * pDb, FlmRecord * pOldRecord, FlmRecord * pNewRecord ) { RCODE rc = FERR_OK; FLMUINT uiNewFldInfo, uiOldFldInfo; void * pvField; char szNativeBuf1[ 32]; char szNativeBuf2[ 32]; FLMUINT uiLen; // Fields must have the same type. May only change the field name. // Set Default in case no type. uiNewFldInfo = uiOldFldInfo = FLM_CONTEXT_TYPE; if( (pvField = pNewRecord->find( pNewRecord->root(), FLM_TYPE_TAG)) != NULL) { if( RC_BAD(rc = DDGetFieldType( pNewRecord, pvField, &uiNewFldInfo))) { goto Exit; } } if( (pvField = pOldRecord->find( pOldRecord->root(), FLM_TYPE_TAG)) != NULL) { if( RC_BAD(rc = DDGetFieldType( pOldRecord, pvField, &uiOldFldInfo))) { goto Exit; } if( uiNewFldInfo != uiOldFldInfo) { rc = RC_SET( FERR_CANNOT_MOD_FIELD_TYPE); goto Exit; } } else { rc = RC_SET( FERR_MISSING_FIELD_TYPE); goto Exit; } // Determine if the field state is changing // 1) The new record must have a state value of 'checking' or 'purge' // 2) or a state of 'unused' (FlmDbSweep or recover only) if( !pDb->bFldStateUpdOk) { szNativeBuf1[ 0] = 0; if( (pvField = pOldRecord->find( pOldRecord->root(), FLM_STATE_TAG)) != NULL) { uiLen = sizeof( szNativeBuf1); if( RC_BAD( rc = pOldRecord->getNative( pvField, szNativeBuf1, &uiLen))) { goto Exit; } } szNativeBuf2[ 0] = 0; if( (pvField = pNewRecord->find( pNewRecord->root(), FLM_STATE_TAG)) != NULL) { uiLen = sizeof( szNativeBuf2); if( RC_BAD( rc = pNewRecord->getNative( pvField, szNativeBuf2, &uiLen))) { goto Exit; } } if( f_strnicmp( szNativeBuf1, szNativeBuf2, 4) != 0 && f_strnicmp( szNativeBuf2, "chec", 4) != 0 && f_strnicmp( szNativeBuf2, "purg", 4) != 0) { rc = RC_SET( FERR_CANNOT_MOD_FIELD_STATE); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Check that the encryption type and key have not changed. ****************************************************************************/ FSTATIC RCODE DDVerifyModEncDef( FDB * pDb, FlmRecord * pOldRecord, FlmRecord * pNewRecord ) { RCODE rc = FERR_OK; void * pvOldField; void * pvNewField; FLMUINT uiOldEncType; FLMUINT uiNewEncType; char szNativeBuf1[ 32]; char szNativeBuf2[ 32]; FLMUINT uiLen; FLMUINT uiOldKeyLen; FLMUINT uiNewKeyLen; if ((pvOldField = pOldRecord->find( pOldRecord->root(), FLM_TYPE_TAG)) == NULL) { rc = RC_SET( FERR_MISSING_ENC_TYPE); goto Exit; } // Verify that the encryption algorithm does not change and that // the key is not being changed. if (RC_BAD( rc = DDGetEncType( pOldRecord, pvOldField, &uiOldEncType))) { goto Exit; } if( (pvNewField = pNewRecord->find( pNewRecord->root(), FLM_TYPE_TAG)) == NULL) { rc = RC_SET( FERR_MISSING_ENC_TYPE); goto Exit; } if (RC_BAD( rc = DDGetEncType( pNewRecord, pvNewField, &uiNewEncType))) { goto Exit; } // Make sure we are not trying to change the encryption type. if (uiNewEncType != uiOldEncType) { rc = RC_SET( FERR_CANNOT_MOD_ENC_TYPE); goto Exit; } // Make sure the key is not being modified if( (pvOldField = pOldRecord->find( pOldRecord->root(), FLM_KEY_TAG)) == NULL) { rc = RC_SET( FERR_MISSING_ENC_KEY); goto Exit; } if ((pvNewField = pNewRecord->find( pNewRecord->root(), FLM_KEY_TAG)) == NULL) { rc = RC_SET( FERR_MISSING_ENC_KEY); goto Exit; } uiOldKeyLen = pOldRecord->getDataLength( pvOldField); uiNewKeyLen = pNewRecord->getDataLength( pvNewField); if (uiOldKeyLen != uiNewKeyLen) { rc = RC_SET( FERR_CANNOT_CHANGE_KEY); goto Exit; } if (uiOldKeyLen == 0) { flmAssert( 0); rc = RC_SET( FERR_BAD_ENC_KEY); goto Exit; } if (f_memcmp(pOldRecord->getDataPtr(pvOldField), pNewRecord->getDataPtr(pvNewField), uiNewKeyLen) != 0) { rc = RC_SET( FERR_CANNOT_CHANGE_KEY); goto Exit; } /* Determine if the record state is changing 1) The new record must have a state value of 'checking' or 'purge' 2) or a state of 'unused' (FlmDbSweep or recover only) */ if( !pDb->bFldStateUpdOk) { szNativeBuf1[ 0] = 0; if( (pvOldField = pOldRecord->find( pOldRecord->root(), FLM_STATE_TAG)) != NULL) { uiLen = sizeof( szNativeBuf1); if( RC_BAD( rc = pOldRecord->getNative( pvOldField, szNativeBuf1, &uiLen))) { goto Exit; } } szNativeBuf2[ 0] = 0; if( (pvNewField = pNewRecord->find( pNewRecord->root(), FLM_STATE_TAG)) != NULL) { uiLen = sizeof( szNativeBuf2); if( RC_BAD( rc = pNewRecord->getNative( pvNewField, szNativeBuf2, &uiLen))) { goto Exit; } } if( f_strnicmp( szNativeBuf1, szNativeBuf2, 4) != 0 && f_strnicmp( szNativeBuf2, "chec", 4) != 0 && f_strnicmp( szNativeBuf2, "purg", 4) != 0) { rc = RC_SET( FERR_CANNOT_MOD_ENC_STATE); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ FSTATIC RCODE flmFreeContainerBlocks( FDB * pDb, LFILE * pLFile) { RCODE rc = FERR_OK; LFILE tmpLFile; flmAssert( pLFile->uiLfType == LF_CONTAINER); if( RC_BAD( rc = flmFreeLFileBlocks( pDb, pLFile))) { goto Exit; } f_memcpy( &tmpLFile, pLFile, sizeof( LFILE)); tmpLFile.uiLfType = LF_INVALID; // Update the LFile if( RC_BAD( rc = flmLFileWrite( pDb, &tmpLFile))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Frees up all index blocks ****************************************************************************/ FSTATIC RCODE flmFreeIndexBlocks( FDB * pDb, LFILE * pLFile, FLMBOOL bInvalidateLFile) { RCODE rc = FERR_OK; BTSK stackBuf[ BH_MAX_LEVELS]; SCACHE * pSCache = NULL; FLMUINT uiDrn; FlmRecord * pRec = NULL; void * pvFld; FFILE * pFile = pDb->pFile; LFILE * pTrackerLFile; LFILE tmpLFile; char szTmpBuf[ 32]; flmAssert( pLFile->uiLfType == LF_INDEX); FSInitStackCache( &stackBuf[ 0], BH_MAX_LEVELS); // Delete the index tracker record, if any if( RC_BAD( rc = flmDeleteTrackerRec( pDb, pLFile->uiLfNum))) { goto Exit; } if( pFile->FileHdr.uiVersionNum <= FLM_VER_4_51) { // Background deletion is not supported. Must delete // the blocks of the LFILE now. if( RC_BAD( rc = flmFreeLFileBlocks( pDb, pLFile))) { goto Exit; } } else { BTSK * pStack; BTSK * pOrigStack; FLMBYTE ucKeyBuf[ MAX_KEY_SIZ]; // Delete the index in the background pStack = pOrigStack = stackBuf; pStack->pKeyBuf = ucKeyBuf; ucKeyBuf[ 0] = 0; if( RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, ucKeyBuf, 1, 0))) { goto Exit; } // After calling FSBtSearch, the bottom block in the stack // will still be held. We release it here. FSReleaseBlock( pStack, FALSE); // If the tree is empty, we don't need to delete anything if( pStack->uiCmpStatus != BT_END_OF_DATA) { // Build the tracker record for the background deletion // thread and signal the thread that it has work to do. if( (pRec = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pRec->insertLast( 0, FLM_DELETE_TAG, FLM_TEXT_TYPE, NULL))) { goto Exit; } pStack++; do { pStack--; if( RC_BAD( rc = pRec->insertLast( 1, FLM_BLOCK_CHAIN_TAG, FLM_TEXT_TYPE, &pvFld))) { goto Exit; } f_sprintf( szTmpBuf, "%u", (unsigned)pStack->uiBlkAddr); if( RC_BAD( rc = pRec->setNative( pvFld, szTmpBuf))) { goto Exit; } } while( pStack != pOrigStack); // Get the tracker LFILE if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pTrackerLFile))) { goto Exit; } // Find an available DRN in the tracker container uiDrn = 0; if( RC_BAD( rc = FSGetNextDrn( pDb, pTrackerLFile, FALSE, &uiDrn))) { goto Exit; } if( uiDrn <= 0x0000FFFF) { // Skip past the reserved and unregistered ID range uiDrn = 0x00010000; } pRec->setID( uiDrn); pRec->setContainerID( FLM_TRACKER_CONTAINER); // Add the record to the tracker container if( RC_BAD( rc = FSRecUpdate( pDb, pTrackerLFile, pRec, uiDrn, 0))) { goto Exit; } if( RC_BAD( rc = flmRcaInsertRec( pDb, FLM_TRACKER_CONTAINER, uiDrn, pRec))) { goto Exit; } pRec->Release(); pRec = NULL; // Signal the maintenance thread that it has work to do f_semSignal( pFile->hMaintSem); } } f_memcpy( &tmpLFile, pLFile, sizeof( LFILE)); if( bInvalidateLFile) { tmpLFile.uiLfType = LF_INVALID; } else { tmpLFile.uiRootBlk = BT_END; } // Update the LFile if( RC_BAD( rc = flmLFileWrite( pDb, &tmpLFile))) { goto Exit; } Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); if( pSCache) { ScaReleaseCache( pSCache, FALSE); } if( pRec) { pRec->Release(); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ FSTATIC RCODE flmFreeBlockChain( FDB * pDb, FLMUINT uiStartAddr, FLMUINT * puiCount, FLMUINT * puiEndAddr) { RCODE rc = FERR_OK; SCACHE * pSCache = NULL; FLMUINT uiBlkAddr = uiStartAddr; FLMUINT uiBlocksFreed = 0; FLMUINT uiNumToDelete = *puiCount; // Make sure an update transaction is going if( flmGetDbTransType( pDb) != FLM_UPDATE_TRANS) { flmAssert( 0); rc = RC_SET( FERR_ILLEGAL_TRANS_OP); goto Exit; } // Free blocks in the chain until we hit the end or meet // our quota. while( uiBlkAddr != BT_END && uiBlocksFreed < uiNumToDelete) { if( RC_BAD(rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiBlkAddr, NULL, &pSCache))) { goto Exit; } // Get next block address before blkFree creams it out uiBlkAddr = FB2UD( &pSCache->pucBlk[ BH_NEXT_BLK]); // The call to FSBlockFree also releases the cache whether // it succeeds or not. That is why we set pSCache to NULL after // calling it. rc = FSBlockFree( pDb, pSCache); pSCache = NULL; if( RC_BAD( rc)) { goto Exit; } uiBlocksFreed++; } *puiEndAddr = uiBlkAddr; *puiCount = uiBlocksFreed; Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE flmMaintFreeBlockChain( FDB * pDb, FLMUINT uiTrackerDrn, FLMUINT uiBlocksToDelete, FLMUINT uiExpectedEndAddr, FLMUINT64 * pui64BlocksFreed) { RCODE rc = FERR_OK; FLMUINT uiStartAddr; FLMUINT uiEndAddr = 0; void * pvBlkChain; FlmRecord * pTrackerRec = NULL; FlmRecord * pTmpRec; FLMUINT uiBlocksFreed = 0; FLMUINT uiTmpCount; FLMBOOL bUpdateTrackerRec = FALSE; // Make sure an update transaction is going and that a // non-zero number of blocks was specified if( flmGetDbTransType( pDb) != FLM_UPDATE_TRANS || !uiBlocksToDelete) { flmAssert( 0); rc = RC_SET( FERR_ILLEGAL_TRANS_OP); goto Exit; } // Retrieve the record if( RC_BAD( rc = flmRetrieveTrackerRec( pDb, uiTrackerDrn, TRUE, &pTrackerRec))) { flmAssert( 0); goto Exit; } // Make a writeable copy of the record if( pTrackerRec->isReadOnly()) { if( (pTmpRec = pTrackerRec->copy()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } pTrackerRec->Release(); pTrackerRec = pTmpRec; pTmpRec = NULL; } while( uiBlocksFreed < uiBlocksToDelete) { // Process the block chain(s) if( (pvBlkChain = pTrackerRec->find( pTrackerRec->root(), FLM_BLOCK_CHAIN_TAG)) == NULL) { if( RC_BAD( rc = flmDeleteTrackerRec( pDb, pTrackerRec->getID()))) { goto Exit; } bUpdateTrackerRec = FALSE; break; } if( RC_BAD( rc = pTrackerRec->getUINT( pvBlkChain, &uiStartAddr))) { goto Exit; } uiTmpCount = uiBlocksToDelete - uiBlocksFreed; if( RC_BAD( rc = flmFreeBlockChain( pDb, uiStartAddr, &uiTmpCount, &uiEndAddr))) { goto Exit; } uiBlocksFreed += uiTmpCount; flmAssert( uiBlocksFreed <= uiBlocksToDelete); if( uiEndAddr == BT_END) { // If we hit the end of the block chain, remove // it from the tracker record pTrackerRec->remove( pvBlkChain); bUpdateTrackerRec = TRUE; } else { // More work to do on the current block chain. Update // the tracker. if( RC_BAD( rc = pTrackerRec->setUINT( pvBlkChain, uiEndAddr))) { goto Exit; } bUpdateTrackerRec = TRUE; } } if( bUpdateTrackerRec) { if( RC_BAD( rc = flmModifyTrackerRec( pDb, pTrackerRec->getID(), pTrackerRec))) { goto Exit; } } if( RC_BAD( rc = pDb->pFile->pRfl->logBlockChainFree( uiTrackerDrn, uiBlocksFreed, uiEndAddr))) { goto Exit; } if( uiExpectedEndAddr) { if( uiBlocksToDelete != uiBlocksFreed || uiEndAddr != uiExpectedEndAddr) { flmAssert( 0); rc = RC_SET( FERR_BAD_RFL_PACKET); goto Exit; } } Exit: if( pui64BlocksFreed) { *pui64BlocksFreed += uiBlocksFreed; } if( pTrackerRec) { pTrackerRec->Release(); } return( rc); } /*************************************************************************** Desc: This routine begins a thread that will perform misc. background work assigned to the tracker. *****************************************************************************/ RCODE flmStartMaintThread( FFILE * pFile) { RCODE rc = FERR_OK; char szThreadName[ F_PATH_MAX_SIZE]; char szBaseName[ F_FILENAME_SIZE]; flmAssert( !pFile->pMaintThrd); // Generate the thread name if( RC_BAD( rc = f_pathReduce( pFile->pszDbPath, szThreadName, szBaseName))) { goto Exit; } f_sprintf( szThreadName, "Maintenance (%s)", szBaseName); // Initialize the tracker status structure f_memset( &pFile->maintStatus, 0, sizeof( FMAINT_STATUS)); // Start the tracker thread. if( RC_BAD( rc = f_threadCreate( &pFile->pMaintThrd, flmMaintThread, szThreadName, FLM_DEFAULT_THREAD_GROUP, 0, pFile, NULL, 32000))) { goto Exit; } // Signal the thread so that it will look for any // work that may already be waiting f_semSignal( pFile->hMaintSem); Exit: if( RC_BAD( rc)) { if( pFile->pMaintThrd) { pFile->pMaintThrd->Release(); pFile->pMaintThrd = NULL; } } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ FSTATIC RCODE flmRetrieveTrackerRec( FDB * pDb, FLMUINT uiDrn, FLMBOOL bExact, FlmRecord ** ppRecord) { RCODE rc = FERR_OK; POOL readPool; LFILE * pTrackerLFile; BTSK stackBuf[ BH_MAX_LEVELS]; BTSK_p pStack = &stackBuf[ 0]; FLMBYTE ucKeyBuf[ DIN_KEY_SIZ]; FLMBYTE ucSearchKey[ DIN_KEY_SIZ]; FlmRecord * pTrackerRec = NULL; FLMUINT uiFoundDrn; FSInitStackCache( &stackBuf[ 0], BH_MAX_LEVELS); GedPoolInit( &readPool, 8192); // Retrieve a tracker record for processing if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pTrackerLFile))) { goto Exit; } pStack->pKeyBuf = ucKeyBuf; longToByte( uiDrn, ucSearchKey); if( RC_BAD( rc = FSBtSearch( pDb, pTrackerLFile, &pStack, ucSearchKey, 4, 0))) { goto Exit; } if( pStack->uiCmpStatus == BT_END_OF_DATA || pStack->uiBlkAddr == BT_END) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } pStack->uiFlags = NO_STACK; // Stack points to leaf element if( (uiFoundDrn = byteToLong( ucKeyBuf)) == DRN_LAST_MARKER) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } if( bExact) { if( uiFoundDrn != uiDrn) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } } else { uiDrn = uiFoundDrn; } if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_TRACKER_CONTAINER, uiDrn, FALSE, NULL, NULL, &pTrackerRec))) { if( rc != FERR_NOT_FOUND) { goto Exit; } if( RC_BAD( rc = FSReadElement( pDb, &readPool, pTrackerLFile, uiDrn, pStack, FALSE, &pTrackerRec, NULL, NULL))) { goto Exit; } } if( *ppRecord) { (*ppRecord)->Release(); } *ppRecord = pTrackerRec; pTrackerRec = NULL; Exit: if( pTrackerRec) { pTrackerRec->Release(); } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); GedPoolFree( &readPool); return( rc); } /*************************************************************************** Desc: *****************************************************************************/ FSTATIC RCODE flmDeleteTrackerRec( FDB * pDb, FLMUINT uiDrn) { RCODE rc = FERR_OK; LFILE * pTrackerLFile; if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pTrackerLFile))) { goto Exit; } if( RC_BAD( rc = FSRecUpdate( pDb, pTrackerLFile, NULL, uiDrn, 0))) { if( rc != FERR_NOT_FOUND) { goto Exit; } rc = FERR_OK; } if( RC_BAD( rc = flmRcaRemoveRec( pDb, FLM_TRACKER_CONTAINER, uiDrn))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ FSTATIC RCODE flmModifyTrackerRec( FDB * pDb, FLMUINT uiDrn, FlmRecord * pRecord) { RCODE rc = FERR_OK; LFILE * pLFile; if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pLFile))) { goto Exit; } if( RC_BAD( rc = FSRecUpdate( pDb, pLFile, pRecord, uiDrn, 0))) { goto Exit; } pRecord->setID( uiDrn); pRecord->setContainerID( FLM_TRACKER_CONTAINER); if( RC_BAD( rc = flmRcaInsertRec( pDb, FLM_TRACKER_CONTAINER, uiDrn, pRecord))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ FSTATIC RCODE flmMaintThread( F_Thread * pThread) { RCODE rc = FERR_OK; FLMBOOL bStartedTrans = FALSE; FLMBOOL bDbInitialized = FALSE; FFILE * pFile = (FFILE *)pThread->getParm1(); LFILE * pTrackerLFile; FDB * pDb = NULL; FLMUINT uiLastDrn; FlmRecord * pTrackerRec = NULL; FMAINT_STATUS * pStatus = NULL; pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); if( pThread->getShutdownFlag()) { goto Exit; } if( RC_BAD( rc = flmOpenFile( pFile, NULL, NULL, NULL, 0, TRUE, NULL, NULL, pFile->pszDbPassword, &pDb))) { // If the file is being closed, this is not an error. if( pFile->uiFlags & DBF_BEING_CLOSED) { rc = FERR_OK; } goto Exit; } bDbInitialized = TRUE; if( RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, 0, 0, &bStartedTrans))) { goto Exit; } pStatus = &pFile->maintStatus; for(;;) { // Set the thread's status pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); // Set the tracker status pStatus->eDoing = FLM_MAINT_IDLE; // Wait for work to become available if( RC_BAD( rc = f_semWait( pFile->hMaintSem, F_SEM_WAITFOREVER))) { goto Exit; } uiLastDrn = 0x00010000; for( ;;) { // See if we should shut down. if( pThread->getShutdownFlag()) { goto Exit; } // Obtain the file lock pStatus->eDoing = FLM_MAINT_WAITING_FOR_LOCK; flmAssert( !(pDb->uiFlags & FDB_HAS_FILE_LOCK)); if( RC_BAD( rc = pDb->pFile->pFileLockObj->Lock( TRUE, pDb, FALSE, TRUE, FLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY, pDb->pDbStats))) { 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()); rc = FERR_OK; } 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( TRUE, pDb))) { 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()); rc = FERR_OK; } goto Exit; } bStartedTrans = TRUE; if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pTrackerLFile))) { goto Exit; } pStatus->eDoing = FLM_MAINT_LOOKING_FOR_WORK; // Get a tracker record to process if( RC_BAD( rc = flmRetrieveTrackerRec( pDb, uiLastDrn, FALSE, &pTrackerRec))) { if( rc != FERR_EOF_HIT) { goto Exit; } rc = FERR_OK; pThread->setThreadStatus( FLM_THREAD_STATUS_ABORTING_TRANS); (void)flmAbortDbTrans( pDb); bStartedTrans = FALSE; break; } else { uiLastDrn = pTrackerRec->getID(); switch( pTrackerRec->getFieldID( pTrackerRec->root())) { case FLM_DELETE_TAG: { pStatus->eDoing = FLM_MAINT_FREEING_BLOCKS; if( RC_BAD( rc = flmMaintFreeBlockChain( pDb, pTrackerRec->getID(), 25, 0, &pDb->pFile->maintStatus.ui64BlocksFreed))) { goto Exit; } break; } default: { // Don't know what to do with this record, so we // will skip it uiLastDrn++; break; } } pThread->setThreadStatus( FLM_THREAD_STATUS_COMMITTING_TRANS); // Commit the transaction pStatus->eDoing = FLM_MAINT_ENDING_TRANS; if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { goto Exit; } bStartedTrans = FALSE; } } } Exit: pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); pFile->maintStatus.eDoing = FLM_MAINT_TERMINATED; if( pTrackerRec) { pTrackerRec->Release(); } if( bStartedTrans) { (void)flmAbortDbTrans( pDb); bStartedTrans = FALSE; } if( pDb && pDb->uiFlags & FDB_HAS_FILE_LOCK) { (void)pDb->pFile->pFileLockObj->Unlock( TRUE, pDb); pDb->uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); } if( bDbInitialized) { fdbExit( pDb); bDbInitialized = FALSE; } if( pDb) { (void)FlmDbClose( (HFDB *)&pDb); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE FlmMaintenanceStatus( HFDB hDb, FMAINT_STATUS * pMaintStatus) { RCODE rc = FERR_OK; FLMBOOL bMutexLocked = FALSE; FDB * pDb = NULL; pDb = (FDB *)hDb; if( RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, NULL))) { goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; f_memcpy( pMaintStatus, &pDb->pFile->maintStatus, sizeof( FMAINT_STATUS)); Exit: if( bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } if( pDb) { fdbExit( pDb); } return( rc); }