git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
3143 lines
67 KiB
C++
3143 lines
67 KiB
C++
//-------------------------------------------------------------------------
|
|
// 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);
|
|
}
|