Files
mars-flaim/flaim/src/flsweep.cpp
ahodgkinson 7de8b6be39 Ported FLAIM to FTK.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@509 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-06-05 22:59:36 +00:00

1032 lines
23 KiB
C++

//-------------------------------------------------------------------------
// Desc: Sweep database to check field usage.
// Tabs: 3
//
// Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: flsweep.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
/****************************************************************************
Desc:
****************************************************************************/
class DbDict : public F_Object
{
public:
DbDict()
{
m_puiStateTbl = NULL;
};
~DbDict();
RCODE Init(
FDB * pDb,
FLMUINT uiMode,
FLMBOOL * pbFoundPurgeField);
FINLINE FLMUINT GetState(
FLMUINT uiFieldID)
{
if( uiFieldID > m_uiTblSize)
{
return( 0);
}
else
{
return( m_puiStateTbl[ uiFieldID]);
}
}
RCODE ChangeState(
FLMUINT uiFieldID,
FLMUINT uiNewState);
RCODE Finish( void);
private:
FDB * m_pDb;
FLMUINT * m_puiStateTbl;
FLMUINT m_uiTblSize;
};
/****************************************************************************
Desc:
****************************************************************************/
class DbWalk : public F_Object
{
public:
SWEEP_INFO m_SwpInfo;
DbWalk()
{
f_memset( &m_SwpInfo, 0, sizeof( SWEEP_INFO));
m_pDb = NULL;
m_uiCallbackFreq = 0;
m_fnStatusHook = NULL;
m_UserData = 0;
m_uiNextLFile = 0;
m_uiRecsRead = 0;
m_uiLastDrn = 0;
}
~DbWalk()
{
}
FINLINE void Init(
FDB * pDb,
FLMUINT uiCallbackFreq,
STATUS_HOOK fnStatusHook,
void * UserData)
{
m_pDb = pDb;
m_uiCallbackFreq = uiCallbackFreq;
m_fnStatusHook = fnStatusHook;
m_UserData = UserData;
}
RCODE NextContainer(
FLMUINT * puiContainer);
RCODE NextRecord(
FlmRecord ** ppRecord);
RCODE UpdateRecord(
FLMUINT uiDrn,
FlmRecord * pRecord);
private:
FDB * m_pDb;
FLMUINT m_uiCallbackFreq;
STATUS_HOOK m_fnStatusHook;
void * m_UserData;
FLMUINT m_uiNextLFile;
FLMUINT m_uiRecsRead;
FLMUINT m_uiLastDrn;
};
/****************************************************************************
Desc: Provides the ability to scan a FLAIM database for the purpose of
performing maintenance activities and collecting statistics.
Notes: During a database sweep, the user may perform one or all of the
following:
1) Check field and record template usage: When FlmDbSweep finds
occurrences of fields or records that have a status of 'checking',
their status will be changed to 'active'. If no occurence of a
particular field/template is found during the database sweep, the
status of the item will be change from 'checking' to 'unused'.
Note: An 'unused' status associated with a field indicates that no
occurances of the field were found within any data records. The field
may still be referenced from an index or record template definition.
It is the user's responsibility to remove any dictionary references
to the 'unused' item before attempting to delete it.
2) Purge field and record templates from a database: This option
will remove any occurances of fields or records that have a 'purge'
status. Before removing occurances, the dictionary is checked to
verify that no other dictionary definitions reference the item to be
purged. If references are found, an error is returned. Otherwise,
'purged' items will be deleted.
3) Visit database items: This option allows the user to visit
all items within the database and gather statistics about the
database and it's contents.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbSweep(
HFDB hDb,
FLMUINT uiSweepMode,
FLMUINT uiCallbackFreq,
STATUS_HOOK fnStatusHook,
void * UserData)
{
RCODE rc = FERR_OK;
DbDict * pDbDict = NULL;
DbWalk * pDbWalk = NULL;
FlmRecord * pRecord = NULL;
FLMUINT uiContainer;
FLMUINT uiState;
SWEEP_INFO SwpInfo;
FLMBOOL bStartedTrans = FALSE;
FDB * pDb = (FDB *) hDb;
FLMUINT uiEncState;
if( IsInCSMode( pDb))
{
fdbInitCS( pDb);
rc = RC_SET( FERR_NOT_IMPLEMENTED);
goto ExitCS;
}
if( ( pDbWalk = f_new DbWalk) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
f_memset( &SwpInfo, 0, sizeof( SWEEP_INFO));
if( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS | FLM_DONT_POISON_CACHE,
0, 0, &bStartedTrans)))
{
goto Exit;
}
pDbWalk->Init( pDb, uiCallbackFreq, fnStatusHook, UserData);
SwpInfo.hDb = pDbWalk->m_SwpInfo.hDb = hDb;
/* Only initialize a DbDict if needed */
if( (uiSweepMode & SWEEP_CHECKING_FLDS) ||
(uiSweepMode & SWEEP_PURGED_FLDS))
{
FLMBOOL bFoundPurgeField;
if( ( pDbDict = f_new DbDict) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pDbDict->Init( pDb, uiSweepMode, &bFoundPurgeField)))
{
goto Exit;
}
// If user is performing purge field sweep and dictionary contains no
// purged fields then just return.
if( uiSweepMode == SWEEP_PURGED_FLDS && !bFoundPurgeField)
{
goto Exit;
}
}
for (;;)
{
// Get the next container
if (RC_BAD( rc = pDbWalk->NextContainer( &uiContainer)))
{
if (rc == FERR_EOF_HIT)
{
// No more containers to process.
rc = FERR_OK;
break;
}
else
{
goto Exit;
}
}
if (!pDbDict &&
!(uiCallbackFreq & EACH_RECORD || uiCallbackFreq & EACH_FIELD))
{
// User is performing a status sweep and they are not visiting
// each record or field. Go to next container.
continue;
}
SwpInfo.uiContainer = uiContainer;
// Visit each record in the container.
for (;;)
{
FLMBOOL bRecChanged;
void * pvField;
void * pvPrevField;
FLMUINT uiDrn;
if (RC_BAD( rc = pDbWalk->NextRecord( &pRecord)))
{
if (rc == FERR_EOF_HIT)
{
rc = FERR_OK;
break;
}
else
{
goto Exit;
}
}
bRecChanged = FALSE;
uiDrn = pRecord->getID();
SwpInfo.uiRecId = uiDrn;
for( pvField = pRecord->root(); pvField;
pvField = pRecord->next( pvField))
{
SwpInfo.pRecord = pRecord;
SwpInfo.pvField = pvField;
// call the field call-back here..
if ((uiCallbackFreq & EACH_FIELD) && fnStatusHook)
{
if (RC_BAD( rc = (fnStatusHook)( FLM_SWEEP_STATUS,
(void *)&SwpInfo,
(void *)EACH_FIELD,
UserData)))
{
if (rc == FERR_EOF_HIT)
{
// User returned FERR_EOF_HIT, means skip to next record.
rc = FERR_OK;
break;
}
else
{
goto Exit;
}
}
}
// Continue to next field if no DbDict object.
if (! pDbDict)
{
continue;
}
uiState = pDbDict->GetState( pRecord->getFieldID( pvField));
// If the field is encrypted, we need to check the state of the
// encryption definition record as well, since it is being
// referenced by this field.
if (pRecord->isEncryptedField( pvField))
{
uiEncState = pDbDict->GetState( pRecord->getEncryptionID( pvField));
}
else
{
uiEncState = 0;
}
if (!uiState && !uiEncState)
{
continue;
}
if ((uiState == ITT_FLD_STATE_CHECKING ||
uiState == ITT_FLD_STATE_PURGE ||
uiEncState == ITT_ENC_STATE_CHECKING ||
uiEncState == ITT_ENC_STATE_PURGE) &&
(uiCallbackFreq & EACH_CHANGE) &&
fnStatusHook)
{
if (RC_BAD( rc = (fnStatusHook)( FLM_SWEEP_STATUS,
(void *)&SwpInfo,
(void *)EACH_CHANGE,
UserData)))
{
goto Exit;
}
}
if (uiState == ITT_FLD_STATE_CHECKING)
{
// Change the field's state to 'active'
if (RC_BAD( rc = pDbDict->ChangeState(
pRecord->getFieldID( pvField),
ITT_FLD_STATE_ACTIVE)))
{
goto Exit;
}
}
else if (uiState == ITT_FLD_STATE_PURGE)
{
// If needed, create writeable version of record and
// reposition to field
if (pRecord->isReadOnly())
{
FlmRecord * pTmpRec;
FLMUINT uiFieldID = pRecord->getFieldID( pvField);
if ((pTmpRec = pRecord->copy()) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
pRecord->Release();
pRecord = pTmpRec;
pvField = pRecord->find( pRecord->root(), uiFieldID);
// Should always be able to re-find the field.
flmAssert( pvField);
}
// Remove the purged field from the record.
bRecChanged = TRUE;
if (pvField == pRecord->root())
{
// Passing a NULL pRecord to UpdateRecord will delete
// the record.
pRecord->Release();
pRecord = NULL;
// Get out of the for loop - nothing more to do with
// this record
break;
}
else
{
// Must save the previous field before removing the
// field so we can set pvField to it after removing
// pvField.
pvPrevField = pRecord->prev( pvField);
pRecord->remove( pvField);
pvField = pvPrevField;
}
}
// Check the EncDef state, independant of the field state. If the field
// has been purged, we don't need to update the EncDef record since it
// is no longer being referenced by this field.
if (uiEncState == ITT_ENC_STATE_CHECKING && uiState != ITT_FLD_STATE_PURGE)
{
// Change the EncDef record's state to 'active'
if (RC_BAD( rc = pDbDict->ChangeState(
pRecord->getEncryptionID( pvField),
ITT_ENC_STATE_ACTIVE)))
{
goto Exit;
}
}
// If the EncDef record has a state of purge, then we must change
// the field of this record so that it is no longer encrypted.
else if (uiEncState == ITT_ENC_STATE_PURGE &&
uiState != ITT_FLD_STATE_PURGE)
{
FLMUINT uiDataLength = pRecord->getDataLength( pvField);
const FLMBYTE * pucDataSource = pRecord->getDataPtr( pvField);
FLMBYTE * pucDestPtr;
if( RC_BAD( rc = pRecord->allocStorageSpace( pvField,
pRecord->getDataType( pvField), uiDataLength, 0, 0, 0,
&pucDestPtr, NULL)))
{
goto Exit;
}
f_memmove(pucDestPtr, pucDataSource, uiDataLength);
bRecChanged = TRUE;
}
}
// Record was changed because a purged field was found.
if (bRecChanged)
{
if (RC_BAD( rc = pDbWalk->UpdateRecord( uiDrn, pRecord)))
{
goto Exit;
}
}
}
}
// Now complete any changes needed within the dictionary
if (pDbDict)
{
rc = pDbDict->Finish();
}
Exit:
if( pDbWalk)
{
pDbWalk->Release();
pDbWalk = NULL;
}
if( pDbDict)
{
pDbDict->Release();
pDbWalk = NULL;
}
if( bStartedTrans && pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( pDb);
}
ExitCS:
flmExit( FLM_DB_SWEEP, pDb, rc);
if( pRecord)
{
pRecord->Release();
}
return( rc);
}
//////////////////////////////////////////////////////////////////////////////
// DbWalk Class Implementation
//////////////////////////////////////////////////////////////////////////////
/****************************************************************************
Desc: Get the next container within this database
****************************************************************************/
RCODE DbWalk::NextContainer( // Returns next container to visit
FLMUINT * puiContainer)
{
LFILE * pLFileTbl = (LFILE *)m_pDb->pDict->pLFileTbl;
FLMBOOL bFoundContainer = FALSE;
RCODE rc = FERR_OK;
FLMUINT uiTblSize;
for (uiTblSize = m_pDb->pDict->uiLFileCnt;
m_uiNextLFile < uiTblSize;
m_uiNextLFile++)
{
if (pLFileTbl [m_uiNextLFile].uiLfType == LF_CONTAINER &&
(pLFileTbl [m_uiNextLFile].uiLfNum == FLM_DATA_CONTAINER ||
pLFileTbl [m_uiNextLFile].uiLfNum < FLM_DICT_CONTAINER))
{
// We've found the next container to visit.
m_SwpInfo.uiContainer = *puiContainer =
pLFileTbl [m_uiNextLFile].uiLfNum;
// Note: don't need to release pRecord, because an addRef wasn't done.
m_SwpInfo.pRecord = NULL;
m_SwpInfo.pvField = NULL;
if ((m_uiCallbackFreq & EACH_CONTAINER) && m_fnStatusHook)
{
if (RC_BAD( rc = (m_fnStatusHook)( FLM_SWEEP_STATUS,
(void *)&m_SwpInfo,
(void *)EACH_CONTAINER,
m_UserData)))
{
if (rc == FERR_EOF_HIT)
{
// User wants to skip this container.
continue;
}
else
{
goto Exit;
}
}
}
// Perform needed setup to read this containers records.
m_uiRecsRead = 0;
m_uiLastDrn = 0;
m_uiNextLFile++;
bFoundContainer = TRUE;
break;
}
}
if (!bFoundContainer)
{
rc = RC_SET( FERR_EOF_HIT);
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Returns the next record within the current container.
****************************************************************************/
RCODE DbWalk::NextRecord(
FlmRecord ** ppRecord) // Returned record
{
RCODE rc = FERR_OK;
FLMUINT uiDrn;
// Loop till we get a record or hit end of container.
for (;;)
{
// Abort and start a new read transaction every 1000 records.
if ((m_uiRecsRead % 1000) == 0)
{
AbortTrans:
if (m_pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( m_pDb);
}
if (RC_BAD( rc = flmBeginDbTrans( m_pDb, FLM_READ_TRANS,
0, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
}
if (*ppRecord)
{
(*ppRecord)->Release();
*ppRecord = NULL;
}
if (RC_BAD( rc = FlmRecordRetrieve( (HFDB)m_pDb,
m_SwpInfo.uiContainer,
m_uiLastDrn, FO_EXCL,
ppRecord, &uiDrn)))
{
if (rc == FERR_OLD_VIEW)
{
goto AbortTrans;
}
// It is possible for the container to go away in the
// middle of walking through it, because we are stopping
// and starting transactions.
if (rc == FERR_BAD_CONTAINER)
{
// Must abort the transaction because FERR_BAD_CONTAINER
// will not allow the transaction to continue
// if we are in an update transaction.
if (m_pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( m_pDb);
}
if (RC_BAD( rc = flmBeginDbTrans( m_pDb, FLM_READ_TRANS,
0, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
// Change error code so that it looks like we have
// hit the end of the container.
rc = RC_SET( FERR_EOF_HIT);
}
goto Exit;
}
m_uiLastDrn = uiDrn;
m_uiRecsRead++;
m_SwpInfo.pRecord = *ppRecord;
m_SwpInfo.pvField = NULL;
// Make STATUS_HOOK callback
if ((m_uiCallbackFreq & EACH_RECORD) && m_fnStatusHook)
{
if ((rc = (m_fnStatusHook)( FLM_SWEEP_STATUS, (void *)&m_SwpInfo,
(void *) EACH_RECORD,
m_UserData)) == FERR_EOF_HIT)
{
// User wants to skip this record.
continue;
}
else
{
// Return this record
break;
}
}
else
{
// Return the record
break;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: The last record returned from DbWalk::NextRecord contained 'purged' fields
which have been removed. This function will now replace the old record
with the new record. This record maybe == NULL, in which case the
record will be deleted.
****************************************************************************/
RCODE DbWalk::UpdateRecord(
FLMUINT uiDrn, // DRN of record to modify or delete
FlmRecord * pRecord // if NULL delete the record
)
{
RCODE rc = FERR_OK;
FLMBOOL bRestartTrans = FALSE;
if (m_pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( m_pDb);
bRestartTrans = TRUE;
}
// Either modify or delete the specified record
rc = ( pRecord)
? FlmRecordModify( m_pDb, m_SwpInfo.uiContainer,
uiDrn, pRecord, FLM_AUTO_TRANS | FLM_NO_TIMEOUT)
: FlmRecordDelete( m_pDb, m_SwpInfo.uiContainer,
uiDrn, FLM_AUTO_TRANS | FLM_NO_TIMEOUT);
if( RC_BAD( rc))
{
goto Exit;
}
// Now (if needed) restart the read transaction.
if (bRestartTrans)
{
if (RC_BAD( rc = flmBeginDbTrans( m_pDb, FLM_READ_TRANS,
0, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
}
Exit:
return( rc);
}
//////////////////////////////////////////////////////////////////////////////
// DbDict Class Implementation
//////////////////////////////////////////////////////////////////////////////
DbDict::~DbDict()
{
if( m_pDb)
{
m_pDb->bFldStateUpdOk = FALSE;
}
if( m_puiStateTbl)
{
f_free( &m_puiStateTbl);
}
}
/****************************************************************************
Desc: Read the dictionary and create a internal table that records
the state of field templates within the specified dictionary
****************************************************************************/
RCODE DbDict::Init(
FDB * pDb,
FLMUINT uiMode, // are we looking for checking or purged or both
FLMBOOL * pbFoundPurgeField)// [out] dictionary contained field that is marked purged.
{
RCODE rc = FERR_OK;
ITT * pItt = NULL;
FLMUINT uiItem;
FLMUINT uiCount;
FLMUINT uiStateMask = 0;
*pbFoundPurgeField = FALSE;
m_pDb = pDb;
/* Need to set a flag that which tells lower level FLAIM code
that its okay to:
1) Set a field state to 'unused' and
2) Delete a field thats in a 'purged' state.
*/
m_pDb->bFldStateUpdOk = TRUE;
/* Allocate state table */
m_uiTblSize = m_pDb->pDict->uiIttCnt;
if( RC_BAD( rc = f_calloc(
(m_uiTblSize * sizeof( FLMUINT)), &m_puiStateTbl)))
{
goto Exit;
}
uiCount = m_pDb->pDict->uiIttCnt;
pItt = m_pDb->pDict->pIttTbl;
if( uiMode & SWEEP_CHECKING_FLDS)
{
uiStateMask |= ITT_FLD_STATE_CHECKING;
}
if( uiMode & SWEEP_PURGED_FLDS)
{
uiStateMask |= ITT_FLD_STATE_PURGE;
}
/* Now loop through the ITT table and set the correct states within
the DbDict's state table */
for( uiItem = 0; uiItem < uiCount; pItt++, uiItem++)
{
/* Make sure the entry is a field or an encryption definition record. */
if( ITT_IS_FIELD( pItt))
{
m_puiStateTbl[ uiItem] = (pItt->uiType & uiStateMask);
if( m_puiStateTbl[ uiItem] == ITT_FLD_STATE_PURGE)
{
*pbFoundPurgeField = TRUE;
/* make sure this field is not references and
can therefore be deleted. */
if (RC_BAD( rc = flmCheckDictFldRefs( m_pDb->pDict, uiItem)))
{
goto Exit;
}
}
}
else if (ITT_IS_ENCDEF( pItt) && !m_pDb->pFile->bInLimitedMode)
{
if (RC_BAD( rc = fdictGetEncInfo(
m_pDb,
uiItem,
NULL,
&m_puiStateTbl[ uiItem])))
{
goto Exit;
}
if (m_puiStateTbl[ uiItem] == ITT_ENC_STATE_PURGE)
{
*pbFoundPurgeField = TRUE;
/* make sure this field is not references and
can therefore be deleted. */
if (RC_BAD( rc = flmCheckDictEncDefRefs( m_pDb->pDict, uiItem)))
{
goto Exit;
}
}
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Change the specified field's state.
Currently this is only for "Checking" -> "Active"
****************************************************************************/
RCODE DbDict::ChangeState(
FLMUINT uiFieldID, // Item to change
FLMUINT uiNewState) // New state
{
RCODE rc;
FLMBOOL bRestartTrans = FALSE;
if( m_puiStateTbl[ uiFieldID] != ITT_FLD_STATE_CHECKING)
{
return( RC_SET( FERR_FAILURE));
}
if( m_pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( m_pDb);
bRestartTrans = TRUE;
}
/* Change the state tables value for this field/record template */
m_puiStateTbl[ uiFieldID] = 0;
/* Change the state of the dictionary item. */
if( RC_BAD( rc = flmChangeItemState( m_pDb, uiFieldID, uiNewState)))
{
goto Exit;
}
/* Now restart the read transaction. */
if( bRestartTrans)
{
if (RC_BAD( rc = flmBeginDbTrans( m_pDb, FLM_READ_TRANS,
0, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Following a complete sweep of a database, this function will
handle items that are still marked 'checking' or 'purge'.
****************************************************************************/
RCODE DbDict::Finish() // Finish will make any final changes to the
// database's dictionary
{
RCODE rc = FERR_OK;
FLMUINT uiItem;
/* If we have a read transaction going then end it. */
if( m_pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( m_pDb);
}
/* Loop through the state table changing:
'checking' fields to 'unused' and
deleting 'purged' fields */
for( uiItem = 1; uiItem < m_uiTblSize && RC_OK( rc) ; uiItem++)
{
if( m_puiStateTbl[ uiItem] == ITT_FLD_STATE_CHECKING)
{
/* Change state to "unused" */
rc = flmChangeItemState( m_pDb, uiItem, ITT_FLD_STATE_UNUSED);
}
else if( m_puiStateTbl[ uiItem] == ITT_FLD_STATE_PURGE)
{
/* Delete the 'purged' item */
if( RC_BAD( rc = FlmRecordDelete( (HFDB)m_pDb, FLM_DICT_CONTAINER,
uiItem, FLM_NO_TIMEOUT | FLM_AUTO_TRANS)))
{
goto Exit;
}
}
}
Exit:
if( m_pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( m_pDb);
}
return( rc);
}
/****************************************************************************
Desc: Change a field/record's defined state. Currently the only supported
changes are:
'checking' -> 'active'
'checking' -> 'unused'
Ret:
****************************************************************************/
RCODE flmChangeItemState(
FDB * pDb,
FLMUINT uiItemId,
FLMUINT uiNewState)
{
RCODE rc = FERR_OK;
FLMBOOL bStartedTrans = FALSE;
FlmRecord * pRecord = NULL;
FlmRecord * pOldRecord = NULL;
void * pvField;
// If needed start a update transaction ...
if( pDb->uiTransType == FLM_NO_TRANS)
{
if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS,
FLM_NO_TIMEOUT, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
bStartedTrans = TRUE;
}
// Now read the dictionary definition
if( RC_BAD( rc = FlmRecordRetrieve( (HFDB)pDb, FLM_DICT_CONTAINER,
uiItemId, FO_EXACT, &pOldRecord, NULL)))
{
goto Exit;
}
if( (pRecord = pOldRecord->copy()) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
// Change the state to the correct state
pvField = pRecord->find( pRecord->root(), FLM_STATE_TAG);
flmAssert( pvField != NULL);
if( RC_BAD( rc = pRecord->setNative( pvField,
(uiNewState == ITT_FLD_STATE_UNUSED)
? "unused"
: "active")))
{
goto Exit;
}
// Update the dictionary
if( RC_BAD( rc = FlmRecordModify( (HFDB)pDb, FLM_DICT_CONTAINER,
pOldRecord->getID(), pRecord, 0)))
{
goto Exit;
}
Exit:
if( pRecord)
{
pRecord->Release();
}
if( pOldRecord)
{
pOldRecord->Release();
}
if( bStartedTrans)
{
if( RC_OK( rc))
{
rc = flmCommitDbTrans( pDb, 0, FALSE);
}
else
{
(void)flmAbortDbTrans( pDb);
}
}
return( rc);
}