Files
mars-flaim/flaim/src/ftrans.cpp
ahodgkinson 8c1ca5fcea Re-organized code and added some new files.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@219 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-03-28 22:50:10 +00:00

1746 lines
43 KiB
C++

//-------------------------------------------------------------------------
// Desc: Begin transaction
// Tabs: 3
//
// Copyright (c) 1991,1994-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: fltrbeg.cpp 12266 2006-01-19 14:45:33 -0700 (Thu, 19 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
/****************************************************************************
Desc: Starts a transaction.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbTransBegin(
HFDB hDb,
FLMUINT uiTransType,
FLMUINT uiMaxLockWait,
FLMBYTE * pucHeader)
{
RCODE rc = FERR_OK;
FLMBOOL bIgnore;
FLMUINT uiFlags = FLM_GET_TRANS_FLAGS( uiTransType);
FDB * pDb = (FDB *)hDb;
uiTransType = FLM_GET_TRANS_TYPE( uiTransType);
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
FCL_WIRE Wire( pDb->pCSContext, pDb);
if (!pDb->pCSContext->bConnectionGood)
{
rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
}
else
{
if( RC_BAD( rc = Wire.doTransOp(
FCS_OP_TRANSACTION_BEGIN, uiTransType, uiFlags,
uiMaxLockWait, pucHeader)))
{
goto Exit;
}
}
goto Exit;
}
if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS,
FDB_TRANS_GOING_OK, 0, &bIgnore)))
{
goto Exit;
}
// Verify the transaction type.
if (( uiTransType != FLM_UPDATE_TRANS) &&
( uiTransType != FLM_READ_TRANS))
{
rc = RC_SET( FERR_ILLEGAL_TRANS);
goto Exit;
}
// Verify the transaction flags
if( (uiFlags & FLM_DONT_KILL_TRANS) && uiTransType != FLM_READ_TRANS)
{
rc = RC_SET( FERR_ILLEGAL_TRANS);
goto Exit;
}
// Can't start an update transaction on a database that
// is locked in shared mode.
if ((uiTransType == FLM_UPDATE_TRANS) &&
(pDb->uiFlags & FDB_FILE_LOCK_SHARED))
{
rc = RC_SET( FERR_PERMISSION);
goto Exit;
}
// If the database has an invisible transaction going, abort it
// before going any further - we don't want application transactions
// to be nested under invisible transactions. Application transactions
// take precedence over invisible transactions.
if ((pDb->uiTransType != FLM_NO_TRANS) &&
(pDb->uiFlags & FDB_INVISIBLE_TRANS))
{
if (RC_BAD( rc = flmAbortDbTrans( pDb)))
{
goto Exit;
}
}
// If the database is not running a transaction, start one.
// Otherwise, start a nested transaction - first verifying that
// the transation type matches.
if (pDb->uiTransType == FLM_NO_TRANS)
{
FLMUINT uiBytesRead;
if( pucHeader)
{
if( RC_BAD( rc = pDb->pSFileHdl->ReadHeader(
0, 2048, pucHeader, &uiBytesRead)))
{
goto Exit;
}
}
if (RC_BAD( rc = flmBeginDbTrans( pDb, uiTransType,
uiMaxLockWait, uiFlags,
pucHeader ? &pucHeader [16] : NULL)))
{
goto Exit;
}
pDb->bHadUpdOper = FALSE;
}
else
{
// Cannot nest transactions.
rc = RC_SET( FERR_TRANS_ACTIVE);
goto Exit;
}
Exit:
flmExit( FLM_DB_TRANS_BEGIN, pDb, rc);
return( rc);
}
/****************************************************************************
Desc: Commits an active transaction.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbTransCommit(
HFDB hDb,
FLMBOOL * pbEmpty)
{
RCODE rc = FERR_OK;
FDB * pDb = (FDB *)hDb;
FLMBOOL bIgnore;
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
FCL_WIRE Wire( pDb->pCSContext, pDb);
if (!pDb->pCSContext->bConnectionGood)
{
rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
}
else
{
rc = Wire.doTransOp( FCS_OP_TRANSACTION_COMMIT, 0, 0, 0);
}
goto Exit;
}
if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS,
FDB_TRANS_GOING_OK | FDB_CLOSING_OK, 0, &bIgnore)))
{
goto Exit;
}
// If there is an invisible transaction going, it should not be
// commitable by an application.
if ((pDb->uiTransType == FLM_NO_TRANS) ||
(pDb->uiFlags & FDB_INVISIBLE_TRANS))
{
rc = RC_SET( FERR_NO_TRANS_ACTIVE);
goto Exit;
}
// See if we have a transaction going which should be aborted.
if( RC_BAD( pDb->AbortRc))
{
rc = RC_SET( FERR_ABORT_TRANS);
goto Exit;
}
if (pbEmpty)
{
*pbEmpty = FALSE;
}
rc = flmCommitDbTrans( pDb, 0, FALSE, pbEmpty);
Exit:
if( RC_OK( rc))
{
rc = flmCheckDatabaseState( pDb);
}
flmExit( FLM_DB_TRANS_COMMIT, pDb, rc);
return( rc);
}
/****************************************************************************
Desc: Aborts an active transaction.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbTransAbort(
HFDB hDb)
{
RCODE rc;
FDB * pDb = (FDB *)hDb;
FLMBOOL bIgnore;
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
FCL_WIRE Wire( pDb->pCSContext, pDb);
if (!pDb->pCSContext->bConnectionGood)
{
rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
}
else
{
rc = Wire.doTransOp( FCS_OP_TRANSACTION_ABORT, 0, 0, 0);
}
goto Exit;
}
if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS,
FDB_TRANS_GOING_OK | FDB_CLOSING_OK, 0, &bIgnore)))
{
goto Exit;
}
// If there is an invisible transaction going, it should not be
// abortable by an application.
if ((pDb->uiTransType == FLM_NO_TRANS) ||
(pDb->uiFlags & FDB_INVISIBLE_TRANS))
{
rc = RC_SET( FERR_NO_TRANS_ACTIVE);
goto Exit;
}
rc = flmAbortDbTrans( pDb);
Exit:
if( RC_OK( rc))
{
rc = flmCheckDatabaseState( pDb);
}
flmExit( FLM_DB_TRANS_ABORT, pDb, rc);
return( rc);
}
/****************************************************************************
Desc : Returns the type of the current database transaction.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbGetTransType(
HFDB hDb,
FLMUINT * puiTransTypeRV)
{
RCODE rc = FERR_OK;
FDB * pDb = (FDB *)hDb;
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
CS_CONTEXT * pCSContext = pDb->pCSContext;
FCL_WIRE Wire( pDb->pCSContext, pDb);
// Send a request to get the transaction type.
if( RC_BAD( rc = Wire.sendOp(
FCS_OPCLASS_TRANS, FCS_OP_TRANSACTION_GET_TYPE)))
{
goto Exit;
}
if (RC_BAD( rc = Wire.sendTerminate()))
{
goto Transmission_Error;
}
// Read the response.
if (RC_BAD( rc = Wire.read()))
{
goto Transmission_Error;
}
*puiTransTypeRV = Wire.getTransType();
rc = Wire.getRCode();
goto Exit;
Transmission_Error:
pCSContext->bConnectionGood = FALSE;
goto Exit;
}
if (!pDb)
{
rc = RC_SET( FERR_BAD_HDL);
goto Exit;
}
fdbUseCheck( pDb);
pDb->uiInitNestLevel++;
(void)flmResetDiag( pDb);
// If the transaction is an internal transaction that is invisible to
// the application, return FLM_NO_TRANS. Application is not supposed
// see invisible transactions.
*puiTransTypeRV = (FLMUINT)(((pDb->uiTransType == FLM_NO_TRANS) ||
(pDb->uiFlags & FDB_INVISIBLE_TRANS))
? (FLMUINT)FLM_NO_TRANS
: pDb->uiTransType);
// See if the database is being forced to close
if( RC_BAD( rc = flmCheckDatabaseState( pDb)))
{
goto Exit;
}
Exit:
flmExit( FLM_DB_GET_TRANS_TYPE, pDb, rc);
return( rc);
}
/****************************************************************************
Desc : Retrieves the current transaction number of a database
Notes: This routine should only be called only from within an update
transaction since read transactions are not assigned a transaction
number.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbGetTransId(
HFDB hDb,
FLMUINT * puiTrNumRV)
{
RCODE rc = FERR_OK;
FDB * pDb = (FDB *)hDb;
FLMBOOL bIgnore;
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
CS_CONTEXT * pCSContext = pDb->pCSContext;
FCL_WIRE Wire( pCSContext, pDb);
// Send a request to get the transaction ID.
if (RC_BAD( rc = Wire.sendOp(
FCS_OPCLASS_DATABASE, FCS_OP_GET_TRANS_ID)))
{
goto Exit;
}
if (RC_BAD( rc = Wire.sendTerminate()))
{
goto Transmission_Error;
}
// Read the response
if (RC_BAD( rc = Wire.read()))
{
goto Transmission_Error;
}
*puiTrNumRV = Wire.getTransId();
rc = Wire.getRCode();
goto Exit;
Transmission_Error:
pCSContext->bConnectionGood = FALSE;
goto Exit;
}
if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
FDB_TRANS_GOING_OK, 0, &bIgnore)))
{
goto Exit;
}
*puiTrNumRV = pDb->LogHdr.uiCurrTransID;
Exit:
flmExit( FLM_DB_GET_TRANS_ID, pDb, rc);
return( rc);
}
/****************************************************************************
Desc: Retrieves the last commit sequence number of a database.
Notes: Whenever a transaction is committed, FLAIM increments the commit
sequence number to indicate that the database has been modified.
An application may use this routine to determine if the database
has been modified.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbGetCommitCnt(
HFDB hDb,
FLMUINT * puiCommitCount)
{
RCODE rc = FERR_OK;
FDB * pDb = (FDB *)hDb;
FLMBOOL bIgnore;
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
CS_CONTEXT * pCSContext = pDb->pCSContext;
FCL_WIRE Wire( pCSContext, pDb);
// Send a request to get the commit count
if (RC_BAD( rc = Wire.sendOp(
FCS_OPCLASS_DATABASE, FCS_OP_GET_COMMIT_CNT)))
{
goto Exit;
}
if (RC_BAD( rc = Wire.sendTerminate()))
{
goto Transmission_Error;
}
// Read the response.
if (RC_BAD( rc = Wire.read()))
{
goto Transmission_Error;
}
*puiCommitCount = (FLMUINT)Wire.getCount();
rc = Wire.getRCode();
goto ExitCS;
Transmission_Error:
pCSContext->bConnectionGood = FALSE;
goto ExitCS;
}
if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS,
FDB_TRANS_GOING_OK, 0, &bIgnore)))
{
goto Exit;
}
// See if we have a transaction going which should be aborted.
if (pDb->uiTransType != FLM_NO_TRANS)
{
if (flmCheckBadTrans( pDb))
{
rc = RC_SET( FERR_ABORT_TRANS);
goto Exit;
}
}
f_mutexLock( gv_FlmSysData.hShareMutex);
*puiCommitCount = (FLMUINT)FB2UD(
&pDb->pFile->ucLastCommittedLogHdr [LOG_COMMIT_COUNT]);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
Exit:
ExitCS:
flmExit( FLM_DB_GET_COMMIT_CNT, pDb, rc);
return( rc);
}
/****************************************************************************
Desc: Forces a checkpoint on the database.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbCheckpoint(
HFDB hDb,
FLMUINT uiTimeout)
{
RCODE rc = FERR_OK;
FDB * pDb = (FDB *)hDb;
FLMBOOL bStartedTrans;
bStartedTrans = FALSE;
if (IsInCSMode( hDb))
{
fdbInitCS( pDb);
CS_CONTEXT * pCSContext = pDb->pCSContext;
FCL_WIRE Wire( pCSContext, pDb);
if( !pCSContext->bConnectionGood)
{
rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
goto Transmission_Error;
}
if( RC_BAD( rc = Wire.sendOp(
FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_CHECKPOINT)))
{
goto Exit;
}
if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiTimeout)))
{
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;
}
goto Exit;
Transmission_Error:
pCSContext->bConnectionGood = FALSE;
goto Exit;
}
// Start an update transaction. Must not already be one going.
if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
0, uiTimeout | FLM_AUTO_TRANS, &bStartedTrans)))
{
goto Exit;
}
// Commit the transaction, forcing it to be checkpointed.
bStartedTrans = FALSE;
pDb->bHadUpdOper = FALSE;
if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE)))
{
goto Exit;
}
Exit:
if (bStartedTrans)
{
(void)flmAbortDbTrans( pDb);
}
flmExit( FLM_DB_CHECKPOINT, pDb, rc);
return( rc);
}
/****************************************************************************
Desc: This routine unlinks an FDB from a transaction's list of FDBs.
****************************************************************************/
void flmUnlinkDbFromTrans(
FDB * pDb,
FLMBOOL bCommitting)
{
FFILE * pFile = pDb->pFile;
flmAssert( pDb->pIxdFixups == NULL);
if( pDb->uiTransType != FLM_NO_TRANS)
{
if (pDb->uiFlags & FDB_HAS_WRITE_LOCK)
{
// If this is a commit operation and we have a commit callback,
// call the callback function before unlocking the DIB.
if( bCommitting && pDb->fnCommit)
{
FLMBOOL bSavedInvisTrans;
CB_ENTER( pDb, &bSavedInvisTrans);
pDb->fnCommit( (HFDB)pDb, pDb->pvCommitData);
CB_EXIT( pDb, bSavedInvisTrans);
}
dbUnlock( pDb);
}
f_mutexLock( gv_FlmSysData.hShareMutex);
if (pDb->pDict)
{
flmUnlinkFdbFromDict( pDb);
}
// Unlink the transaction from the FFILE if it is a read
// transaction
if( pDb->uiTransType == FLM_READ_TRANS)
{
if (pDb->pNextReadTrans)
{
pDb->pNextReadTrans->pPrevReadTrans = pDb->pPrevReadTrans;
}
else if (!pDb->uiKilledTime)
{
pFile->pLastReadTrans = pDb->pPrevReadTrans;
}
if (pDb->pPrevReadTrans)
{
pDb->pPrevReadTrans->pNextReadTrans = pDb->pNextReadTrans;
}
else if (pDb->uiKilledTime)
{
pFile->pFirstKilledTrans = pDb->pNextReadTrans;
}
else
{
pFile->pFirstReadTrans = pDb->pNextReadTrans;
}
// Zero out so it will be zero for next transaction begin.
pDb->uiKilledTime = 0;
}
else
{
// Reset to NULL or zero for next update transaction.
pDb->pBlobList = NULL;
pDb->pIxStartList = pDb->pIxStopList = NULL;
flmAssert( pDb->pIxdFixups == NULL);
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
pDb->uiTransType = FLM_NO_TRANS;
pDb->uiFlags &= (~(FDB_UPDATED_DICTIONARY |
FDB_INVISIBLE_TRANS |
FDB_DONT_KILL_TRANS |
FDB_DONT_POISON_CACHE));
}
}
/****************************************************************************
Desc: This routine starts a transaction for the specified database. The
transaction may be part of an overall larger transaction.
****************************************************************************/
RCODE flmBeginDbTrans(
FDB * pDb,
FLMUINT uiTransType,
FLMUINT uiMaxLockWait,
FLMUINT uiFlags,
FLMBYTE * pucLogHdr)
{
RCODE rc = FERR_OK;
FFILE * pFile = pDb->pFile;
FLMBOOL bMutexLocked = FALSE;
FLMBYTE * pucLastCommittedLogHdr;
DB_STATS * pDbStats = pDb->pDbStats;
if( RC_BAD( rc = flmCheckDatabaseState( pDb)))
{
goto Exit;
}
// Initialize a few things - as few as is necessary to avoid
// unnecessary overhead.
pDb->eAbortFuncId = FLM_UNKNOWN_FUNC;
pDb->AbortRc = FERR_OK;
pucLastCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0];
pDb->KrefCntrl.bKrefSetup = FALSE;
pDb->uiTransType = uiTransType;
pDb->uiThreadId = (FLMUINT)f_threadId();
pDb->uiTransCount++;
// Link the FDB to the file's most current FDICT structure,
// if there is one.
//
// Also, if it is a read transaction, link the FDB
// into the list of read transactions off of
// the FFILE structure.
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
if (pFile->pDictList)
{
// Link the FDB to the FDICT.
flmLinkFdbToDict( pDb, pFile->pDictList);
}
// If it is a read transaction, link into the list of
// read transactions off of the FFILE structure. Until we
// get the log header transaction ID below, we set uiCurrTransID
// to zero and link this transaction in at the beginning of the
// list.
if (uiTransType == FLM_READ_TRANS)
{
flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr);
// Link in at the end of the transaction list.
pDb->pNextReadTrans = NULL;
if ((pDb->pPrevReadTrans = pFile->pLastReadTrans) != NULL)
{
// Make sure transaction IDs are always in ascending order. They
// should be at this point.
flmAssert( pFile->pLastReadTrans->LogHdr.uiCurrTransID <=
pDb->LogHdr.uiCurrTransID);
pFile->pLastReadTrans->pNextReadTrans = pDb;
}
else
{
pFile->pFirstReadTrans = pDb;
}
pFile->pLastReadTrans = pDb;
pDb->uiInactiveTime = 0;
if( uiFlags & FLM_DONT_KILL_TRANS)
{
pDb->uiFlags |= FDB_DONT_KILL_TRANS;
}
else
{
pDb->uiFlags &= ~FDB_DONT_KILL_TRANS;
}
if (pucLogHdr)
{
f_memcpy( pucLogHdr, &pDb->pFile->ucLastCommittedLogHdr[0],
LOG_HEADER_SIZE);
}
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
if( uiFlags & FLM_DONT_POISON_CACHE)
{
pDb->uiFlags |= FDB_DONT_POISON_CACHE;
}
else
{
pDb->uiFlags &= ~FDB_DONT_POISON_CACHE;
}
// Put an exclusive lock on the database if we are not in a read
// transaction. Read transactions require no lock.
if (uiTransType != FLM_READ_TRANS)
{
flmAssert( pDb->pIxStats == NULL);
// Set the bHadUpdOper to TRUE for all transactions to begin with.
// Many calls to flmBeginDbTrans are internal, and we WANT the
// normal behavior at the end of the transaction when it is
// committed or aborted. The only time this flag will be set
// to FALSE is when the application starts the transaction as
// opposed to an internal starting of the transaction.
pDb->bHadUpdOper = TRUE;
// Initialize the count of blocks changed to be 0
pDb->uiBlkChangeCnt = 0;
if (RC_BAD( rc = dbLock( pDb, uiMaxLockWait)))
{
goto Exit;
}
// If there was a problem with the RFL volume, we must wait
// for a checkpoint to be completed before continuing.
// The checkpoint thread looks at this same flag and forces
// a checkpoint. If it completes one successfully, it will
// reset this flag.
//
// Also, if the last forced checkpoint had a problem
// (pFile->CheckpointRc != FERR_OK), we don't want to
// start up a new update transaction until it is resolved.
if (!pFile->pRfl->seeIfRflVolumeOk() ||
RC_BAD( pFile->CheckpointRc))
{
rc = RC_SET( FERR_MUST_WAIT_CHECKPOINT);
goto Exit;
}
// Set the first log block address to zero.
pFile->uiFirstLogBlkAddress = 0;
// Header must be read before opening roll forward log file to make
// sure we have the most current log file and log options.
f_memcpy( pFile->ucUncommittedLogHdr, pucLastCommittedLogHdr,
LOG_HEADER_SIZE);
flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr);
// Need to increment the current checkpoint for update transactions
// so that it will be correct when we go to mark cache blocks.
if (pDb->uiFlags & FDB_REPLAYING_RFL)
{
// During recovery we need to set the transaction ID to the
// transaction ID that was logged.
pDb->LogHdr.uiCurrTransID = pFile->pRfl->getCurrTransID();
}
else
{
pDb->LogHdr.uiCurrTransID++;
}
f_mutexLock( gv_FlmSysData.hShareMutex);
// Link FDB to the most current local dictionary, if there
// is one.
if (pFile->pDictList != pDb->pDict && pFile->pDictList)
{
flmLinkFdbToDict( pDb, pFile->pDictList);
}
pFile->uiUpdateTransID = pDb->LogHdr.uiCurrTransID;
f_mutexUnlock( gv_FlmSysData.hShareMutex);
// Set the transaction EOF to the current file EOF
pDb->uiTransEOF = pDb->LogHdr.uiLogicalEOF;
// Put the transaction ID into the uncommitted log header.
UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID,
&pFile->ucUncommittedLogHdr [LOG_CURR_TRANS_ID]);
if (pucLogHdr)
{
f_memcpy( pucLogHdr, &pDb->pFile->ucUncommittedLogHdr [0],
LOG_HEADER_SIZE);
}
}
if (pDbStats)
{
f_timeGetTimeStamp( &pDb->TransStartTime);
}
// If we do not have a dictionary, read it in from disk.
// NOTE: This should only happen when we are first opening
// the database.
if (!pDb->pDict)
{
flmAssert( pDb->pFile->uiFlags & DBF_BEING_OPENED);
if (RC_BAD( rc = fdictRebuild( pDb)))
{
if (pDb->pDict)
{
flmFreeDict( pDb->pDict);
pDb->pDict = NULL;
}
goto Exit;
}
f_mutexLock( gv_FlmSysData.hShareMutex);
// At this point, we will not yet have opened the database for
// general use, so there is no way that any other thread can have
// created a dictionary yet.
flmAssert( pDb->pFile->pDictList == NULL);
// Link the new local dictionary to its file structure.
flmLinkDictToFile( pDb->pFile, pDb->pDict);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
if (uiTransType != FLM_READ_TRANS)
{
if (RC_OK( rc))
{
rc = pFile->pRfl->logBeginTransaction( pDb);
}
#ifdef FLM_DBG_LOG
flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID,
0, 0, rc, "TBeg");
#endif
}
if( uiTransType == FLM_UPDATE_TRANS &&
gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmTransEventCallback( F_EVENT_BEGIN_TRANS, (HFDB)pDb, rc,
(FLMUINT)(RC_OK( rc)
? pDb->LogHdr.uiCurrTransID
: (FLMUINT)0));
}
if (RC_BAD( rc))
{
// If there was an error, unlink the database from the transaction
// structure as well as from the FDICT structure.
flmUnlinkDbFromTrans( pDb, FALSE);
if (pDb->pStats)
{
(void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats);
}
}
return( rc);
}
/****************************************************************************
Desc: This routine aborts an active transaction for a particular
database. If the database is open via a server, a message is
sent to the server to abort the transaction. Otherwise, the
transaction is rolled back locally.
****************************************************************************/
RCODE flmAbortDbTrans(
FDB * pDb,
FLMBOOL bOkToLogAbort)
{
RCODE rc = FERR_OK;
FFILE * pFile = pDb->pFile;
FLMUINT uiTransType;
FLMBYTE * pucLastCommittedLogHdr;
FLMBYTE * pucUncommittedLogHdr;
FLMBOOL bDumpedCache = FALSE;
DB_STATS * pDbStats = pDb->pDbStats;
FLMBOOL bKeepAbortedTrans;
FLMUINT uiTransId;
FLMBOOL bInvisibleTrans;
// Get transaction type
if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS)
{
goto Exit;
}
// No recovery required if it is a read transaction.
if (uiTransType == FLM_READ_TRANS)
{
if( pDb->KrefCntrl.bKrefSetup)
{
// KrefCntrlFree could be called w/o checking bKrefSetup because
// it checks the flag, but it is more optimal to check the
// flag before making the call because most of the time it will
// be false.
KrefCntrlFree( pDb);
}
goto Unlink_From_Trans;
}
#ifdef FLM_DBG_LOG
flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID,
0, 0, FERR_OK, "TAbrt");
#endif
pFile->pRfl->clearLogHdrs();
// If the transaction had no update operations, restore it
// to its pre-transaction state - make it appear that no
// transaction ever happened.
pucLastCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0];
pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0];
uiTransId = pDb->LogHdr.uiCurrTransID;
// Free up all keys associated with this database. This is done even
// if we didn't have any update operations because the KREF may
// have been initialized by key generation operations performed
// by cursors, etc.
KrefCntrlFree( pDb);
// Free any index counts we may have allocated.
FSFreeIxCounts( pDb);
if (pDb->bHadUpdOper)
{
// Dump any BLOB structures that should be aborted.
FBListAfterAbort( pDb);
// Dump any start and stop indexing stubs that should be aborted.
flmIndexingAfterAbort( pDb);
// Log the abort record to the rfl file, or throw away the logged
// records altogether, depending on the LOG_KEEP_ABORTED_TRANS_IN_RFL
// flag. If the RFL volume is bad, we will not attempt to keep this
// transaction in the RFL.
if (!pFile->pRfl->seeIfRflVolumeOk())
{
bKeepAbortedTrans = FALSE;
}
else
{
bKeepAbortedTrans =
(pucUncommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL])
? TRUE
: FALSE;
}
}
else
{
bKeepAbortedTrans = FALSE;
}
// Log an abort transaction record to the roll-forward log or
// throw away the entire transaction, depending on the
// bKeepAbortedTrans flag.
// If the transaction is being "dumped" because of a failed commit,
// don't log anything to the RFL.
if( bOkToLogAbort)
{
flmAssert( pDb->LogHdr.uiCurrTransID == pFile->pRfl->getCurrTransID());
if (RC_BAD( rc = pFile->pRfl->logEndTransaction(
RFL_TRNS_ABORT_PACKET, !bKeepAbortedTrans)))
{
goto Exit1;
}
}
#ifdef FLM_DEBUG
else
{
// If bOkToLogAbort is FALSE, this always means that either a
// commit failed while trying to log an end transaction packet or a
// commit packet was logged and the transaction commit subsequently
// failed for some other reason. In either case, the RFL should be
// in a good state, with its current transaction ID reset to 0. If
// not, either bOkToLogAbort is being used incorrectly by the caller
// or there is a bug in the RFL logic.
flmAssert( pFile->pRfl->getCurrTransID() == 0);
}
#endif
// If there were no operations in the transaction, restore
// everything as if the transaction never happened.
if (!pDb->bHadUpdOper)
{
f_mutexLock( gv_FlmSysData.hShareMutex);
pFile->uiUpdateTransID = 0;
f_mutexUnlock( gv_FlmSysData.hShareMutex);
// Pretend we dumped cache - shouldn't be any to worry about at
// this point.
bDumpedCache = TRUE;
goto Exit1;
}
// Dump ALL modified cache blocks associated with the DB.
// NOTE: This needs to be done BEFORE the call to flmGetLogHdrInfo
// below, because that call will change pDb->LogHdr.uiCurrTransID,
// and that value is used by flmRcaAbortTrans.
ScaFreeModifiedBlocks( pDb);
flmRcaAbortTrans( pDb);
bDumpedCache = TRUE;
// Reset the LogHdr from the last committed log header in pFile.
flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr);
if (RC_BAD( rc = flmPhysRollback( pDb,
(FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_ROLLBACK_EOF]),
pFile->uiFirstLogBlkAddress, FALSE, 0)))
{
goto Exit1;
}
f_mutexLock( gv_FlmSysData.hShareMutex);
// Put the new transaction ID into the log header even though
// we are not committing. We want to keep the transaction IDs
// incrementing even though we aborted.
UD2FBA( (FLMUINT32)uiTransId,
&pucLastCommittedLogHdr [LOG_CURR_TRANS_ID]);
// Preserve where we are at in the roll-forward log. Even though
// the transaction aborted, we may have kept it in the RFL instead of
// throw it away.
f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_FILE_NUM],
&pucUncommittedLogHdr [LOG_RFL_FILE_NUM], 4);
f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET],
&pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET], 4);
f_memcpy( &pucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM],
&pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM],
F_SERIAL_NUM_SIZE);
f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM],
&pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM],
F_SERIAL_NUM_SIZE);
// The following items tell us where we are at in the roll-back log.
// During a transaction we may log blocks for the checkpoint or for
// read transactions. So, even though we are aborting this transaction,
// there may be other things in the roll-back log that we don't want
// to lose. These items should not be reset until we do a checkpoint,
// which is when we know it is safe to throw away the entire roll-back log.
f_memcpy( &pucLastCommittedLogHdr [LOG_ROLLBACK_EOF],
&pucUncommittedLogHdr [LOG_ROLLBACK_EOF], 4);
f_memcpy( &pucLastCommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR],
&pucUncommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR], 4);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
pFile->pRfl->commitLogHdrs( pucLastCommittedLogHdr,
pFile->ucCheckpointLogHdr);
Exit1:
// Dump cache, if not done above.
if (!bDumpedCache)
{
ScaFreeModifiedBlocks( pDb);
flmRcaAbortTrans( pDb);
bDumpedCache = TRUE;
}
// Throw away IXD_FIXUPs
if (pDb->pIxdFixups)
{
IXD_FIXUP * pIxdFixup;
IXD_FIXUP * pDeleteIxdFixup;
pIxdFixup = pDb->pIxdFixups;
while (pIxdFixup)
{
pDeleteIxdFixup = pIxdFixup;
pIxdFixup = pIxdFixup->pNext;
f_free( &pDeleteIxdFixup);
}
pDb->pIxdFixups = NULL;
}
if (uiTransType != FLM_READ_TRANS &&
gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmTransEventCallback( F_EVENT_ABORT_TRANS, (HFDB)pDb, rc,
uiTransId);
}
Unlink_From_Trans:
bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE;
if (pDb->uiFlags & FDB_HAS_WRITE_LOCK)
{
RCODE tmpRc;
if (RC_BAD( tmpRc = pFile->pRfl->completeTransWrites( pDb, FALSE, FALSE)))
{
if (RC_OK( rc))
{
rc = tmpRc;
}
}
}
// Unlink the database from the transaction
// structure as well as from the FLDICT structure.
flmUnlinkDbFromTrans( pDb, FALSE);
if (pDbStats)
{
FLMUINT64 ui64ElapMilli = 0;
flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli);
pDbStats->bHaveStats = TRUE;
if (uiTransType == FLM_READ_TRANS)
{
pDbStats->ReadTransStats.AbortedTrans.ui64Count++;
pDbStats->ReadTransStats.AbortedTrans.ui64ElapMilli +=
ui64ElapMilli;
if (bInvisibleTrans)
{
pDbStats->ReadTransStats.InvisibleTrans.ui64Count++;
pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli +=
ui64ElapMilli;
}
}
else
{
pDbStats->UpdateTransStats.AbortedTrans.ui64Count++;
pDbStats->UpdateTransStats.AbortedTrans.ui64ElapMilli +=
ui64ElapMilli;
}
}
if (pDb->pStats)
{
(void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats);
}
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine commits an active transaction for a particular
database. If the database is open via a server, a message is
sent to the server to commit the transaction. Otherwise, the
transaction is committed locally.
****************************************************************************/
RCODE flmCommitDbTrans(
FDB * pDb,
FLMUINT uiNewLogicalEOF, // New logical end-of-file. This is only
// set by the FlmDbReduceSize function when
// it is truncating the file.
FLMBOOL bForceCheckpoint, // Force a checkpoint?
FLMBOOL * pbEmpty) // May be NULL
{
RCODE rc = FERR_OK;
FLMBYTE * pucUncommittedLogHdr;
FFILE * pFile = pDb->pFile;
FLMUINT uiCPFileNum = 0;
FLMUINT uiCPOffset = 0;
FLMUINT uiTransId = 0;
FLMBOOL bTransEndLogged;
FLMBOOL bForceCloseOnError = FALSE;
FLMBOOL bOkToLogAbort = TRUE;
DB_STATS * pDbStats = pDb->pDbStats;
FLMUINT uiTransType;
FLMBOOL bInvisibleTrans = FALSE;
FLMBOOL bIndexAfterCommit = FALSE;
pDb->uiFlags |= FDB_COMMITTING_TRANS;
// See if we even have a transaction going.
if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS)
{
goto Exit; // Will return FERR_OK.
}
// See if we have a transaction going which should be aborted.
if (flmCheckBadTrans( pDb))
{
rc = RC_SET( FERR_ABORT_TRANS);
goto Exit;
}
// If we are in a read transaction we can skip most of the stuff
// below because no updates would have occurred. This will help
// improve performance.
if (uiTransType == FLM_READ_TRANS)
{
if( pDb->KrefCntrl.bKrefSetup)
{
// KrefCntrlFree could be called w/o checking bKrefSetup because
// it checks the flag, but it is more optimal to check the
// flag before making the call because most of the time it will
// be false.
KrefCntrlFree( pDb);
}
goto Exit1;
}
// At this point, we know we have an update transaction.
pFile->pRfl->clearLogHdrs();
#ifdef FLM_DBG_LOG
flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID,
0, 0, FERR_OK, "TCmit");
#endif
uiTransId = pDb->LogHdr.uiCurrTransID;
// If the transaction had no update operations, restore it
// to its pre-transaction state - make it appear that no
// transaction ever happened.
if (!pDb->bHadUpdOper)
{
bOkToLogAbort = FALSE;
rc = pFile->pRfl->logEndTransaction( RFL_TRNS_COMMIT_PACKET, TRUE);
// Even though we didn't have any update operations, there may have
// been operations during the transaction (i.e., query operations)
// that initialized the KREF in order to generate keys.
KrefCntrlFree( pDb);
// Restore everything as if the transaction never happened.
f_mutexLock( gv_FlmSysData.hShareMutex);
pFile->uiUpdateTransID = 0;
f_mutexUnlock( gv_FlmSysData.hShareMutex);
if (pbEmpty)
{
*pbEmpty = TRUE;
}
goto Exit1;
}
// Log commit record to roll-forward log
bOkToLogAbort = FALSE;
if (RC_BAD( rc = pFile->pRfl->logEndTransaction(
RFL_TRNS_COMMIT_PACKET, FALSE, &bTransEndLogged)))
{
goto Exit1;
}
bForceCloseOnError = TRUE;
// Commit any keys in the KREF buffers.
if (RC_BAD( rc = KYKeysCommit( pDb, TRUE)))
{
flmLogError( rc, "calling KYKeysCommit from flmCommitDbTrans");
goto Exit1;
}
if (RC_BAD( rc = FSCommitIxCounts( pDb)))
{
flmLogError( rc, "calling FSCommitIxCounts from flmCommitDbTrans");
goto Exit1;
}
// Reinitialize the log header. If the local dictionary was updated
// during the transaction, increment the local dictionary ID so that
// other concurrent users will know that it has been modified and
// that they need to re-read it into memory.
// If we are in recovery mode, see if we need to force
// a checkpoint with what we have so far. We force a
// checkpoint on one of two conditions:
// 1. If it appears that we have a buildup of dirty cache
// blocks. We force a checkpoint on this condition
// because it will be more efficient than replacing
// cache blocks one at a time.
// We check for this condition by looking to see if
// our LRU block is not used and it is dirty. That is
// a pretty good indicator that we have a buildup
// of dirty cache blocks.
// 2. We are at the end of the roll-forward log. We
// want to force a checkpoint here to complete the
// recovery phase.
if ( pDb->uiFlags & FDB_REPLAYING_RFL)
{
// If we are in the middle of upgrading, and are forcing
// a checkpoint, use the file number and offset that were
// set in the FDB.
if ((pDb->uiFlags & FDB_UPGRADING) && bForceCheckpoint)
{
uiCPFileNum = pDb->uiUpgradeCPFileNum;
uiCPOffset = pDb->uiUpgradeCPOffset;
}
else
{
SCACHE * pTmpSCache;
F_Rfl * pRfl = pFile->pRfl;
f_mutexLock( gv_FlmSysData.hShareMutex);
pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUCache;
// Test for buildup of dirty cache blocks.
if (((pTmpSCache) &&
(!pTmpSCache->uiUseCount) &&
(pTmpSCache->ui16Flags &
(CA_DIRTY | CA_LOG_FOR_CP | CA_WRITE_TO_LOG)))
|| // Test for end of roll-forward log.
pRfl->atEndOfLog()
||
bForceCheckpoint)
{
bForceCheckpoint = TRUE;
uiCPFileNum = pRfl->getCurrFileNum();
uiCPOffset = pRfl->getCurrReadOffset();
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
}
// Move information collected in the pDb->LogHdr into the
// uncommitted log header. Other things that need to be
// set have already been set in the uncommitted log header
// at various places in the code.
// Mutex does not have to be locked while we do this because
// the update transaction is the only one that ever accesses
// the uncommitted log header buffer.
pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0];
// Set the new logical EOF if passed in.
if (uiNewLogicalEOF)
{
pDb->LogHdr.uiLogicalEOF = uiNewLogicalEOF;
}
UD2FBA( (FLMUINT32)pDb->LogHdr.uiLogicalEOF,
&pucUncommittedLogHdr [LOG_LOGICAL_EOF]);
// Increment the commit counter.
flmIncrUint( &pucUncommittedLogHdr [LOG_COMMIT_COUNT], 1);
// Set the last committed transaction ID
if( (bTransEndLogged || (pDb->uiFlags & FDB_REPLAYING_COMMIT)) &&
pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31)
{
UD2FBA( (FLMUINT32)uiTransId,
&pucUncommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]);
}
// Write the header
pFile->pRfl->commitLogHdrs( pucUncommittedLogHdr,
pFile->ucCheckpointLogHdr);
// Commit any record cache.
flmRcaCommitTrans( pDb);
// Push the IXD_FIXUP values back into the IXD
if (pDb->pIxdFixups)
{
IXD_FIXUP * pIxdFixup;
IXD_FIXUP * pDeleteIxdFixup;
IXD * pIxd;
pIxdFixup = pDb->pIxdFixups;
while (pIxdFixup)
{
if( RC_BAD( fdictGetIndex(
pDb->pDict, pDb->pFile->bInLimitedMode,
pIxdFixup->uiIndexNum, NULL, &pIxd, TRUE)))
{
flmAssert( 0);
pIxd = NULL;
}
if( pIxd)
{
pIxd->uiLastContainerIndexed = pIxdFixup->uiLastContainerIndexed;
pIxd->uiLastDrnIndexed = pIxdFixup->uiLastDrnIndexed;
}
pDeleteIxdFixup = pIxdFixup;
pIxdFixup = pIxdFixup->pNext;
f_free( &pDeleteIxdFixup);
}
pDb->pIxdFixups = NULL;
}
// Set the update transaction ID back to zero only
// AFTER we know the transaction has safely committed.
f_mutexLock( gv_FlmSysData.hShareMutex);
f_memcpy( pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr,
LOG_HEADER_SIZE);
pFile->uiUpdateTransID = 0;
ScaReleaseLogBlocks( pFile);
if (pDb->uiFlags & FDB_UPDATED_DICTIONARY)
{
// Link the new local dictionary to its file.
// Since the new local dictionary will be linked at the head
// of the list of FDICT structures, see if the FDICT currently
// at the head of the list is unused and can be unlinked.
if ((pFile->pDictList) && (!pFile->pDictList->uiUseCount))
{
flmUnlinkDict( pFile->pDictList);
}
flmLinkDictToFile( pFile, pDb->pDict);
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
Exit1:
// If the local dictionary was updated during this transaction,
// link the new local dictionary structures to their file - or free
// them if there was an error.
if (pDb->uiFlags & FDB_UPDATED_DICTIONARY)
{
if( RC_BAD( rc) && pDb->pDict)
{
// Unlink the FDB from the FDICT. - Shouldn't have
// to lock semaphore, because the DICT is NOT linked
// to the FFILE.
flmAssert( pDb->pDict->pFile == NULL);
flmUnlinkFdbFromDict( pDb);
}
}
if (RC_BAD( rc))
{
// Since we failed to commit, do an abort. We are purposely not
// checking the return code from flmAbortDbTrans because we already
// have an error return code. If we attempted to log the transaction
// to the RFL and failed, we don't want to try to log an abort packet.
// The RFL code has already reset the log back to the starting point
// of the transaction, thereby discarding all operations.
pDb->uiFlags &= ~FDB_COMMITTING_TRANS;
(void)flmAbortDbTrans( pDb, bOkToLogAbort);
uiTransType = FLM_NO_TRANS;
// Do we need to force all handles to close?
if( bForceCloseOnError)
{
// Since the commit packet has already been logged to the RFL,
// we must have failed when trying to write the log header. The
// database is in a bad state and must be closed.
// Set the "must close" flag on all FDBs linked to the FFILE
// and set the FFILE's "must close" flag. This will cause any
// subsequent operations on the database to fail until all
// handles have been closed.
flmSetMustCloseFlags( pFile, rc, FALSE);
}
}
else
{
bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE;
if (uiTransType == FLM_UPDATE_TRANS)
{
if (gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmTransEventCallback( F_EVENT_COMMIT_TRANS, (HFDB)pDb, rc,
uiTransId);
}
// Do the BLOB and indexing work before we unlock the db.
FBListAfterCommit( pDb);
if (pDb->pIxStopList || pDb->pIxStartList)
{
// Must not call flmIndexingAfterCommit until after
// completeTransWrites. Otherwise, there is a potential
// deadlock condition where flmIndexingAfterCommit is
// waiting on an indexing thread to quit, but that
// thread is waiting to be signaled by this thread that
// writes are completed. However, flmIndexingAfterCommit
// also must only be called while the database is still
// locked. If we were to leave the database locked for
// every call to completeTransWrites, however, we would
// lose the group commit capability. Hence, we opt to
// only lose it when there are actual indexing operations
// to start or stop - which should be very few transactions.
// That is what the bIndexAfterCommit flag is for.
bIndexAfterCommit = TRUE;
}
}
}
// Unlock the database, if the update transaction is still going.
// NOTE: We check uiTransType because it may have been reset
// to FLM_NO_TRANS up above if flmAbortDbTrans was called.
if (uiTransType == FLM_UPDATE_TRANS)
{
if (RC_BAD( rc))
{
// SHOULD NEVER HAPPEN - because it would have been taken
// care of above - flmAbortDbTrans would have been called and
// uiTransType would no longer be FLM_UPDATE_TRANS.
flmAssert( 0);
(void)pFile->pRfl->completeTransWrites( pDb, FALSE, TRUE);
}
else if (!bForceCheckpoint)
{
if (bIndexAfterCommit)
{
rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE);
flmIndexingAfterCommit( pDb);
flmUnlinkDbFromTrans( pDb, TRUE);
}
else
{
rc = pFile->pRfl->completeTransWrites( pDb, TRUE, TRUE);
}
}
else
{
// Do checkpoint, if forcing. Before doing the checkpoint
// we have to make sure the roll-forward log writes
// complete. We don't want to unlock the DB while the
// writes are happening in this case - thus, the FALSE
// parameter to completeTransWrites.
if (RC_OK( rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE)))
{
bForceCloseOnError = FALSE;
rc = ScaDoCheckpoint( pDbStats, pDb->pSFileHdl, pFile,
(pDb->uiFlags & FDB_DO_TRUNCATE) ? TRUE : FALSE,
TRUE, CP_TIME_INTERVAL_REASON,
uiCPFileNum, uiCPOffset);
}
if (bIndexAfterCommit)
{
flmIndexingAfterCommit( pDb);
}
flmUnlinkDbFromTrans( pDb, TRUE);
}
if (RC_BAD( rc) && bForceCloseOnError)
{
// Since the commit packet has already been logged to the RFL,
// we must have failed when trying to write the log header. The
// database is in a bad state and must be closed.
// Set the "must close" flag on all FDBs linked to the FFILE
// and set the FFILE's "must close" flag. This will cause any
// subsequent operations on the database to fail until all
// handles have been closed.
flmSetMustCloseFlags( pFile, rc, FALSE);
}
}
else
{
// Unlink the database from the transaction
// structure as well as from the FDICT structure.
flmUnlinkDbFromTrans( pDb, FALSE);
}
if (pDbStats && uiTransType != FLM_NO_TRANS)
{
FLMUINT64 ui64ElapMilli = 0;
flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli);
pDbStats->bHaveStats = TRUE;
if (uiTransType == FLM_READ_TRANS)
{
pDbStats->ReadTransStats.CommittedTrans.ui64Count++;
pDbStats->ReadTransStats.CommittedTrans.ui64ElapMilli +=
ui64ElapMilli;
if (bInvisibleTrans)
{
pDbStats->ReadTransStats.InvisibleTrans.ui64Count++;
pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli +=
ui64ElapMilli;
}
}
else
{
pDbStats->UpdateTransStats.CommittedTrans.ui64Count++;
pDbStats->UpdateTransStats.CommittedTrans.ui64ElapMilli +=
ui64ElapMilli;
}
}
// Update stats
if (pDb->pStats)
{
(void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats);
}
Exit:
pDb->uiFlags &= ~FDB_COMMITTING_TRANS;
return( rc);
}