Files
mars-flaim/flaim/src/flupdate.cpp
ahodgkinson f54e6ce080 Changed license to LGPL.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1009 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2007-01-23 09:38:48 +00:00

1209 lines
28 KiB
C++

//-------------------------------------------------------------------------
// Desc: Record add, modify, and delete routines.
// Tabs: 3
//
// Copyright (c) 1991-1992, 1994-2007 Novell, Inc. All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version 2.1
// of the License.
//
// This library 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
// Library Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; 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$
//------------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC RCODE flmDoUpdateCS(
FDB * pDb,
FLMUINT uiOp,
FLMUINT uiContainer,
FLMUINT * puiDrn,
FlmRecord * pRecord,
FLMUINT uiAutoTrans);
/****************************************************************************
Desc: Internal routine for doing updates via client/server
****************************************************************************/
FSTATIC RCODE flmDoUpdateCS(
FDB * pDb,
FLMUINT uiOp,
FLMUINT uiContainer,
FLMUINT * puiDrn,
FlmRecord * pRecord,
FLMUINT uiAutoTrans)
{
RCODE rc = FERR_OK;
CS_CONTEXT *pCSContext = pDb->pCSContext;
FCL_WIRE Wire( pCSContext, pDb);
// Send a request to do the update
if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_RECORD, uiOp)))
{
goto Exit;
}
if (uiContainer)
{
if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_CONTAINER_ID, uiContainer)))
{
goto Transmission_Error;
}
}
if (*puiDrn)
{
if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_DRN, *puiDrn)))
{
goto Transmission_Error;
}
}
if (pRecord)
{
if (RC_BAD( rc = Wire.sendRecord( WIRE_VALUE_RECORD, pRecord)))
{
goto Transmission_Error;
}
}
if (uiAutoTrans)
{
if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_AUTOTRANS, uiAutoTrans)))
{
goto Transmission_Error;
}
}
if (RC_BAD( rc = Wire.sendTerminate()))
{
goto Transmission_Error;
}
// Read the response
if (RC_BAD( rc = Wire.read()))
{
goto Transmission_Error;
}
if (RC_BAD( rc = Wire.getRCode()))
{
goto Exit;
}
if (uiOp == FCS_OP_RECORD_ADD)
{
*puiDrn = Wire.getDrn();
}
Exit:
return( rc);
Transmission_Error:
pCSContext->bConnectionGood = FALSE;
goto Exit;
}
/************************************************************************
Desc : Outputs an update event callback.
*************************************************************************/
void flmUpdEventCallback(
FDB * pDb,
FEventType eEventType,
HFDB hDb,
RCODE rc,
FLMUINT uiDrn,
FLMUINT uiContainer,
FlmRecord * pNewRecord,
FlmRecord * pOldRecord)
{
FLM_UPDATE_EVENT UpdEvent;
UpdEvent.uiThreadId = f_threadId();
UpdEvent.hDb = hDb;
UpdEvent.uiTransID = (FLMUINT)( (pDb->uiTransType)
? pDb->LogHdr.uiCurrTransID
: 0);
UpdEvent.rc = rc;
UpdEvent.uiDrn = uiDrn;
UpdEvent.uiContainer = uiContainer;
UpdEvent.pNewRecord = pNewRecord;
UpdEvent.pOldRecord = pOldRecord;
flmDoEventCallback( F_EVENT_UPDATES, eEventType, &UpdEvent, NULL);
}
/****************************************************************************
Desc : Adds a record to a container.
Notes: If an index definition record is added to the dictionary container,
the index will be built automatically when the transaction commits.
When a unique index is added to the database, FLAIM needs to verify
that each key in the proposed index is indeed a unique key. However,
this verification does NOT occur until the dictionary transaction
commits and the index is actually built. If FLAIM discovers that the
keys in an index are not unique, the transaction commit will fail and
return an error.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmRecordAdd(
HFDB hDb,
FLMUINT uiContainer,
FLMUINT * puiDrn,
FlmRecord * pRecord,
FLMUINT uiAutoTrans)
{
RCODE rc = FERR_OK;
FLMUINT uiDrn = 0;
FDB * pDb = (FDB *)hDb;
LFILE * pLFile;
FLMBOOL bStartedAutoTrans = FALSE;
FLMBOOL bLogCompleteIndexSet = FALSE;
DB_STATS * pDbStats = NULL;
F_TMSTAMP StartTime;
if( puiDrn)
{
uiDrn = *puiDrn;
}
if( uiContainer == FLM_TRACKER_CONTAINER)
{
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
rc = flmDoUpdateCS( pDb, FCS_OP_RECORD_ADD, uiContainer,
&uiDrn, pRecord, uiAutoTrans);
goto ExitCS;
}
if( RC_BAD( rc = fdbInit( (FDB *)hDb, FLM_UPDATE_TRANS,
FDB_TRANS_GOING_OK,
uiAutoTrans, &bStartedAutoTrans)))
{
goto Exit;
}
if( (pDbStats = pDb->pDbStats) != NULL)
{
f_timeGetTimeStamp( &StartTime);
}
// Make sure we have a valid record
if( !pRecord)
{
rc = RC_SET( FERR_INVALID_PARM);
goto Exit;
}
// We cannot add a record that is marked read-only, because it
// is probably already in the cache under a different record ID.
if( pRecord->isReadOnly())
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
{
goto Exit;
}
rc = flmAddRecord( pDb, pLFile, &uiDrn, pRecord, FALSE,
(uiAutoTrans & FLM_DO_IN_BACKGROUND) ? TRUE : FALSE,
(uiAutoTrans & FLM_SUSPENDED) ? TRUE : FALSE,
(FLMBOOL)((uiAutoTrans & FLM_DONT_INSERT_IN_CACHE) ? FALSE : TRUE),
&bLogCompleteIndexSet);
Exit:
rc = FB_OperationEnd( pDb, rc);
if( RC_OK( rc))
{
if( RC_OK( rc = pDb->pFile->pRfl->logUpdate(
uiContainer, uiDrn, uiAutoTrans, NULL, pRecord)) &&
bLogCompleteIndexSet &&
pDb->pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_51)
{
// Log the fact that we indexed everything so the redo will also
// index all data records in the container.
rc = pDb->pFile->pRfl->logIndexSet( uiDrn, 0, 1, 0xFFFFFFFF);
}
}
if( pDbStats)
{
flmAddElapTime( &StartTime, &pDbStats->RecordAdds.ui64ElapMilli);
pDbStats->RecordAdds.ui64Count++;
pDbStats->bHaveStats = TRUE;
}
if( gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmUpdEventCallback( pDb, F_EVENT_ADD_RECORD, hDb, rc, uiDrn,
uiContainer, pRecord, NULL);
}
// If started an automatic transaction end it.
if( bStartedAutoTrans)
{
rc = flmEndAutoTrans( pDb, rc);
}
ExitCS:
if( puiDrn)
{
*puiDrn = uiDrn;
}
flmExit( FLM_RECORD_ADD, pDb, rc);
return( rc);
}
/****************************************************************************
Desc: Internal interface routine for database add operation.
Must be in a valid transaction.
Note: Internal name is used so that later we can call this within FLAIM.
****************************************************************************/
RCODE flmAddRecord(
FDB * pDb,
LFILE * pLFile,
FLMUINT * puiDrn, // Record Number to set AND return.
FlmRecord * pRecord, // Record to add, must NOT be NULL.
FLMBOOL bBatchProcessing, // Set to TRUE if called by REBUILD.
FLMBOOL bDoInBackground,
FLMBOOL bCreateSuspended,
FLMBOOL bKeepInCache,
FLMBOOL * pbLogCompleteIndexSet)
{
RCODE rc = FERR_OK;
FLMUINT uiDrn = 0;
FLMBOOL bProcessedKeys = FALSE;
FLMUINT uiLfNum = pLFile->uiLfNum;
FLMUINT uiAddAppendFlags = REC_UPD_ADD;
FLMBOOL bHadUniqueKeys;
if( puiDrn)
{
uiDrn = *puiDrn;
}
if( pDb->uiFlags & FDB_COMMITTING_TRANS)
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
goto Exit;
}
// Assert that the record is not read-only
flmAssert( !pRecord->isReadOnly());
// Set up for indexing.
if( RC_BAD( rc = KrefCntrlCheck( pDb)))
{
goto Exit;
}
if( !pRecord)
{
rc = RC_SET( FERR_NULL_RECORD);
goto Exit;
}
// If this is a dictionary record then one routine takes care of it.
if( pLFile->uiLfNum == FLM_LOCAL_DICT_CONTAINER)
{
if( RC_OK( rc = flmLFileDictUpdate( pDb, &pLFile, &uiDrn,
pRecord, NULL, bDoInBackground, bCreateSuspended,
pbLogCompleteIndexSet)))
{
if( puiDrn)
{
*puiDrn = uiDrn;
}
}
goto Exit;
}
else
{
if( !uiDrn || uiDrn == DRN_LAST_MARKER)
{
if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiDrn)))
{
#ifdef FLM_DBG_LOG
uiDrn = 0;
#endif
goto Exit;
}
uiAddAppendFlags |= REC_UPD_NEW_RECORD;
}
}
// Add the records keys, and then the record. NOTE: If the bBatchProcessing
// flag is set to TRUE, we are being called as part of a rebuild. In this
// case, we do NOT want BLOBs to be reprocessed. Also, we do not want
// the QF job list to be generated (it could get very large - and all of
// the entries are stored in a single record). So we set the
// KREF_INDEXING_ONLY flag. This flag will prohibit the processing of
// BLOB data. Also, it will prohibit constructing a QF job list. Instead
// of the QF job list, the entries will be fed directly to QuickFinder and
// then processed when KYKeysCommit is called.
bProcessedKeys = TRUE;
bHadUniqueKeys = FALSE;
if( RC_BAD( rc = flmProcessRecFlds( pDb, NULL, pLFile->uiLfNum, uiDrn, pRecord,
(FLMUINT)((bBatchProcessing)
? (KREF_ADD_KEYS | KREF_INDEXING_ONLY)
: KREF_ADD_KEYS),
FALSE, &bHadUniqueKeys)))
{
goto Exit;
}
// NOTE: The LFile table may have changed locations if the dictionary
// was update because of a change in a field state
if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiLfNum, &pLFile)))
{
goto Exit;
}
if( RC_BAD( rc = FSRecUpdate( pDb, pLFile, pRecord,
uiDrn, uiAddAppendFlags)))
{
goto Exit;
}
if( puiDrn)
{
*puiDrn = uiDrn;
}
// Sort and check keys for uniqueness.
if( RC_BAD( rc = KYProcessDupKeys( pDb, bHadUniqueKeys)))
{
// Remove the record that was added because of the error.
RCODE rc1 = FSRecUpdate( pDb, pLFile, NULL, uiDrn, REC_UPD_DELETE);
if( RC_BAD(rc1))
{
rc = (rc == FERR_NOT_UNIQUE) ? rc1 : rc;
}
goto Exit;
}
// Insert record into cache
pRecord->setID( uiDrn);
pRecord->setContainerID( pLFile->uiLfNum);
if (bKeepInCache)
{
if( RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, uiDrn, pRecord)))
{
// Remove the record that was added because of the error.
FSRecUpdate( pDb, pLFile, NULL, uiDrn, REC_UPD_DELETE);
goto Exit;
}
}
// Don't make this call until we are sure of success - because we want to
// be able to back things out of KREF table.
KYFinishCurrentRecord( pDb);
Exit:
if( RC_BAD( rc) && bProcessedKeys)
{
KYAbortCurrentRecord( pDb);
}
#ifdef FLM_DBG_LOG
flmDbgLogUpdate( pDb->pFile->uiFFileId, pDb->LogHdr.uiCurrTransID,
uiLfNum, uiDrn, rc, "RAdd");
#endif
return( rc);
}
/****************************************************************************
Desc : Modifies a record within a container.
Notes: If an index definition record is modified in the dictionary container,
the index B-TREE will be deleted and rebuilt automatically when the
transaction commits. If an index definition record is changed into
a field definition record or a container definition record, the index
will be automatically deleted when the transaction commits. When a
non-unique index is changed to a unique index, or the fields in a
unique index are changed, FLAIM needs to verify that each key in the
proposed index is indeed a unique key. However, this verification
does NOT occur until the dictionary transaction commits and the
index is actually rebuilt. If FLAIM discovers that the keys in the
proposed index are not unique, the transaction commit will fail and
return FERR_NOT_UNIQUE.
If a container definition record is changed into a field definition
record, the container will be deleted automatically when the
transaction commits. If a container definition record is changed
into an index definition record, the container will be automatically
deleted and the index will be automatically built when the transaction
commits.
Only the name and state of field definition records can be modified.
Changing a field type or changing a field definition record into an
index definition record is not allowed. For information on changing
the state of a field, see the Dictionary Syntax document.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmRecordModify(
HFDB hDb,
FLMUINT uiContainer,
FLMUINT uiDrn,
FlmRecord * pRecord,
FLMUINT uiAutoTrans
)
{
RCODE rc = FERR_OK;
RCODE rc1;
FDB * pDb = (FDB *)hDb;
FlmRecord * pOldRecord = NULL;
LFILE * pLFile;
FLMBOOL bStartedAutoTrans = FALSE;
FLMBOOL bProcessedKeys = FALSE;
FLMBOOL bLogCompleteIndexSet = FALSE;
FLMBOOL bHadUniqueKeys;
DB_STATS * pDbStats = NULL;
F_TMSTAMP StartTime;
if( uiContainer == FLM_TRACKER_CONTAINER)
{
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
rc = flmDoUpdateCS( pDb, FCS_OP_RECORD_MODIFY, uiContainer,
&uiDrn, pRecord, uiAutoTrans);
goto ExitCS;
}
if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
FDB_TRANS_GOING_OK, uiAutoTrans, &bStartedAutoTrans)))
{
goto Exit;
}
if( pDb->uiFlags & FDB_COMMITTING_TRANS)
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
goto Exit;
}
if ((pDbStats = pDb->pDbStats) != NULL)
{
f_timeGetTimeStamp( &StartTime);
}
// Make sure we have a valid record
if( !pRecord)
{
rc = RC_SET( FERR_INVALID_PARM);
goto Exit;
}
// We cannot modify a record that is marked read-only.
if( pRecord->isReadOnly())
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if( !uiDrn || (uiDrn == (FLMUINT) DRN_LAST_MARKER))
{
rc = RC_SET( FERR_BAD_DRN);
goto Exit;
}
if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
{
goto Exit;
}
if( RC_BAD( rc = KrefCntrlCheck( pDb)))
{
goto Exit;
}
// DICTIONARY RECORD MODIFY
if( uiContainer == FLM_LOCAL_DICT_CONTAINER)
{
if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
uiContainer, uiDrn, FALSE, NULL, NULL, &pOldRecord)))
{
// NOTE: Deliberately not reading in to cache if not found.
if (rc != FERR_NOT_FOUND)
{
goto Exit;
}
if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn, &pOldRecord, NULL, NULL)))
{
goto Exit;
}
}
// Sanity check -- make sure that the new and old records point at
// different objects.
flmAssert( pRecord != pOldRecord);
if( RC_BAD( rc = flmLFileDictUpdate( pDb, &pLFile, &uiDrn,
pRecord, pOldRecord,
(uiAutoTrans & FLM_DO_IN_BACKGROUND) ? TRUE : FALSE,
(uiAutoTrans & FLM_SUSPENDED) ? TRUE : FALSE,
&bLogCompleteIndexSet)))
{
goto Exit;
}
pRecord->setID( uiDrn);
pRecord->setContainerID( uiContainer);
if( RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, uiDrn, pRecord)))
{
goto Exit;
}
goto Exit;
}
// First read the old record, and delete it's keys.
// To do this we need to be able to read any purged fields and delete any
// keys they build.
if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
uiContainer, uiDrn, FALSE, NULL, NULL, &pOldRecord)))
{
// NOTE: Deliberately not reading in to cache if not found.
if (rc != FERR_NOT_FOUND)
{
goto Exit;
}
if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn, &pOldRecord, NULL, NULL)))
{
goto Exit;
}
}
// Sanity check -- make sure that the new and old records point at
// different objects.
flmAssert( pRecord != pOldRecord);
bProcessedKeys = TRUE;
bHadUniqueKeys = FALSE;
if( RC_BAD( rc = flmProcessRecFlds( pDb, NULL, uiContainer, uiDrn, pOldRecord,
KREF_DEL_KEYS | KREF_IN_MODIFY,
TRUE, // Purged Field OK
&bHadUniqueKeys)))
{
goto Exit;
}
if( RC_BAD( rc = flmProcessRecFlds( pDb, NULL, uiContainer, uiDrn, pRecord,
KREF_ADD_KEYS | KREF_IN_MODIFY,
FALSE,
&bHadUniqueKeys)))
{
goto Exit;
}
// NOTE: The LFile table may have changed locations if the dictionary
// was update because of a change in a field state
if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
{
goto Exit;
}
if( RC_BAD( rc = FSRecUpdate( pDb, pLFile, pRecord, uiDrn, REC_UPD_MODIFY)))
{
goto Exit;
}
// Finish up the keys adding the unique keys to the indexes.
if( RC_BAD( rc = KYProcessDupKeys( pDb, bHadUniqueKeys)))
{
// Undo the record that was modified - replace with original record.
if( RC_BAD( rc1 = FSRecUpdate( pDb, pLFile,
pOldRecord, uiDrn, REC_UPD_MODIFY)))
{
rc = (rc == FERR_NOT_UNIQUE) ? rc1 : rc;
}
goto Exit;
}
// Insert record into cache
pRecord->setID( uiDrn);
pRecord->setContainerID( uiContainer);
if( RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, uiDrn, pRecord)))
{
if ( rc != FERR_MEM)
{
flmAssert( 0);
}
// Undo the record that was modified - replace with original record.
FSRecUpdate( pDb, pLFile, pOldRecord, uiDrn, REC_UPD_MODIFY);
goto Exit;
}
// Don't make this call until we are sure of success - because we want to
// be able to back things out of KREF table.
KYFinishCurrentRecord( pDb);
Exit:
if( RC_BAD( rc) && bProcessedKeys)
{
KYAbortCurrentRecord( pDb);
}
// Add the BLOB entries to the blob list.
rc = FB_OperationEnd( pDb, rc);
if( RC_OK( rc))
{
if( RC_OK( rc = pDb->pFile->pRfl->logUpdate(
uiContainer, uiDrn, uiAutoTrans, pOldRecord, pRecord)) &&
bLogCompleteIndexSet &&
pDb->pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_51)
{
// Log the fact that we indexed everything so the redo will also
// index all data records in the container.
rc = pDb->pFile->pRfl->logIndexSet( uiDrn, 0, 1, 0xFFFFFFFF);
}
}
if( pDbStats)
{
flmAddElapTime( &StartTime, &pDbStats->RecordModifies.ui64ElapMilli);
pDbStats->RecordModifies.ui64Count++;
pDbStats->bHaveStats = TRUE;
}
if( gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmUpdEventCallback( pDb, F_EVENT_MODIFY_RECORD, hDb, rc, uiDrn,
uiContainer, pRecord, pOldRecord);
}
#ifdef FLM_DBG_LOG
flmDbgLogUpdate( pDb->pFile->uiFFileId,
pDb->LogHdr.uiCurrTransID, uiContainer, uiDrn, rc, "RMod");
#endif
// If started an automatic transaction, end it.
if( bStartedAutoTrans)
{
rc = flmEndAutoTrans( pDb, rc);
}
if( pOldRecord)
{
pOldRecord->Release();
pOldRecord = NULL;
}
ExitCS:
flmExit( FLM_RECORD_MODIFY, pDb, rc);
return( rc);
}
/****************************************************************************
Desc : Deletes a record from a container.
Notes: If an index definition record or a container definition record is
deleted from the dictionary container, the index B-TREE container or
container B-TREE will be deleted automatically when the transaction
commits. Field definition records can only be deleted from the
dictionary using this routine if the field is not in use. For more
information on deletion of field definitions, see the Dictionary Syntax
document.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmRecordDelete(
HFDB hDb,
FLMUINT uiContainer,
FLMUINT uiDrn,
FLMUINT uiAutoTrans
)
{
RCODE rc = FERR_OK;
LFILE * pLFile;
FDB * pDb = (FDB *)hDb;
FLMBOOL bStartedAutoTrans;
FlmRecord * pOldRecord;
FlmRecord ** ppOldRecord;
DB_STATS * pDbStats = NULL;
F_TMSTAMP StartTime;
if( uiContainer == FLM_TRACKER_CONTAINER)
{
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
rc = flmDoUpdateCS( pDb, FCS_OP_RECORD_DELETE, uiContainer,
&uiDrn, NULL, uiAutoTrans);
goto ExitCS;
}
bStartedAutoTrans = FALSE;
pOldRecord = NULL;
ppOldRecord = NULL;
if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK,
uiAutoTrans, &bStartedAutoTrans)))
{
goto Exit;
}
if( (pDbStats = pDb->pDbStats) != NULL)
{
f_timeGetTimeStamp( &StartTime);
}
if( !uiDrn || uiDrn == (FLMUINT) DRN_LAST_MARKER)
{
rc = RC_SET( FERR_BAD_DRN);
goto Exit;
}
if( RC_BAD(rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
{
goto Exit;
}
if( gv_FlmSysData.UpdateEvents.pEventCBList)
{
// Do not have flmDeleteRecord fetch the old version of the record
// unless an event callback is registered.
ppOldRecord = &pOldRecord;
}
// NOTE: pLFile should NOT be used after this call, because flmDeleteRecord
// could actually change its position in memory due to field changes.
if (RC_BAD( rc = flmDeleteRecord( pDb, pLFile, uiDrn, ppOldRecord, FALSE)))
{
goto Exit;
}
Exit:
if( RC_OK( rc))
{
rc = pDb->pFile->pRfl->logUpdate( uiContainer, uiDrn,
uiAutoTrans, NULL, NULL);
}
if( pDbStats)
{
flmAddElapTime( &StartTime, &pDbStats->RecordDeletes.ui64ElapMilli);
pDbStats->RecordDeletes.ui64Count++;
pDbStats->bHaveStats = TRUE;
}
if( gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmUpdEventCallback( pDb, F_EVENT_DELETE_RECORD, hDb, rc, uiDrn,
uiContainer, NULL, pOldRecord);
}
#ifdef FLM_DBG_LOG
flmDbgLogUpdate( pDb->pFile->uiFFileId,
pDb->LogHdr.uiCurrTransID, uiContainer, uiDrn, rc, "RDel");
#endif
// If started an automatic transaction, end it.
if( bStartedAutoTrans)
{
rc = flmEndAutoTrans( pDb, rc);
}
if( pOldRecord)
{
pOldRecord->Release();
pOldRecord = NULL;
}
ExitCS:
flmExit( FLM_RECORD_DELETE, pDb, rc);
return( rc);
}
/************************************************************************
Desc : Deletes a record from a container.
Notes: This is the internal call that corresponds to FlmRecordDelete.
It may also be called by the database checking code when
repairing an index.
*************************************************************************/
RCODE flmDeleteRecord(
FDB * pDb, // Operation context
LFILE * pLFile, // LFILE of container to delete from.
FLMUINT uiDrn, // DRN of the record to be deleted.
FlmRecord ** ppOldRecord, // Returns old record, if not null.
FLMBOOL bMissingKeysOK // Is it OK for keys to be missing in the
// indexes that are to be updated?
// should we report when index entries
// aren't found? TRUE=don't report,
// FALSE=do report.
)
{
RCODE rc = FERR_OK;
FlmRecord * pOldRecord = NULL;
FLMUINT uiContainer = pLFile->uiLfNum;
FLMUINT uiAction;
FLMBOOL bHadUniqueKeys;
FLMBOOL bProcessedKeys = FALSE;
if( pDb->uiFlags & FDB_COMMITTING_TRANS)
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
goto Exit;
}
if( RC_BAD( rc = KrefCntrlCheck( pDb)))
{
goto Exit;
}
if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
uiContainer, uiDrn, FALSE, NULL, NULL, &pOldRecord)))
{
if (rc != FERR_NOT_FOUND)
{
goto Exit;
}
if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn,
&pOldRecord, NULL, NULL)))
{
goto Exit;
}
}
if( uiContainer == FLM_LOCAL_DICT_CONTAINER )
{
if( RC_OK( rc = flmLFileDictUpdate( pDb, &pLFile,
&uiDrn, NULL, pOldRecord, FALSE, FALSE, NULL)))
{
rc = flmRcaRemoveRec( pDb, uiContainer, uiDrn);
}
goto Exit;
}
// First read the record, then delete it's keys and then it.
uiAction = (FLMUINT)((bMissingKeysOK)
? (FLMUINT)(KREF_DEL_KEYS | KREF_MISSING_KEYS_OK)
: (FLMUINT)(KREF_DEL_KEYS));
bProcessedKeys = TRUE;
bHadUniqueKeys = FALSE;
if( RC_BAD(rc = flmProcessRecFlds( pDb, NULL, uiContainer,
uiDrn, pOldRecord, uiAction,
TRUE,
&bHadUniqueKeys)))
{
goto Exit;
}
// NOTE: The LFile table may have changed locations if the dictionary
// was updated because of a change in a field state
if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
{
goto Exit;
}
if( RC_BAD( rc = KYProcessDupKeys( pDb, bHadUniqueKeys)))
{
goto Exit;
}
if( RC_BAD( rc = FSRecUpdate( pDb, pLFile, NULL, uiDrn, REC_UPD_DELETE)))
{
goto Exit;
}
// Remove the record from cache
if( RC_BAD( rc = flmRcaRemoveRec( pDb, uiContainer, uiDrn)))
{
(void)FSRecUpdate( pDb, pLFile, pOldRecord, uiDrn, REC_UPD_ADD);
goto Exit;
}
// Don't make this call until we are sure of success - because we want to
// be able to back things out of KREF table.
KYFinishCurrentRecord( pDb);
Exit:
if (RC_BAD( rc) && bProcessedKeys)
{
KYAbortCurrentRecord( pDb);
}
if( ppOldRecord)
{
*ppOldRecord = pOldRecord;
}
else if( pOldRecord)
{
pOldRecord->Release();
}
// Add the BLOB entries to the blob list.
rc = FB_OperationEnd( pDb, rc);
return( rc);
}
/****************************************************************************
Desc: Returns the next DRN that record ADD would return. The database
must be in an existing update transaction.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmReserveNextDrn(
HFDB hDb,
FLMUINT uiContainer,
FLMUINT * puiDrnRV)
{
RCODE rc;
FDB * pDb = (FDB *)hDb;
LFILE * pLFile;
FLMBOOL bIgnore;
FLMUINT uiDrn = 0;
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
CS_CONTEXT * pCSContext = pDb->pCSContext;
FCL_WIRE Wire( pCSContext, pDb);
// Send the request
if( RC_BAD( rc = Wire.sendOp(
FCS_OPCLASS_RECORD, FCS_OP_RESERVE_NEXT_DRN)))
{
goto ExitCS;
}
if( uiContainer)
{
if (RC_BAD( rc = Wire.sendNumber(
WIRE_VALUE_CONTAINER_ID, uiContainer)))
{
goto Transmission_Error;
}
}
if( RC_BAD( rc = Wire.sendTerminate()))
{
goto Transmission_Error;
}
// Read the response
if( RC_BAD( rc = Wire.read()))
{
goto Transmission_Error;
}
if( RC_BAD( rc = Wire.getRCode()))
{
goto ExitCS;
}
*puiDrnRV = Wire.getDrn();
goto ExitCS;
Transmission_Error:
pCSContext->bConnectionGood = FALSE;
goto ExitCS;
}
bIgnore = FALSE; // Set to shut up compiler.
if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
FDB_TRANS_GOING_OK, // byFlags
0, // wAutoTrans
&bIgnore))) // bStartedAutoTrans
{
goto Exit;
}
if( pDb->uiFlags & FDB_COMMITTING_TRANS)
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
goto Exit;
}
if( RC_BAD( fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))
{
#ifdef FLM_DBG_LOG
uiDrn = 0;
#endif
goto Exit;
}
uiDrn = (FLMUINT) 0; // Must initialize before call.
if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, TRUE, &uiDrn)))
{
#ifdef FLM_DBG_LOG
uiDrn = 0;
#endif
goto Exit;
}
*puiDrnRV = uiDrn; // Set return value.
Exit:
if (RC_OK( rc))
{
rc = pDb->pFile->pRfl->logUpdatePacket(
RFL_RESERVE_DRN_PACKET, uiContainer, *puiDrnRV, 0);
}
if( gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmUpdEventCallback( pDb, F_EVENT_RESERVE_DRN, hDb, rc, *puiDrnRV,
uiContainer, NULL, NULL);
}
#ifdef FLM_DBG_LOG
flmDbgLogUpdate( pDb->pFile->uiFFileId, pDb->LogHdr.uiCurrTransID,
uiContainer, uiDrn, rc, "RDrn");
#endif
ExitCS:
flmExit( FLM_RESERVE_NEXT_DRN, pDb, rc);
return( rc);
}
/****************************************************************************
Desc: Searches for an available DRN in the dictionary container.
Differs from FlmReserveNextDrn in that it will attempt to reuse
dictionary DRNS.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmFindUnusedDictDrn(
HFDB hDb,
FLMUINT uiStartDrn,
FLMUINT uiEndDrn,
FLMUINT * puiDrnRV)
{
RCODE rc;
FDB * pDb = (FDB *)hDb;
FLMBOOL bIgnore = FALSE;
FDICT * pDict;
FLMUINT uiCurrDrn;
FLMUINT uiStopSearch;
if( RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK,
0, &bIgnore)))
{
*puiDrnRV = (FLMUINT)-1;
goto Exit;
}
// Search through the ITT table looking for the first occurance
// of ITT_EMPTY_SLOT
pDict = pDb->pDict;
uiCurrDrn = f_max( uiStartDrn, 1);
uiStopSearch = f_min( uiEndDrn, pDict->uiIttCnt - 1);
while (uiCurrDrn <= uiStopSearch)
{
if (pDict->pIttTbl[ uiCurrDrn].uiType == ITT_EMPTY_SLOT)
{
break;
}
else
{
uiCurrDrn++;
}
}
if (uiCurrDrn > uiEndDrn)
{
rc = RC_SET( FERR_NO_MORE_DRNS);
goto Exit;
}
*puiDrnRV = uiCurrDrn;
Exit:
fdbExit( pDb);
return( rc);
}