git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@528 0109f412-320b-0410-ab79-c3e0c5ffbbe6
588 lines
15 KiB
C++
588 lines
15 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Routines for working with and FDB database handle structure.
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1999-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: fdb.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
FSTATIC void flmLogMustCloseReason(
|
|
FFILE * pFile,
|
|
const char * pszFileName,
|
|
FLMINT iLineNumber);
|
|
|
|
/****************************************************************************
|
|
Desc: This function will use the FDB for use by the current thread.
|
|
If another thread already has the FDB used, it will go into the
|
|
debugger.
|
|
****************************************************************************/
|
|
#if defined( FLM_DEBUG)
|
|
void fdbUseCheck(
|
|
FDB * pDb)
|
|
{
|
|
FLMUINT uiMyThreadId = (FLMUINT)f_threadId();
|
|
|
|
f_mutexLock( pDb->hMutex);
|
|
if (!pDb->uiUseCount)
|
|
{
|
|
pDb->uiUseCount++;
|
|
pDb->uiThreadId = uiMyThreadId;
|
|
}
|
|
else if (pDb->uiThreadId != uiMyThreadId)
|
|
{
|
|
flmAssert( 0);
|
|
}
|
|
else
|
|
{
|
|
pDb->uiUseCount++;
|
|
}
|
|
f_mutexUnlock( pDb->hMutex);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc: This function will unuse the FDB for use by the current thread.
|
|
****************************************************************************/
|
|
#if defined( FLM_DEBUG)
|
|
void fdbUnuse(
|
|
FDB * pDb)
|
|
{
|
|
FLMUINT uiMyThreadId = (FLMUINT)f_threadId();
|
|
|
|
f_mutexLock( pDb->hMutex);
|
|
if ((!pDb->uiUseCount) || (uiMyThreadId != pDb->uiThreadId))
|
|
{
|
|
flmAssert( 0);
|
|
}
|
|
else
|
|
{
|
|
pDb->uiUseCount--;
|
|
}
|
|
f_mutexUnlock( pDb->hMutex);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc: This function will init an FDB for a database that is being handled
|
|
via a client/server connection.
|
|
****************************************************************************/
|
|
void fdbInitCS(
|
|
FDB * pDb)
|
|
{
|
|
if (pDb)
|
|
{
|
|
fdbUseCheck( pDb);
|
|
(void)flmResetDiag( pDb);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This function will init an FDB for use. It will also start
|
|
the necessary type of transaction - if any.
|
|
****************************************************************************/
|
|
RCODE fdbInit(
|
|
FDB * pDb, // Pointer to database.
|
|
FLMUINT uiTransType, // Type of transaction to start.
|
|
FLMUINT uiFlags, // Flags for function.
|
|
FLMUINT uiAutoTrans, // Auto transaction OK? Used only if
|
|
// uiTransType == FLM_UPDATE_TRANS. This
|
|
// also has the max lock wait time.
|
|
FLMBOOL * pbStartedTransRV // Returns flag indicating whether or not
|
|
// we started a transaction inside this
|
|
// routine.
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiTransFlags = FLM_GET_TRANS_FLAGS( uiTransType);
|
|
|
|
uiTransType = FLM_GET_TRANS_TYPE( uiTransType);
|
|
|
|
if( pbStartedTransRV)
|
|
{
|
|
*pbStartedTransRV = FALSE;
|
|
}
|
|
|
|
fdbUseCheck( pDb);
|
|
if (!pDb->uiInitNestLevel)
|
|
{
|
|
if (!(uiFlags & FDB_DONT_RESET_DIAG) && !pDb->uiInitNestLevel)
|
|
{
|
|
(void)flmResetDiag( pDb);
|
|
}
|
|
|
|
if (!gv_FlmSysData.Stats.bCollectingStats)
|
|
{
|
|
pDb->pStats = NULL;
|
|
pDb->pDbStats = NULL;
|
|
}
|
|
else
|
|
{
|
|
pDb->pStats = &pDb->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 (!pDb->Stats.bCollectingStats)
|
|
{
|
|
flmStatStart( &pDb->Stats);
|
|
}
|
|
else if (pDb->Stats.uiStartTime < gv_FlmSysData.Stats.uiStartTime)
|
|
{
|
|
flmStatReset( &pDb->Stats, FALSE, FALSE);
|
|
}
|
|
(void)flmStatGetDb( &pDb->Stats, pDb->pFile,
|
|
0, &pDb->pDbStats, NULL, NULL);
|
|
pDb->pLFileStats = NULL;
|
|
}
|
|
}
|
|
|
|
pDb->uiInitNestLevel++;
|
|
|
|
// Now that the nest level has been incremented, test
|
|
// to see if the database is being forced to close.
|
|
|
|
if( !(uiFlags & FDB_CLOSING_OK))
|
|
{
|
|
if( RC_BAD( rc = flmCheckDatabaseState( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
/*
|
|
If uiTransType == FLM_NO_TRANS, lock down the default dictionary
|
|
if the FDB is not currently involved in a transaction. If the
|
|
FDB is already involved in a transaction, there is no need to
|
|
lock down anything, because the FDICT structure and
|
|
tables will already be locked down.
|
|
*/
|
|
|
|
if (uiTransType == FLM_NO_TRANS)
|
|
{
|
|
if (pDb->uiTransType == FLM_NO_TRANS)
|
|
{
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
if (pDb->pFile->pDictList && pDb->pDict != pDb->pFile->pDictList)
|
|
{
|
|
flmLinkFdbToDict( pDb, pDb->pFile->pDictList);
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
If they are requesting a read transaction, set the FLM_AUTO_TRANS
|
|
bit to TRUE.
|
|
*/
|
|
|
|
if (uiTransType == FLM_READ_TRANS)
|
|
{
|
|
uiAutoTrans |= FLM_AUTO_TRANS;
|
|
}
|
|
else
|
|
{
|
|
if (pDb->uiTransType == FLM_UPDATE_TRANS)
|
|
{
|
|
pDb->bHadUpdOper = TRUE;
|
|
}
|
|
|
|
/* uiTransType == FLM_UPDATE_TRANS, make sure updates are OK. */
|
|
|
|
if (pDb->uiFlags & FDB_FILE_LOCK_SHARED)
|
|
{
|
|
// There is a shared lock on the database.
|
|
rc = RC_SET( FERR_PERMISSION);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Test_Trans:
|
|
|
|
/* See if we already have a transaction going on the DB. */
|
|
|
|
if (pDb->uiTransType != FLM_NO_TRANS)
|
|
{
|
|
/*
|
|
If the transaction is an invisible transaction, we may need to
|
|
abort it, depending on what is being asked for.
|
|
*/
|
|
|
|
if (pDb->uiFlags & FDB_INVISIBLE_TRANS)
|
|
{
|
|
/*
|
|
Several conditions will cause us to abort an invisible
|
|
transaction:
|
|
|
|
1. If it is NOT ok for a transaction to already be in progress.
|
|
That is, the caller does NOT want to piggy-back on an
|
|
already running transaction.
|
|
|
|
2. If it is a transaction that has been marked as being
|
|
required to abort because of some error.
|
|
|
|
3. If the transaction needed is an update transaction, but
|
|
the invisible transaction is a read transaction.
|
|
|
|
4. The flag requesting that we join invisible transactions
|
|
is not set.
|
|
|
|
*/
|
|
|
|
if ((!(uiFlags & FDB_TRANS_GOING_OK)) ||
|
|
(!(uiFlags & FDB_INVISIBLE_TRANS_OK)) ||
|
|
(flmCheckBadTrans( pDb)) ||
|
|
((uiTransType == FLM_UPDATE_TRANS) &&
|
|
(pDb->uiTransType != FLM_UPDATE_TRANS)))
|
|
{
|
|
if (RC_BAD( rc = flmAbortDbTrans( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
goto Test_Trans;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !(uiFlags & FDB_TRANS_GOING_OK))
|
|
{
|
|
rc = RC_SET( FERR_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
/* See if the transaction should be aborted. */
|
|
|
|
if( flmCheckBadTrans( pDb))
|
|
{
|
|
rc = RC_SET( FERR_ABORT_TRANS);
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
If we need an update transaction, make sure that is what we
|
|
currently have going. Also, make sure we are not in read-only
|
|
mode.
|
|
*/
|
|
|
|
if ((uiTransType == FLM_UPDATE_TRANS) &&
|
|
(pDb->uiTransType != FLM_UPDATE_TRANS))
|
|
{
|
|
rc = RC_SET( FERR_ILLEGAL_TRANS_OP);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else if (!(uiAutoTrans & FLM_AUTO_TRANS))
|
|
{
|
|
rc = RC_SET( FERR_NO_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
|
|
// If we get to this point, we need to start a transaction on the
|
|
// database.
|
|
|
|
if( RC_BAD( rc = flmBeginDbTrans( pDb, uiTransType,
|
|
(FLMUINT)(0x00FF & uiAutoTrans), uiTransFlags)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( pbStartedTransRV)
|
|
{
|
|
*pbStartedTransRV = TRUE;
|
|
}
|
|
|
|
if (uiTransType == FLM_UPDATE_TRANS)
|
|
{
|
|
pDb->bHadUpdOper = TRUE;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
// WARNING: The calling routine must call fdbExit or flmExit to unlock
|
|
// any resources that are locked by fdbInit (even if this fdbInit
|
|
// returns an error).
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This function will unlock an FDB.
|
|
****************************************************************************/
|
|
void fdbExit(
|
|
FDB * pDb)
|
|
{
|
|
flmAssert( pDb);
|
|
|
|
if( !pDb->pCSContext)
|
|
{
|
|
flmAssert( pDb->uiInitNestLevel);
|
|
pDb->uiInitNestLevel--;
|
|
if (!pDb->uiInitNestLevel)
|
|
{
|
|
if (pDb->pDict && pDb->uiTransType == FLM_NO_TRANS)
|
|
{
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
flmUnlinkFdbFromDict( pDb);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
|
|
pDb->pStats = NULL;
|
|
}
|
|
}
|
|
fdbUnuse( pDb);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This function is used to determine if a function id corresponds to a
|
|
cursor function.
|
|
****************************************************************************/
|
|
FINLINE FLMBOOL IsQueryFunc(
|
|
FLMUINT uiFuncId)
|
|
{
|
|
return( (uiFuncId == FLM_CURSOR_CONFIG ||
|
|
uiFuncId == FLM_CURSOR_NEXT ||
|
|
uiFuncId == FLM_CURSOR_NEXT_DRN ||
|
|
uiFuncId == FLM_CURSOR_PREV ||
|
|
uiFuncId == FLM_CURSOR_PREV_DRN ||
|
|
uiFuncId == FLM_CURSOR_FIRST ||
|
|
uiFuncId == FLM_CURSOR_FIRST_DRN ||
|
|
uiFuncId == FLM_CURSOR_LAST ||
|
|
uiFuncId == FLM_CURSOR_LAST_DRN ||
|
|
uiFuncId == FLM_CURSOR_MOVE_RELATIVE ||
|
|
uiFuncId == FLM_CURSOR_REC_COUNT)
|
|
? TRUE
|
|
: FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This function is used to determine if an error should
|
|
require an update transaction to be aborted.
|
|
****************************************************************************/
|
|
FINLINE FLMBOOL IsAbortError(
|
|
FLMUINT uiFuncId,
|
|
RCODE rc)
|
|
{
|
|
return( (rc != FERR_OK &&
|
|
rc != FERR_END &&
|
|
rc != FERR_BOF_HIT &&
|
|
rc != FERR_EOF_HIT &&
|
|
rc != FERR_EXISTS &&
|
|
rc != FERR_NOT_FOUND &&
|
|
rc != FERR_NOT_UNIQUE &&
|
|
rc != FERR_BAD_FIELD_NUM &&
|
|
rc != FERR_ABORT_TRANS &&
|
|
rc != FERR_IO_FILE_LOCK_ERR &&
|
|
rc != FERR_IO_ACCESS_DENIED &&
|
|
rc != FERR_IO_PATH_NOT_FOUND &&
|
|
rc != FERR_IO_INVALID_PATH &&
|
|
rc != FERR_OLD_VIEW &&
|
|
rc != FERR_PERMISSION &&
|
|
rc != FERR_ILLEGAL_OP &&
|
|
rc != FERR_ILLEGAL_TRANS_OP &&
|
|
rc != FERR_DUPLICATE_DICT_REC &&
|
|
rc != FERR_TIMEOUT &&
|
|
rc != FERR_INDEX_OFFLINE &&
|
|
(rc != FERR_BAD_IX ||
|
|
(!IsQueryFunc( uiFuncId) && uiFuncId != FLM_INDEX_STATUS)) &&
|
|
(rc != FERR_CURSOR_SYNTAX || !IsQueryFunc( uiFuncId)))
|
|
? TRUE
|
|
: FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This function checks to see if an update transaction should be forced
|
|
to abort. It also resets the gedcom memory pool.
|
|
****************************************************************************/
|
|
void flmExit(
|
|
eFlmFuncs eFlmFuncId,
|
|
FDB * pDb,
|
|
RCODE rc)
|
|
{
|
|
|
|
// There are a few functions that may not have an FDB
|
|
|
|
if (pDb)
|
|
{
|
|
// If this is an update transaction, see if it should be aborted.
|
|
|
|
if (pDb->uiTransType == FLM_UPDATE_TRANS &&
|
|
IsAbortError( eFlmFuncId, rc))
|
|
{
|
|
|
|
// Set the abort flag
|
|
|
|
pDb->eAbortFuncId = eFlmFuncId;
|
|
pDb->AbortRc = rc;
|
|
}
|
|
|
|
// Don't reset or free the temporary pool if FLAIM func was called
|
|
// within a user call-back that called another FLAIM functions.
|
|
|
|
if (pDb->uiInFlmFunc == 0)
|
|
{
|
|
|
|
// Keep the main pool block around inbetween FLAIM calls.
|
|
|
|
pDb->TempPool.poolReset();
|
|
}
|
|
fdbExit( pDb);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Logs information about an error
|
|
****************************************************************************/
|
|
void flmLogError(
|
|
RCODE rc,
|
|
const char * pszDoing,
|
|
const char * pszFileName,
|
|
FLMINT iLineNumber)
|
|
{
|
|
flmLogMessage(
|
|
F_DEBUG_MESSAGE,
|
|
FLM_YELLOW,
|
|
FLM_BLACK,
|
|
pszFileName
|
|
? "Error %s: 0x%04X (%s), File=%s, Line=%d."
|
|
: "Error %s: 0x%04X (%s).",
|
|
pszDoing, (unsigned)rc, FlmErrorString( rc),
|
|
pszFileName ? pszFileName : "",
|
|
pszFileName ? (int)iLineNumber : 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Logs messages
|
|
****************************************************************************/
|
|
void flmLogMessage(
|
|
eLogMessageSeverity eMsgSeverity,
|
|
eColorType foreground,
|
|
eColorType background,
|
|
const char * pszFormat,
|
|
...)
|
|
{
|
|
FLMINT iLen;
|
|
f_va_list args;
|
|
IF_LogMessageClient * pLogMsg = NULL;
|
|
char * pszMsgBuf = NULL;
|
|
|
|
if( !gv_FlmSysData.pLogger)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( (pLogMsg = gv_FlmSysData.pLogger->beginMessage(
|
|
FLM_GENERAL_MESSAGE, eMsgSeverity)) != NULL)
|
|
{
|
|
if( RC_OK( f_alloc( 1024, &pszMsgBuf)))
|
|
{
|
|
f_va_start( args, pszFormat);
|
|
iLen = f_vsprintf( pszMsgBuf, pszFormat, &args);
|
|
f_va_end( args);
|
|
|
|
pLogMsg->changeColor( foreground, background);
|
|
pLogMsg->appendString( pszMsgBuf);
|
|
}
|
|
|
|
f_endLogMessage( &pLogMsg);
|
|
|
|
if( pszMsgBuf)
|
|
{
|
|
f_free( &pszMsgBuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Logs the reason for the "must close" flag being set
|
|
****************************************************************************/
|
|
FSTATIC void flmLogMustCloseReason(
|
|
FFILE * pFile,
|
|
const char * pszFileName,
|
|
FLMINT iLineNumber)
|
|
{
|
|
// Log a message indicating why the "must close" flag was set
|
|
|
|
flmLogMessage(
|
|
F_DEBUG_MESSAGE,
|
|
FLM_YELLOW,
|
|
FLM_BLACK,
|
|
"Database (%s) must be closed because of a 0x%04X error, File=%s, Line=%d.",
|
|
(pFile->pszDbPath
|
|
? pFile->pszDbPath
|
|
: ""),
|
|
(unsigned)pFile->rcMustClose,
|
|
pszFileName, (int)iLineNumber);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Checks to see if the database should be closed
|
|
****************************************************************************/
|
|
RCODE flmCheckDatabaseStateImp(
|
|
FDB * pDb,
|
|
const char * pszFileName,
|
|
FLMINT iLineNumber)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( pDb && pDb->bMustClose)
|
|
{
|
|
flmLogMustCloseReason( pDb->pFile, pszFileName, iLineNumber);
|
|
rc = RC_SET( FERR_CLOSING_DATABASE);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Checks the FFILE state
|
|
****************************************************************************/
|
|
RCODE flmCheckFFileStateImp(
|
|
FFILE * pFile,
|
|
const char * pszFileName,
|
|
FLMINT iLineNumber)
|
|
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( pFile && pFile->bMustClose)
|
|
{
|
|
flmLogMustCloseReason( pFile, pszFileName, iLineNumber);
|
|
rc = RC_SET( FERR_CLOSING_DATABASE);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|