Files
mars-flaim/flaim/src/flsweep.cpp
2006-07-20 19:20:32 +00:00

1095 lines
24 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;
m_bInternalTrans = FALSE;
}
~DbDict();
RCODE init(
FDB * pDb,
FLMBOOL bInternalTrans,
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;
FLMBOOL m_bInternalTrans;
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_bInternalTrans = FALSE;
m_uiCallbackFreq = 0;
m_fnStatusHook = NULL;
m_UserData = 0;
m_uiNextLFile = 0;
m_uiRecsRead = 0;
m_uiLastDrn = 0;
}
~DbWalk()
{
}
FINLINE void init(
FDB * pDb,
FLMBOOL bInternalTrans,
FLMUINT uiCallbackFreq,
STATUS_HOOK fnStatusHook,
void * UserData)
{
m_pDb = pDb;
m_bInternalTrans = bInternalTrans;
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;
FLMBOOL m_bInternalTrans;
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_NO_TRANS,
FDB_TRANS_GOING_OK, 0, NULL)))
{
goto Exit;
}
if( pDb->uiTransType == FLM_READ_TRANS)
{
rc = RC_SET_AND_ASSERT( FERR_ILLEGAL_TRANS_OP);
goto Exit;
}
else if( pDb->uiTransType == FLM_NO_TRANS)
{
if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_READ_TRANS,
0, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
bStartedTrans = TRUE;
}
pDbWalk->init( pDb, bStartedTrans, 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, bStartedTrans,
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
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;
}
}
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 the EncDef record has a state of purge, then we must
// change the field of this record so that it is no longer
// encrypted.
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)
{
if( pDb->uiTransType != FLM_NO_TRANS)
{
(void)flmAbortDbTrans( pDb);
}
}
ExitCS:
flmExit( FLM_DB_SWEEP, pDb, rc);
if( pRecord)
{
pRecord->Release();
}
return( rc);
}
/****************************************************************************
Desc: Get the next container within this database
****************************************************************************/
RCODE DbWalk::nextContainer(
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)
{
RCODE rc = FERR_OK;
FLMUINT uiDrn;
// Loop till we get a record or hit end of container.
for( ;;)
{
if( m_bInternalTrans)
{
// 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)
{
if( m_bInternalTrans)
{
goto AbortTrans;
}
goto Exit;
}
// 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_bInternalTrans)
{
goto Exit;
}
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 may be == NULL, in which
case the record will be deleted.
****************************************************************************/
RCODE DbWalk::updateRecord(
FLMUINT uiDrn,
FlmRecord * pRecord)
{
RCODE rc = FERR_OK;
FLMBOOL bRestartTrans = FALSE;
if( m_bInternalTrans)
{
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)
{
flmAssert( m_bInternalTrans);
if( RC_BAD( rc = flmBeginDbTrans( m_pDb, FLM_READ_TRANS,
0, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
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,
FLMBOOL bInternalTrans,
FLMUINT uiMode,
FLMBOOL * pbFoundPurgeField)
{
RCODE rc = FERR_OK;
ITT * pItt = NULL;
FLMUINT uiItem;
FLMUINT uiCount;
FLMUINT uiStateMask = 0;
*pbFoundPurgeField = FALSE;
m_pDb = pDb;
m_bInternalTrans = bInternalTrans;
// 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,
FLMUINT uiNewState)
{
RCODE rc = FERR_OK;
FLMBOOL bRestartTrans = FALSE;
if( m_puiStateTbl[ uiFieldID] != ITT_FLD_STATE_CHECKING)
{
return( RC_SET( FERR_FAILURE));
}
if( m_bInternalTrans)
{
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)
{
flmAssert( m_bInternalTrans);
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( void)
{
RCODE rc = FERR_OK;
FLMUINT uiItem;
// If we have an internal read transaction going then end it.
if( m_bInternalTrans)
{
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_bInternalTrans)
{
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);
}