git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@490 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1001 lines
23 KiB
C++
1001 lines
23 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Contains routines for starting a 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$
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
/****************************************************************************
|
|
Desc: This routine unlinks an F_Db from a transaction's list of F_Dbs.
|
|
****************************************************************************/
|
|
void F_Db::unlinkFromTransList(
|
|
FLMBOOL bCommitting)
|
|
{
|
|
flmAssert( m_pIxdFixups == NULL);
|
|
if( m_eTransType != SFLM_NO_TRANS)
|
|
{
|
|
if (m_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 && m_pCommitClient)
|
|
{
|
|
m_pCommitClient->commit( this);
|
|
}
|
|
unlockExclusive();
|
|
}
|
|
|
|
m_pDatabase->lockMutex();
|
|
if (m_pDict)
|
|
{
|
|
unlinkFromDict();
|
|
}
|
|
|
|
// Unlink the transaction from the F_Database if it is a read transaction.
|
|
|
|
if (m_eTransType == SFLM_READ_TRANS)
|
|
{
|
|
if (m_pNextReadTrans)
|
|
{
|
|
m_pNextReadTrans->m_pPrevReadTrans = m_pPrevReadTrans;
|
|
}
|
|
else if (!m_uiKilledTime)
|
|
{
|
|
m_pDatabase->m_pLastReadTrans = m_pPrevReadTrans;
|
|
}
|
|
if (m_pPrevReadTrans)
|
|
{
|
|
m_pPrevReadTrans->m_pNextReadTrans = m_pNextReadTrans;
|
|
}
|
|
else if (m_uiKilledTime)
|
|
{
|
|
m_pDatabase->m_pFirstKilledTrans = m_pNextReadTrans;
|
|
}
|
|
else
|
|
{
|
|
m_pDatabase->m_pFirstReadTrans = m_pNextReadTrans;
|
|
}
|
|
|
|
// Zero out so it will be zero for next transaction begin.
|
|
|
|
m_uiKilledTime = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Reset to NULL or zero for next update transaction.
|
|
|
|
m_pIxStartList = m_pIxStopList = NULL;
|
|
flmAssert( !m_pIxdFixups);
|
|
}
|
|
|
|
m_pDatabase->unlockMutex();
|
|
m_eTransType = SFLM_NO_TRANS;
|
|
m_uiFlags &= (~(FDB_UPDATED_DICTIONARY |
|
|
FDB_DONT_KILL_TRANS |
|
|
FDB_DONT_POISON_CACHE |
|
|
FDB_SWEEP_SCHEDULED));
|
|
flmAssert( !m_uiDirtyRowCount);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine reads a database's dictionary. This is called only
|
|
when we did not have a dictionary off of the F_Database object -
|
|
which will be the first transaction after a database is opened.
|
|
****************************************************************************/
|
|
RCODE F_Db::readDictionary( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
if (RC_BAD( rc = dictOpen()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pDatabase->lockMutex();
|
|
|
|
// 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( !m_pDatabase->m_pDictList);
|
|
|
|
// Link the new local dictionary to its file structure.
|
|
|
|
m_pDict->linkToDatabase( m_pDatabase);
|
|
m_pDatabase->unlockMutex();
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine starts a transaction for the specified database. The
|
|
transaction may be part of an overall larger transaction.
|
|
****************************************************************************/
|
|
RCODE F_Db::beginTrans(
|
|
eDbTransType eTransType,
|
|
FLMUINT uiMaxLockWait,
|
|
FLMUINT uiFlags,
|
|
SFLM_DB_HDR * pDbHdr)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
SFLM_DB_HDR * pLastCommittedDbHdr;
|
|
F_Rfl * pRfl = m_pDatabase->m_pRfl;
|
|
FLMUINT uiRflToken = 0;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
// Should not be calling on a temporary database
|
|
|
|
flmAssert( !m_pDatabase->m_bTempDb);
|
|
|
|
// Check the state of the database engine
|
|
|
|
if( RC_BAD( rc = checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize a few things - as few as is necessary to avoid
|
|
// unnecessary overhead.
|
|
|
|
m_AbortRc = NE_SFLM_OK;
|
|
pLastCommittedDbHdr = &m_pDatabase->m_lastCommittedDbHdr;
|
|
m_bKrefSetup = FALSE;
|
|
m_eTransType = eTransType;
|
|
m_uiThreadId = (FLMUINT)f_threadId();
|
|
m_uiTransCount++;
|
|
|
|
// Link the F_Db to the database's most current F_Dict structure,
|
|
// if there is one. Also, if it is a read transaction, link the F_Db
|
|
// into the list of read transactions off of the F_Database object.
|
|
|
|
m_pDatabase->lockMutex();
|
|
bMutexLocked = TRUE;
|
|
|
|
if (m_pDatabase->m_pDictList)
|
|
{
|
|
// Link the F_Db to the right F_Dict object
|
|
|
|
linkToDict( m_pDatabase->m_pDictList);
|
|
}
|
|
|
|
// If it is a read transaction, link into the list of
|
|
// read transactions off of the F_Database object. Until we
|
|
// get the DB header transaction ID below, we set ui64CurrTransID
|
|
// to zero and link this transaction in at the beginning of the
|
|
// list.
|
|
|
|
if (eTransType == SFLM_READ_TRANS)
|
|
{
|
|
getDbHdrInfo( pLastCommittedDbHdr);
|
|
|
|
// Link in at the end of the transaction list.
|
|
|
|
m_pNextReadTrans = NULL;
|
|
if ((m_pPrevReadTrans = m_pDatabase->m_pLastReadTrans) != NULL)
|
|
{
|
|
// Make sure transaction IDs are always in ascending order. They
|
|
// should be at this point.
|
|
|
|
flmAssert( m_pDatabase->m_pLastReadTrans->m_ui64CurrTransID <=
|
|
m_ui64CurrTransID);
|
|
m_pDatabase->m_pLastReadTrans->m_pNextReadTrans = this;
|
|
}
|
|
else
|
|
{
|
|
m_pDatabase->m_pFirstReadTrans = this;
|
|
}
|
|
m_pDatabase->m_pLastReadTrans = this;
|
|
m_uiInactiveTime = 0;
|
|
|
|
if (uiFlags & SFLM_DONT_KILL_TRANS)
|
|
{
|
|
m_uiFlags |= FDB_DONT_KILL_TRANS;
|
|
}
|
|
else
|
|
{
|
|
m_uiFlags &= ~FDB_DONT_KILL_TRANS;
|
|
}
|
|
|
|
if (pDbHdr)
|
|
{
|
|
f_memcpy( pDbHdr, &m_pDatabase->m_lastCommittedDbHdr,
|
|
sizeof( SFLM_DB_HDR));
|
|
}
|
|
}
|
|
|
|
m_pDatabase->unlockMutex();
|
|
bMutexLocked = FALSE;
|
|
|
|
if (uiFlags & SFLM_DONT_POISON_CACHE)
|
|
{
|
|
m_uiFlags |= FDB_DONT_POISON_CACHE;
|
|
}
|
|
else
|
|
{
|
|
m_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 (eTransType != SFLM_READ_TRANS)
|
|
{
|
|
// Set the m_bHadUpdOper to TRUE for all transactions to begin with.
|
|
// Many calls to beginTrans 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.
|
|
|
|
m_bHadUpdOper = TRUE;
|
|
|
|
// Initialize the count of blocks changed to be 0
|
|
|
|
m_uiBlkChangeCnt = 0;
|
|
|
|
if (RC_BAD( rc = lockExclusive( 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 != NE_SFLM_OK), we don't want to
|
|
// start up a new update transaction until it is resolved.
|
|
|
|
if( !pRfl->seeIfRflVolumeOk() || RC_BAD( m_pDatabase->m_CheckpointRc))
|
|
|
|
{
|
|
rc = RC_SET( NE_SFLM_MUST_WAIT_CHECKPOINT);
|
|
goto Exit;
|
|
}
|
|
|
|
// Set the first log block address to zero.
|
|
|
|
m_pDatabase->m_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( &m_pDatabase->m_uncommittedDbHdr, pLastCommittedDbHdr,
|
|
sizeof( SFLM_DB_HDR));
|
|
getDbHdrInfo( pLastCommittedDbHdr);
|
|
|
|
// Need to increment the current checkpoint for update transactions
|
|
// so that it will be correct when we go to mark cache blocks.
|
|
|
|
if (m_uiFlags & FDB_REPLAYING_RFL)
|
|
{
|
|
// During recovery we need to set the transaction ID to the
|
|
// transaction ID that was logged.
|
|
|
|
m_ui64CurrTransID = pRfl->getCurrTransID();
|
|
}
|
|
else
|
|
{
|
|
m_ui64CurrTransID++;
|
|
}
|
|
|
|
// Link F_Db to the most current local dictionary, if there
|
|
// is one.
|
|
|
|
m_pDatabase->lockMutex();
|
|
if (m_pDatabase->m_pDictList != m_pDict &&
|
|
m_pDatabase->m_pDictList)
|
|
{
|
|
linkToDict( m_pDatabase->m_pDictList);
|
|
}
|
|
m_pDatabase->unlockMutex();
|
|
|
|
// Set the transaction EOF to the current file EOF
|
|
|
|
m_uiTransEOF = m_uiLogicalEOF;
|
|
|
|
// Put the transaction ID into the uncommitted log header.
|
|
|
|
m_pDatabase->m_uncommittedDbHdr.ui64CurrTransID = m_ui64CurrTransID;
|
|
|
|
if (pDbHdr)
|
|
{
|
|
f_memcpy( pDbHdr, &m_pDatabase->m_uncommittedDbHdr,
|
|
sizeof( SFLM_DB_HDR));
|
|
}
|
|
}
|
|
|
|
// Set up to collect statistics. We only do this at transaction
|
|
// begin and not on any other type of operation. So this is the
|
|
// only time when an F_Db will sense that statistics have been
|
|
// turned on or off.
|
|
|
|
if (!gv_SFlmSysData.Stats.bCollectingStats)
|
|
{
|
|
m_pStats = NULL;
|
|
m_pDbStats = NULL;
|
|
}
|
|
else
|
|
{
|
|
m_pStats = &m_Stats;
|
|
|
|
// Statistics are being collected for the system. Therefore,
|
|
// if we are not currently collecting statistics in the
|
|
// session, start. If we were collecting statistics, but the
|
|
// start time was earlier than the start time in the system
|
|
// statistics structure, reset the statistics in the session.
|
|
|
|
if (!m_Stats.bCollectingStats)
|
|
{
|
|
flmStatStart( &m_Stats);
|
|
}
|
|
else if (m_Stats.uiStartTime < gv_SFlmSysData.Stats.uiStartTime)
|
|
{
|
|
flmStatReset( &m_Stats, FALSE);
|
|
}
|
|
(void)flmStatGetDb( &m_Stats, m_pDatabase,
|
|
0, &m_pDbStats, NULL, NULL);
|
|
m_pLFileStats = NULL;
|
|
}
|
|
|
|
if (m_pDbStats)
|
|
{
|
|
f_timeGetTimeStamp( &m_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 (!m_pDict)
|
|
{
|
|
if (eTransType != SFLM_READ_TRANS)
|
|
{
|
|
pRfl->disableLogging( &uiRflToken);
|
|
}
|
|
|
|
if (RC_BAD( rc = readDictionary()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
m_pDatabase->unlockMutex();
|
|
}
|
|
|
|
if( uiRflToken)
|
|
{
|
|
pRfl->enableLogging( &uiRflToken);
|
|
}
|
|
|
|
if (eTransType != SFLM_READ_TRANS)
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = pRfl->logBeginTransaction( this);
|
|
}
|
|
#ifdef FLM_DBG_LOG
|
|
flmDbgLogUpdate( m_pDatabase, m_ui64CurrTransID,
|
|
0, 0, rc, "TBeg");
|
|
#endif
|
|
}
|
|
|
|
if (eTransType == SFLM_UPDATE_TRANS &&
|
|
gv_SFlmSysData.EventHdrs [SFLM_EVENT_UPDATES].pEventCBList)
|
|
{
|
|
flmTransEventCallback( SFLM_EVENT_BEGIN_TRANS, this, rc,
|
|
(FLMUINT)(RC_OK( rc)
|
|
? m_ui64CurrTransID
|
|
: (FLMUINT64)0));
|
|
}
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
// If there was an error, unlink the database from the transaction
|
|
// structure as well as from the FDICT structure. Also dump any nodes
|
|
// that are already in the cache.
|
|
|
|
unlinkFromTransList( FALSE);
|
|
|
|
if (m_pStats)
|
|
{
|
|
(void)flmStatUpdate( &m_Stats);
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine starts a transaction for the specified database. It uses
|
|
the transaction information in the passed in pDb.
|
|
****************************************************************************/
|
|
RCODE F_Db::beginTrans(
|
|
F_Db * pDb)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
// Should not be calling on a temporary database
|
|
|
|
flmAssert( !m_pDatabase->m_bTempDb);
|
|
|
|
// pDb better be running a read transaction.
|
|
|
|
flmAssert( pDb->m_eTransType == SFLM_READ_TRANS);
|
|
|
|
if( RC_BAD( rc = checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize a few things - as few as is necessary to avoid
|
|
// unnecessary overhead.
|
|
|
|
m_AbortRc = NE_SFLM_OK;
|
|
m_bKrefSetup = FALSE;
|
|
m_eTransType = SFLM_READ_TRANS;
|
|
m_uiThreadId = (FLMUINT)f_threadId();
|
|
m_uiTransCount++;
|
|
|
|
// Link the F_Db to the database's most current F_Dict structure,
|
|
// if there is one. Also, if it is a read transaction, link the F_Db
|
|
// into the list of read transactions off of the F_Database object.
|
|
|
|
m_pDatabase->lockMutex();
|
|
bMutexLocked = TRUE;
|
|
|
|
// Link to the same dictionary as pDb.
|
|
|
|
linkToDict( pDb->m_pDict);
|
|
|
|
// If it is a read transaction, link into the list of
|
|
// read transactions off of the F_Database object. Until we
|
|
// get the DB header transaction ID below, we set ui64CurrTransID
|
|
// to zero and link this transaction in at the beginning of the
|
|
// list.
|
|
|
|
getDbHdrInfo( pDb);
|
|
|
|
// Link into the transaction list right after the point where
|
|
// pDb is linked in. We need to keep transaction IDs in ascending
|
|
// order.
|
|
|
|
m_pPrevReadTrans = pDb;
|
|
if ((m_pNextReadTrans = pDb->m_pNextReadTrans) != NULL)
|
|
{
|
|
m_pNextReadTrans->m_pPrevReadTrans = this;
|
|
}
|
|
else
|
|
{
|
|
m_pDatabase->m_pLastReadTrans = this;
|
|
}
|
|
pDb->m_pNextReadTrans = this;
|
|
|
|
m_uiInactiveTime = 0;
|
|
|
|
if (pDb->m_uiFlags & FDB_DONT_KILL_TRANS)
|
|
{
|
|
m_uiFlags |= FDB_DONT_KILL_TRANS;
|
|
}
|
|
else
|
|
{
|
|
m_uiFlags &= ~FDB_DONT_KILL_TRANS;
|
|
}
|
|
if (pDb->m_uiFlags & FDB_DONT_POISON_CACHE)
|
|
{
|
|
m_uiFlags |= FDB_DONT_POISON_CACHE;
|
|
}
|
|
else
|
|
{
|
|
m_uiFlags &= ~FDB_DONT_POISON_CACHE;
|
|
}
|
|
|
|
m_pDatabase->unlockMutex();
|
|
bMutexLocked = FALSE;
|
|
|
|
// Set up to collect statistics. We only do this at transaction
|
|
// begin and not on any other type of operation. So this is the
|
|
// only time when an F_Db will sense that statistics have been
|
|
// turned on or off.
|
|
|
|
if (!gv_SFlmSysData.Stats.bCollectingStats)
|
|
{
|
|
m_pStats = NULL;
|
|
m_pDbStats = NULL;
|
|
}
|
|
else
|
|
{
|
|
m_pStats = &m_Stats;
|
|
|
|
// Statistics are being collected for the system. Therefore,
|
|
// if we are not currently collecting statistics in the
|
|
// session, start. If we were collecting statistics, but the
|
|
// start time was earlier than the start time in the system
|
|
// statistics structure, reset the statistics in the session.
|
|
|
|
if (!m_Stats.bCollectingStats)
|
|
{
|
|
flmStatStart( &m_Stats);
|
|
}
|
|
else if (m_Stats.uiStartTime < gv_SFlmSysData.Stats.uiStartTime)
|
|
{
|
|
flmStatReset( &m_Stats, FALSE);
|
|
}
|
|
(void)flmStatGetDb( &m_Stats, m_pDatabase,
|
|
0, &m_pDbStats, NULL, NULL);
|
|
m_pLFileStats = NULL;
|
|
}
|
|
|
|
if (m_pDbStats)
|
|
{
|
|
f_timeGetTimeStamp( &m_TransStartTime);
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
m_pDatabase->unlockMutex();
|
|
}
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
// If there was an error, unlink the database from the transaction
|
|
// structure as well as from the FDICT structure. Also dump any nodes
|
|
// that are already in the cache.
|
|
|
|
unlinkFromTransList( FALSE);
|
|
|
|
if (m_pStats)
|
|
{
|
|
(void)flmStatUpdate( &m_Stats);
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Starts a transaction.
|
|
****************************************************************************/
|
|
RCODE F_Db::transBegin(
|
|
eDbTransType eTransType,
|
|
FLMUINT uiMaxLockWait,
|
|
FLMUINT uiFlags,
|
|
SFLM_DB_HDR * pDbHdr)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
// Verify the transaction type.
|
|
|
|
if (eTransType != SFLM_UPDATE_TRANS && eTransType != SFLM_READ_TRANS)
|
|
{
|
|
rc = RC_SET( NE_SFLM_ILLEGAL_TRANS_TYPE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify the transaction flags
|
|
|
|
if ((uiFlags & SFLM_DONT_KILL_TRANS) && eTransType != SFLM_READ_TRANS)
|
|
{
|
|
rc = RC_SET( NE_SFLM_ILLEGAL_TRANS_TYPE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Can't start an update transaction on a database that
|
|
// is locked in shared mode.
|
|
|
|
if (eTransType == SFLM_UPDATE_TRANS && (m_uiFlags & FDB_FILE_LOCK_SHARED))
|
|
{
|
|
rc = RC_SET( NE_SFLM_SHARED_LOCK);
|
|
goto Exit;
|
|
}
|
|
|
|
// If the database is not running a transaction, start one.
|
|
|
|
if (m_eTransType != SFLM_NO_TRANS)
|
|
{
|
|
|
|
// Cannot nest transactions.
|
|
|
|
rc = RC_SET( NE_SFLM_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = beginTrans( eTransType,
|
|
uiMaxLockWait, uiFlags, pDbHdr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_bHadUpdOper = FALSE;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Starts a transaction.
|
|
****************************************************************************/
|
|
RCODE F_Db::transBegin(
|
|
F_Db * pDb)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
// Database cannot already be running a transaction.
|
|
|
|
if (m_eTransType != SFLM_NO_TRANS)
|
|
{
|
|
|
|
// Cannot nest transactions.
|
|
|
|
rc = RC_SET( NE_SFLM_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify the transaction type.
|
|
|
|
if (((F_Db *)pDb)->m_eTransType != SFLM_READ_TRANS)
|
|
{
|
|
rc = RC_SET( NE_SFLM_ILLEGAL_TRANS_TYPE);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = beginTrans( (F_Db *)pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_bHadUpdOper = FALSE;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Obtains a a lock on the database.
|
|
****************************************************************************/
|
|
RCODE F_Db::dbLock(
|
|
eLockType eLockType,
|
|
FLMINT iPriority,
|
|
FLMUINT uiTimeout)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
// eLockType better be exclusive or shared
|
|
|
|
if (eLockType != FLM_LOCK_EXCLUSIVE && eLockType != FLM_LOCK_SHARED)
|
|
{
|
|
rc = RC_SET( NE_SFLM_ILLEGAL_OP);
|
|
goto Exit;
|
|
}
|
|
|
|
// Nesting of locks is not allowed - this test also keeps this call from
|
|
// being executed inside an update transaction that implicitly acquired
|
|
// the lock.
|
|
|
|
if (m_uiFlags &
|
|
(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED | FDB_FILE_LOCK_IMPLICIT))
|
|
{
|
|
rc = RC_SET( NE_SFLM_ILLEGAL_OP);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Attempt to acquire the lock.
|
|
|
|
if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->lock( m_hWaitSem,
|
|
(FLMBOOL)((eLockType == FLM_LOCK_EXCLUSIVE)
|
|
? (FLMBOOL)TRUE
|
|
: (FLMBOOL)FALSE),
|
|
uiTimeout, iPriority,
|
|
m_pDbStats ? &m_pDbStats->LockStats : NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_uiFlags |= FDB_HAS_FILE_LOCK;
|
|
if (eLockType == FLM_LOCK_SHARED)
|
|
{
|
|
m_uiFlags |= FDB_FILE_LOCK_SHARED;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Releases a lock on the database
|
|
****************************************************************************/
|
|
RCODE F_Db::dbUnlock( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
// If we don't have an explicit lock, can't do the unlock. It is
|
|
// also illegal to do the unlock during an update transaction.
|
|
|
|
if (!(m_uiFlags & FDB_HAS_FILE_LOCK) ||
|
|
(m_uiFlags & FDB_FILE_LOCK_IMPLICIT) ||
|
|
(m_eTransType == SFLM_UPDATE_TRANS))
|
|
{
|
|
rc = RC_SET( NE_SFLM_ILLEGAL_OP);
|
|
goto Exit;
|
|
}
|
|
|
|
// Unlock the file.
|
|
|
|
if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->unlock()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Unset the flags that indicated the file was explicitly locked.
|
|
|
|
m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED));
|
|
|
|
Exit:
|
|
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = checkState( __FILE__, __LINE__);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Returns information about current and pending locks on the
|
|
database.
|
|
****************************************************************************/
|
|
RCODE F_Db::getLockInfo(
|
|
FLMINT iPriority,
|
|
eLockType * peCurrLockType,
|
|
FLMUINT * puiThreadId,
|
|
FLMUINT * puiNumExclQueued,
|
|
FLMUINT * puiNumSharedQueued,
|
|
FLMUINT * puiPriorityCount)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
if (RC_BAD( rc = checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pDatabase->m_pDatabaseLockObj->getLockInfo( iPriority,
|
|
peCurrLockType, puiThreadId,
|
|
puiNumExclQueued, puiNumSharedQueued,
|
|
puiPriorityCount);
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Returns information about the lock held by the specified database
|
|
handle.
|
|
****************************************************************************/
|
|
RCODE F_Db::getLockType(
|
|
eLockType * peLockType,
|
|
FLMBOOL * pbImplicit)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
if (peLockType)
|
|
{
|
|
*peLockType = FLM_LOCK_NONE;
|
|
}
|
|
|
|
if (pbImplicit)
|
|
{
|
|
*pbImplicit = FALSE;
|
|
}
|
|
|
|
if (RC_BAD( rc = checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_uiFlags & FDB_HAS_FILE_LOCK)
|
|
{
|
|
if (peLockType)
|
|
{
|
|
if (m_uiFlags & FDB_FILE_LOCK_SHARED)
|
|
{
|
|
*peLockType = FLM_LOCK_SHARED;
|
|
}
|
|
else
|
|
{
|
|
*peLockType = FLM_LOCK_EXCLUSIVE;
|
|
}
|
|
}
|
|
|
|
if (pbImplicit)
|
|
{
|
|
*pbImplicit = (m_uiFlags & FDB_FILE_LOCK_IMPLICIT)
|
|
? TRUE
|
|
: FALSE;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Forces a checkpoint on the database.
|
|
****************************************************************************/
|
|
RCODE F_Db::doCheckpoint(
|
|
FLMUINT uiTimeout)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
if (RC_BAD( rc = checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Start an update transaction. Must not already be one going.
|
|
|
|
if (m_eTransType != SFLM_NO_TRANS)
|
|
{
|
|
rc = RC_SET( NE_SFLM_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
// If we get to this point, we need to start a transaction on the
|
|
// database.
|
|
|
|
if( RC_BAD( rc = beginTrans( SFLM_UPDATE_TRANS, uiTimeout)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Commit the transaction, forcing it to be checkpointed.
|
|
|
|
m_bHadUpdOper = FALSE;
|
|
if (RC_BAD( rc = commitTrans( 0, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine locks a database for exclusive access.
|
|
****************************************************************************/
|
|
RCODE F_Db::lockExclusive(
|
|
FLMUINT uiMaxLockWait)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBOOL bGotFileLock = FALSE;
|
|
|
|
flmAssert( !m_pDatabase->m_bTempDb);
|
|
|
|
// There must NOT be a shared lock on the file.
|
|
|
|
if (m_uiFlags & FDB_FILE_LOCK_SHARED)
|
|
{
|
|
rc = RC_SET( NE_SFLM_SHARED_LOCK);
|
|
goto Exit;
|
|
}
|
|
|
|
// Must acquire an exclusive file lock first, if it hasn't been
|
|
// acquired.
|
|
|
|
if (!(m_uiFlags & FDB_HAS_FILE_LOCK))
|
|
{
|
|
if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->lock(
|
|
m_hWaitSem, TRUE, uiMaxLockWait, 0,
|
|
m_pDbStats ? &m_pDbStats->LockStats : NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bGotFileLock = TRUE;
|
|
m_uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT);
|
|
}
|
|
|
|
if (RC_OK( rc = m_pDatabase->dbWriteLock( m_hWaitSem, m_pDbStats, FLM_NO_TIMEOUT)))
|
|
{
|
|
m_uiFlags |= FDB_HAS_WRITE_LOCK;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (rc == NE_SFLM_DATABASE_LOCK_REQ_TIMEOUT)
|
|
{
|
|
if (bGotFileLock)
|
|
{
|
|
(void)m_pDatabase->m_pDatabaseLockObj->unlock();
|
|
m_uiFlags &= (~(FDB_HAS_FILE_LOCK |
|
|
FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK));
|
|
}
|
|
|
|
if (m_eTransType != SFLM_NO_TRANS)
|
|
{
|
|
|
|
// Unlink the DB from the transaction.
|
|
|
|
unlinkFromTransList( FALSE);
|
|
}
|
|
}
|
|
else if (RC_BAD( rc))
|
|
{
|
|
if (bGotFileLock)
|
|
{
|
|
(void)m_pDatabase->m_pDatabaseLockObj->unlock();
|
|
m_uiFlags &= (~(FDB_HAS_FILE_LOCK |
|
|
FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK));
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine unlocks a database that was previously locked
|
|
using the lockExclusive routine.
|
|
****************************************************************************/
|
|
void F_Db::unlockExclusive( void)
|
|
{
|
|
flmAssert( !m_pDatabase->m_bTempDb);
|
|
|
|
// If we have the write lock, unlock it first.
|
|
|
|
flmAssert( m_uiFlags & FDB_HAS_WRITE_LOCK);
|
|
|
|
m_pDatabase->dbWriteUnlock();
|
|
m_uiFlags &= ~FDB_HAS_WRITE_LOCK;
|
|
|
|
// Give up the file lock, if it was acquired implicitly.
|
|
|
|
if (m_uiFlags & FDB_FILE_LOCK_IMPLICIT)
|
|
{
|
|
(void)m_pDatabase->m_pDatabaseLockObj->unlock();
|
|
m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT));
|
|
}
|
|
|
|
}
|