git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@729 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2066 lines
49 KiB
C++
2066 lines
49 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Contains the F_DbSystem::openDatabase method.
|
|
//
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1990-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"
|
|
|
|
#define MAX_DIRTY_PERCENT 70
|
|
|
|
FSTATIC void flmFreeCPInfo(
|
|
CP_INFO ** ppCPInfoRV);
|
|
|
|
FSTATIC RCODE FLMAPI flmCPThread(
|
|
IF_Thread * pThread);
|
|
|
|
/***************************************************************************
|
|
Desc: Does most of the actual work of opening an existing database, but
|
|
doesn't provide COM interfaces...
|
|
****************************************************************************/
|
|
RCODE F_DbSystem::openDatabase(
|
|
const char * pszDbFileName,
|
|
const char * pszDataDir,
|
|
const char * pszRflDir,
|
|
const char * pszPassword,
|
|
FLMUINT uiOpenFlags,
|
|
F_Db ** ppDb)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
*ppDb = NULL;
|
|
if (!pszDbFileName || *pszDbFileName == 0)
|
|
{
|
|
rc = RC_SET( NE_FLM_IO_INVALID_FILENAME);
|
|
goto Exit;
|
|
}
|
|
|
|
// Open the file
|
|
|
|
if (RC_BAD( rc = openDatabase( NULL, pszDbFileName, pszDataDir, pszRflDir,
|
|
pszPassword, uiOpenFlags, FALSE, NULL, NULL, NULL, ppDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Constructor for F_Db object.
|
|
****************************************************************************/
|
|
F_Db::F_Db(
|
|
FLMBOOL bInternalOpen)
|
|
{
|
|
m_pDatabase = NULL;
|
|
m_pDict = NULL;
|
|
m_pNextForDatabase = NULL;
|
|
m_pPrevForDatabase = NULL;
|
|
m_pvAppData = NULL;
|
|
m_uiThreadId = 0;
|
|
m_bMustClose = FALSE;
|
|
m_pSFileHdl = NULL;
|
|
m_uiFlags = bInternalOpen ? FDB_INTERNAL_OPEN : 0;
|
|
m_uiTransCount = 0;
|
|
m_eTransType = SFLM_NO_TRANS;
|
|
m_AbortRc = NE_SFLM_OK;
|
|
m_ui64CurrTransID = 0;
|
|
m_uiFirstAvailBlkAddr = 0;
|
|
m_uiLogicalEOF = 0;
|
|
m_uiUpgradeCPFileNum = 0;
|
|
m_uiUpgradeCPOffset = 0;
|
|
m_uiTransEOF = 0;
|
|
f_memset( &m_TransStartTime, 0, sizeof( m_TransStartTime));
|
|
m_bHadUpdOper = FALSE;
|
|
m_uiBlkChangeCnt = 0;
|
|
m_pIxdFixups = NULL;
|
|
m_pNextReadTrans = NULL;
|
|
m_pPrevReadTrans = NULL;
|
|
m_uiInactiveTime = 0;
|
|
m_uiKilledTime = 0;
|
|
m_bItemStateUpdOk = FALSE;
|
|
m_pDeleteStatus = NULL;
|
|
m_pIxClient = NULL;
|
|
m_pIxStatus = NULL;
|
|
m_pCommitClient = NULL;
|
|
m_pStats = NULL;
|
|
m_pDbStats = NULL;
|
|
m_pLFileStats = NULL;
|
|
m_uiLFileAllocSeq = 0;
|
|
f_memset( &m_Stats, 0, sizeof( m_Stats));
|
|
m_bStatsInitialized = TRUE;
|
|
m_pIxStartList = NULL;
|
|
m_pIxStopList = NULL;
|
|
m_pCachedBTree = NULL;
|
|
m_pKeyColl = NULL;
|
|
m_uiDirtyRowCount = 0;
|
|
m_hWaitSem = F_SEM_NULL;
|
|
|
|
m_bKrefSetup = FALSE;
|
|
m_pKrefTbl = NULL;
|
|
m_uiKrefTblSize = 0;
|
|
m_uiKrefCount = 0;
|
|
m_uiTotalKrefBytes = 0;
|
|
m_pucKrefKeyBuf = NULL;
|
|
m_pKrefPool = NULL;
|
|
m_bReuseKrefPool = FALSE;
|
|
m_bKrefCompoundKey = FALSE;
|
|
m_pKrefReset = NULL;
|
|
|
|
m_tmpKrefPool.poolInit( 8192);
|
|
m_tempPool.poolInit( SFLM_MAX_KEY_SIZE * 4);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Allocates and initializes an F_Db object for a database which
|
|
is to be opened or created.
|
|
****************************************************************************/
|
|
RCODE F_DbSystem::allocDb(
|
|
F_Db ** ppDb,
|
|
FLMBOOL bInternalOpen)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
F_Db * pDb = NULL;
|
|
|
|
*ppDb = NULL;
|
|
|
|
// Allocate the F_Db object.
|
|
|
|
if ((pDb = f_new F_Db( bInternalOpen)) == NULL)
|
|
{
|
|
rc = RC_SET( NE_SFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = f_semCreate( &pDb->m_hWaitSem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*ppDb = pDb;
|
|
pDb = NULL;
|
|
|
|
Exit:
|
|
|
|
if (pDb)
|
|
{
|
|
pDb->Release();
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine performs all of the necessary steps to complete
|
|
a create or open of a database, including notifying other threads
|
|
waiting for the open or create to complete.
|
|
NOTE: If RC_BAD( rc), this routine will delete the F_Db object.
|
|
****************************************************************************/
|
|
void F_Db::completeOpenOrCreate(
|
|
RCODE rc,
|
|
FLMBOOL bNewDatabase
|
|
)
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
|
|
// If this is a newly created F_Database, we need to notify any
|
|
// threads waiting for the database to be created or opened that
|
|
// the create or open is now complete.
|
|
|
|
if (bNewDatabase)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
m_pDatabase->newDatabaseFinish( NE_SFLM_OK);
|
|
f_mutexUnlock( gv_SFlmSysData.hShareMutex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
F_Database * pDatabase = m_pDatabase;
|
|
|
|
// Temporarily increment the open count on the F_Database structure
|
|
// so that it will NOT be freed when pDb is freed below.
|
|
|
|
if (bNewDatabase)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
pDatabase->m_uiOpenIFDbCount++;
|
|
f_mutexUnlock( gv_SFlmSysData.hShareMutex);
|
|
}
|
|
|
|
// NOTE: Cannot access this F_Db object after this!
|
|
// Must do this before potentially deleting the F_Database object
|
|
// below, so that the F_Db object will unlink itself from
|
|
// the F_Database object.
|
|
Release();
|
|
|
|
// If we allocated the F_Database object, notify any
|
|
// waiting threads.
|
|
|
|
if (bNewDatabase)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
|
|
// Decrement the use count to compensate for the increment
|
|
// that occurred above.
|
|
|
|
pDatabase->m_uiOpenIFDbCount--;
|
|
|
|
// If this is a newly created F_Database, we need to notify any
|
|
// threads waiting for the database to be created or opened that
|
|
// the create or open is now complete.
|
|
|
|
pDatabase->newDatabaseFinish( rc);
|
|
pDatabase->freeDatabase();
|
|
f_mutexUnlock ( gv_SFlmSysData.hShareMutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns the length of the base part of a database name. If the
|
|
name ends with a '.' or ".db", this will not be included in the
|
|
returned length.
|
|
****************************************************************************/
|
|
void flmGetDbBasePath(
|
|
char * pszBaseDbName,
|
|
const char * pszDbName,
|
|
FLMUINT * puiBaseDbNameLen)
|
|
{
|
|
FLMUINT uiBaseLen = f_strlen( pszDbName);
|
|
|
|
if( uiBaseLen <= 3 ||
|
|
f_stricmp( &pszDbName[ uiBaseLen - 3], ".db") != 0)
|
|
{
|
|
if( pszDbName[ uiBaseLen - 1] == '.')
|
|
{
|
|
uiBaseLen--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiBaseLen -= 3;
|
|
}
|
|
|
|
f_memcpy( pszBaseDbName, pszDbName, uiBaseLen);
|
|
pszBaseDbName[ uiBaseLen] = 0;
|
|
|
|
if( puiBaseDbNameLen)
|
|
{
|
|
*puiBaseDbNameLen = uiBaseLen;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine will open a database, returning a pointer to an F_Db
|
|
object that can be used to access it.
|
|
****************************************************************************/
|
|
RCODE F_DbSystem::openDatabase(
|
|
F_Database * pDatabase,
|
|
const char * pszDbPath,
|
|
const char * pszDataDir,
|
|
const char * pszRflDir,
|
|
const char * pszPassword,
|
|
FLMUINT uiOpenFlags,
|
|
FLMBOOL bInternalOpen,
|
|
IF_RestoreClient * pRestoreObj,
|
|
IF_RestoreStatus * pRestoreStatus,
|
|
IF_FileHdl * pLockFileHdl,
|
|
F_Db ** ppDb)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBOOL bNewDatabase = FALSE;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
F_Db * pDb = NULL;
|
|
FLMBOOL bNeedToOpen = FALSE;
|
|
|
|
// Allocate and initialize an F_Db object.
|
|
|
|
if (RC_BAD( rc = allocDb( &pDb, bInternalOpen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// Free any unused structures that have been unused for the maximum
|
|
// amount of time. May unlock and re-lock the global mutex.
|
|
|
|
checkNotUsedObjects();
|
|
|
|
// Look up the file using findDatabase to see if we already
|
|
// have the file open.
|
|
|
|
if (!pDatabase)
|
|
{
|
|
bNeedToOpen = TRUE;
|
|
|
|
// May unlock and re-lock the global mutex.
|
|
|
|
if (RC_BAD( rc = findDatabase( pszDbPath, pszDataDir, &pDatabase)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (pDatabase)
|
|
{
|
|
if( RC_BAD( rc = pDatabase->checkState( __FILE__, __LINE__)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!pDatabase)
|
|
{
|
|
if (RC_BAD( rc = allocDatabase( pszDbPath, pszDataDir, FALSE, &pDatabase)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
flmAssert( !pLockFileHdl);
|
|
bNewDatabase = TRUE;
|
|
}
|
|
else if( pLockFileHdl)
|
|
{
|
|
flmAssert( pDatabase);
|
|
flmAssert( !pDatabase->m_uiOpenIFDbCount);
|
|
flmAssert( pDatabase->m_uiFlags & DBF_BEING_OPENED);
|
|
|
|
pDatabase->m_pLockFileHdl = pLockFileHdl;
|
|
|
|
// Set to NULL to prevent lock file from being released below
|
|
|
|
pLockFileHdl = NULL;
|
|
|
|
bNewDatabase = TRUE;
|
|
bNeedToOpen = TRUE;
|
|
}
|
|
else
|
|
{
|
|
FLMBOOL bWaited = FALSE;
|
|
flmAssert( !pLockFileHdl);
|
|
|
|
if (RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bWaited)
|
|
{
|
|
bNewDatabase = FALSE;
|
|
bNeedToOpen = FALSE;
|
|
}
|
|
}
|
|
|
|
// Link the F_Db object to the F_Database object.
|
|
|
|
rc = pDb->linkToDatabase( pDatabase);
|
|
f_mutexUnlock( gv_SFlmSysData.hShareMutex);
|
|
bMutexLocked = FALSE;
|
|
if (RC_BAD(rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
(void)flmStatGetDb( &pDb->m_Stats, pDatabase,
|
|
0, &pDb->m_pDbStats, NULL, NULL);
|
|
|
|
if (bNeedToOpen)
|
|
{
|
|
if (RC_BAD( rc = pDatabase->physOpen(
|
|
pDb, pszDbPath, pszRflDir, pszPassword, uiOpenFlags,
|
|
bNewDatabase, pRestoreObj, pRestoreStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Start a checkpoint thread
|
|
|
|
if( bNewDatabase && !(uiOpenFlags & SFLM_DONT_REDO_LOG))
|
|
{
|
|
flmAssert( !pDatabase->m_pCPThrd);
|
|
flmAssert( !pDatabase->m_pMaintThrd);
|
|
|
|
if( RC_BAD( rc = pDatabase->startCPThread()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( !(uiOpenFlags & SFLM_DONT_RESUME_THREADS))
|
|
{
|
|
if( RC_BAD( rc = pDb->startBackgroundIndexing()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pDatabase->startMaintThread()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_SFlmSysData.hShareMutex);
|
|
}
|
|
|
|
if (pLockFileHdl)
|
|
{
|
|
pLockFileHdl->Release();
|
|
}
|
|
|
|
if (pDb)
|
|
{
|
|
// completeOpenOrCreate will delete pDb if RC_BAD( rc)
|
|
|
|
pDb->completeOpenOrCreate( rc, bNewDatabase);
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
pDb = NULL;
|
|
}
|
|
}
|
|
*ppDb = pDb;
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine checks to see if it is OK for another F_Db to use an
|
|
F_Database object.
|
|
If so, it increments the database's use counter. NOTE: This routine
|
|
assumes that the calling routine has locked the global mutex.
|
|
****************************************************************************/
|
|
RCODE F_Database::verifyOkToUse(
|
|
FLMBOOL * pbWaited)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
F_SEM hWaitSem = F_SEM_NULL;
|
|
|
|
// Can't open the database if it is being closed by someone else.
|
|
|
|
if (m_uiFlags & DBF_BEING_CLOSED)
|
|
{
|
|
rc = RC_SET( NE_FLM_IO_ACCESS_DENIED);
|
|
goto Exit;
|
|
}
|
|
|
|
// If the file is in the process of being opened by another
|
|
// thread, wait for the open to complete.
|
|
|
|
if (m_uiFlags & DBF_BEING_OPENED)
|
|
{
|
|
if( RC_BAD( rc = f_semCreate( &hWaitSem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*pbWaited = TRUE;
|
|
if (RC_BAD( rc = flmWaitNotifyReq(
|
|
gv_SFlmSysData.hShareMutex, hWaitSem, &m_pOpenNotifies, (void *)0)))
|
|
{
|
|
// If flmWaitNotifyReq returns a bad RC, assume that the other
|
|
// thread will unlock and free the F_Database object. This
|
|
// routine should only unlock the object if an error occurs at
|
|
// some other point.
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pbWaited = FALSE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( hWaitSem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &hWaitSem);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine obtains exclusive access to a database by creating
|
|
a .lck file. FLAIM holds the .lck file open as long as the database
|
|
is open. When the database is finally closed, it deletes the .lck
|
|
file. This is only used for 3.x databases.
|
|
****************************************************************************/
|
|
RCODE flmCreateLckFile(
|
|
const char * pszFilePath,
|
|
IF_FileHdl ** ppLockFileHdlRV)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
char szLockPath [F_PATH_MAX_SIZE];
|
|
char szDbBaseName [F_FILENAME_SIZE];
|
|
char * pszFileExt;
|
|
IF_FileHdl * pLockFileHdl = NULL;
|
|
char szFilePathStr[ F_PATH_MAX_SIZE];
|
|
|
|
if( RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathToStorageString(
|
|
pszFilePath, szFilePathStr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Extract the 8.3 name and put a .lck extension on it to create
|
|
// the full path for the .lck file.
|
|
|
|
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathReduce(
|
|
szFilePathStr, szLockPath, szDbBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pszFileExt = &szDbBaseName [0];
|
|
while ((*pszFileExt) && (*pszFileExt != '.'))
|
|
pszFileExt++;
|
|
f_strcpy( pszFileExt, ".lck");
|
|
|
|
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathAppend(
|
|
szLockPath, szDbBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = gv_SFlmSysData.pFileSystem->createLockFile(
|
|
szLockPath, &pLockFileHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*ppLockFileHdlRV = (IF_FileHdl *)pLockFileHdl;
|
|
pLockFileHdl = NULL;
|
|
|
|
Exit:
|
|
|
|
if (pLockFileHdl)
|
|
{
|
|
(void)pLockFileHdl->closeFile();
|
|
pLockFileHdl->Release();
|
|
pLockFileHdl = NULL;
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine obtains exclusive access to a database by creating
|
|
a .lck file. FLAIM holds the .lck file open as long as the database
|
|
is open. When the database is finally closed, it deletes the .lck
|
|
file. This is only used for 3.x databases.
|
|
****************************************************************************/
|
|
RCODE F_Database::getExclAccess(
|
|
const char * pszFilePath)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBOOL bNotifyWaiters = FALSE;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
F_SEM hWaitSem = F_SEM_NULL;
|
|
|
|
// If m_pLockFileHdl is non-NULL, it means that we currently
|
|
// have the database locked with a lock file. There is no need to make
|
|
// this test inside a mutex lock, because the lock file handle can only
|
|
// be set to NULL when the use count goes to zero, meaning that the thread
|
|
// that sets it to NULL will be the only thread accessing it.
|
|
|
|
// However, it is possible that two or more threads will simultaneously
|
|
// test m_pLockFileHdl and discover that it is NULL. In that case,
|
|
// we allow one thread to proceed and attempt to get a lock on the database
|
|
// while the other threads wait to be notified of the results of the
|
|
// attempt to lock the database.
|
|
|
|
if (m_pLockFileHdl)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
lockMutex();
|
|
bMutexLocked = TRUE;
|
|
|
|
if (m_bBeingLocked)
|
|
{
|
|
// If the database is in the process of being locked by another
|
|
// thread, wait for the lock to complete. NOTE: flmWaitNotifyReq will
|
|
// re-lock the mutex before returning.
|
|
|
|
if( RC_BAD( rc = f_semCreate( &hWaitSem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = flmWaitNotifyReq( m_hMutex, hWaitSem, &m_pLockNotifies, (void *)0);
|
|
goto Exit;
|
|
}
|
|
|
|
// No other thread was attempting to lock the database, so
|
|
// set this thread up to make the attempt. Other threads
|
|
// coming in at this point will be required to wait and
|
|
// be notified of the results.
|
|
|
|
m_bBeingLocked = TRUE;
|
|
bNotifyWaiters = TRUE;
|
|
unlockMutex();
|
|
bMutexLocked = FALSE;
|
|
if (RC_BAD( rc = flmCreateLckFile( pszFilePath, &m_pLockFileHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (bNotifyWaiters)
|
|
{
|
|
FNOTIFY * pNotify;
|
|
F_SEM hSem;
|
|
|
|
// Notify any thread waiting on the lock what its status is.
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
lockMutex();
|
|
bMutexLocked = TRUE;
|
|
}
|
|
|
|
pNotify = m_pLockNotifies;
|
|
while (pNotify)
|
|
{
|
|
*(pNotify->pRc) = rc;
|
|
hSem = pNotify->hSem;
|
|
pNotify = pNotify->pNext;
|
|
f_semSignal( hSem);
|
|
}
|
|
|
|
m_bBeingLocked = FALSE;
|
|
m_pLockNotifies = NULL;
|
|
unlockMutex();
|
|
bMutexLocked = FALSE;
|
|
}
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
unlockMutex();
|
|
}
|
|
|
|
if( hWaitSem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &hWaitSem);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine checks to see if it is OK for another FDB to use a file.
|
|
If so, it increments the file's use counter. NOTE: This routine
|
|
assumes that the global mutex is NOT locked.
|
|
****************************************************************************/
|
|
RCODE F_Database::physOpen(
|
|
F_Db * pDb,
|
|
const char * pszFilePath,
|
|
const char * pszRflDir,
|
|
const char * pszPassword,
|
|
FLMUINT uiOpenFlags,
|
|
FLMBOOL bNewDatabase, // Is this a new F_Database object?
|
|
IF_RestoreClient * pRestoreObj,
|
|
IF_RestoreStatus * pRestoreStatus)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
// See if we need to read in the database header. If the database was
|
|
// already open (bNewDatabase == FALSE), we don't need to again.
|
|
|
|
if (bNewDatabase)
|
|
{
|
|
|
|
// Read in the database header.
|
|
|
|
if (RC_BAD( rc = readDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl,
|
|
(FLMBYTE *)pszPassword,
|
|
(uiOpenFlags & SFLM_ALLOW_LIMITED_MODE) ? TRUE : FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate the pRfl object. Could not do this until this point
|
|
// because we need to have the version number, block size, etc.
|
|
// setup in the database header.
|
|
|
|
flmAssert( !m_pRfl);
|
|
|
|
if ((m_pRfl = f_new F_Rfl) == NULL)
|
|
{
|
|
rc = RC_SET( NE_SFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = m_pRfl->setup( this, pszRflDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// We must have exclusive access. Create a lock file for that
|
|
// purpose, if there is not already a lock file.
|
|
|
|
if (!m_pLockFileHdl)
|
|
{
|
|
if (RC_BAD( rc = getExclAccess( pszFilePath)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Do a recovery to ensure a consistent database
|
|
// state before going any further. The FO_DONT_REDO_LOG
|
|
// flag is used ONLY by the VIEW program.
|
|
|
|
if (bNewDatabase && !(uiOpenFlags & SFLM_DONT_REDO_LOG))
|
|
{
|
|
if (RC_BAD( rc = doRecover( pDb, pRestoreObj, pRestoreStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
(void)pDb->m_pSFileHdl->releaseFiles();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine finishes up after creating a new F_Database object. It
|
|
will notify any other threads waiting for the operation to complete
|
|
of the status of the operation.
|
|
****************************************************************************/
|
|
void F_Database::newDatabaseFinish(
|
|
RCODE OpenRc) // Return code to send to other threads that are
|
|
// waiting for the open to complete.
|
|
{
|
|
FNOTIFY * pNotify;
|
|
F_SEM hSem;
|
|
|
|
// Notify anyone waiting on the operation what its status is.
|
|
|
|
pNotify = m_pOpenNotifies;
|
|
while (pNotify)
|
|
{
|
|
*(pNotify->pRc) = OpenRc;
|
|
hSem = pNotify->hSem;
|
|
pNotify = pNotify->pNext;
|
|
f_semSignal( hSem);
|
|
}
|
|
|
|
m_pOpenNotifies = NULL;
|
|
m_uiFlags &= (~(DBF_BEING_OPENED));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine is used to see if a file is already in use somewhere.
|
|
This is only called for files which are opened directly by the
|
|
application.
|
|
Notes: This routine assumes that the global mutex is locked, but it
|
|
may unlock and re-lock the mutex if needed.
|
|
****************************************************************************/
|
|
RCODE F_DbSystem::findDatabase(
|
|
const char * pszDbPath,
|
|
const char * pszDataDir,
|
|
F_Database ** ppDatabase)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
F_BUCKET * pBucket;
|
|
FLMUINT uiBucket;
|
|
FLMBOOL bMutexLocked = TRUE;
|
|
F_Database * pDatabase;
|
|
char szDbPathStr1 [F_PATH_MAX_SIZE];
|
|
char szDbPathStr2 [F_PATH_MAX_SIZE];
|
|
F_SEM hWaitSem = F_SEM_NULL;
|
|
|
|
*ppDatabase = NULL;
|
|
|
|
// Normalize the path to a string before looking for it.
|
|
// NOTE: On non-UNIX, non-WIN platforms, this will basically convert
|
|
// the string to upper case.
|
|
|
|
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathToStorageString(
|
|
pszDbPath, szDbPathStr1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Retry:
|
|
|
|
*ppDatabase = NULL;
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
}
|
|
|
|
pBucket = gv_SFlmSysData.pDatabaseHashTbl;
|
|
uiBucket = f_strHashBucket( szDbPathStr1, pBucket, FILE_HASH_ENTRIES);
|
|
pDatabase = (F_Database *)pBucket [uiBucket].pFirstInBucket;
|
|
while (pDatabase)
|
|
{
|
|
// Compare the strings. On non-Unix platforms we must use
|
|
// f_stricmp, because case does not matter for file names
|
|
// on those platforms.
|
|
|
|
#ifdef FLM_UNIX
|
|
if( f_strcmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0)
|
|
#else
|
|
if( f_stricmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0)
|
|
#endif
|
|
{
|
|
|
|
// Make sure data paths match.
|
|
|
|
if (pszDataDir && *pszDataDir)
|
|
{
|
|
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathToStorageString(
|
|
pszDataDir, szDbPathStr2)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (pDatabase->m_pszDataDir)
|
|
{
|
|
// f_stricmp must be used on non-unix platforms because file
|
|
// names are case insensitive on those platforms.
|
|
#ifdef FLM_UNIX
|
|
if (f_strcmp( pDatabase->m_pszDataDir, szDbPathStr2) != 0)
|
|
#else
|
|
if (f_stricmp( pDatabase->m_pszDataDir, szDbPathStr2) != 0)
|
|
#endif
|
|
{
|
|
rc = RC_SET( NE_SFLM_DATA_PATH_MISMATCH);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = RC_SET( NE_SFLM_DATA_PATH_MISMATCH);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (pDatabase->m_pszDataDir)
|
|
{
|
|
rc = RC_SET( NE_SFLM_DATA_PATH_MISMATCH);
|
|
goto Exit;
|
|
}
|
|
*ppDatabase = pDatabase;
|
|
break;
|
|
}
|
|
pDatabase = pDatabase->m_pNext;
|
|
}
|
|
|
|
if (*ppDatabase && pDatabase->m_uiFlags & DBF_BEING_CLOSED)
|
|
{
|
|
if( RC_BAD( rc = f_semCreate( &hWaitSem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Put ourselves into the notify list and then re-try
|
|
// the lookup when we wake up
|
|
|
|
if (RC_BAD( rc = flmWaitNotifyReq( gv_SFlmSysData.hShareMutex, hWaitSem,
|
|
&pDatabase->m_pCloseNotifies, (void *)0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_semDestroy( &hWaitSem);
|
|
|
|
// The mutex will be locked at this point. Re-try the lookup.
|
|
// IMPORTANT NOTE: pDatabase will have been destroyed by this
|
|
// time. DO NOT use it for anything!
|
|
|
|
goto Retry;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( hWaitSem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &hWaitSem);
|
|
}
|
|
|
|
// Make sure the global mutex is re-locked before exiting
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
}
|
|
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Make sure a database is NOT open. If it is, return an error.
|
|
****************************************************************************/
|
|
RCODE F_DbSystem::checkDatabaseClosed(
|
|
const char * pszDbName,
|
|
const char * pszDataDir)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
F_Database * pDatabase;
|
|
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
rc = findDatabase( pszDbName, pszDataDir, &pDatabase);
|
|
f_mutexUnlock( gv_SFlmSysData.hShareMutex);
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (pDatabase)
|
|
{
|
|
rc = RC_SET( NE_SFLM_DATABASE_OPEN);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Constructor for F_Database object.
|
|
****************************************************************************/
|
|
F_Database::F_Database(
|
|
FLMBOOL bTempDb)
|
|
{
|
|
m_krefPool.poolInit( DEFAULT_KREF_POOL_BLOCK_SIZE * 8);
|
|
m_pNext = NULL;
|
|
m_pPrev = NULL;
|
|
m_pFirstSQLQuery = NULL;
|
|
m_pLastSQLQuery = NULL;
|
|
m_uiBlockSize = 0;
|
|
m_uiDefaultLanguage = 0;
|
|
m_uiMaxFileSize = 0;
|
|
m_uiOpenIFDbCount = 0;
|
|
m_bTempDb = bTempDb;
|
|
m_pFirstDb = NULL;
|
|
m_pszDbPath = NULL;
|
|
m_pszDataDir = NULL;
|
|
m_pSCacheList = NULL;
|
|
m_pFirstRow = NULL;
|
|
m_pLastRow = NULL;
|
|
m_pLastDirtyRow = NULL;
|
|
m_pPendingWriteList = NULL;
|
|
m_pLastDirtyBlk = NULL;
|
|
m_pFirstInLogList = NULL;
|
|
m_pLastInLogList = NULL;
|
|
m_uiLogListCount = 0;
|
|
m_pFirstInNewList = NULL;
|
|
m_pLastInNewList = NULL;
|
|
m_uiNewCount = 0;
|
|
m_uiDirtyCacheCount = 0;
|
|
m_uiLogCacheCount = 0;
|
|
m_ppBlocksDone = NULL;
|
|
m_uiBlocksDoneArraySize = 0;
|
|
m_uiBlocksDone = 0;
|
|
m_pTransLogList = NULL;
|
|
m_pOpenNotifies = NULL;
|
|
m_pCloseNotifies = NULL;
|
|
m_pDictList = NULL;
|
|
m_bMustClose = FALSE;
|
|
m_rcMustClose = NE_SFLM_OK;
|
|
m_uiSigBitsInBlkSize = 0;
|
|
if (!bTempDb)
|
|
{
|
|
m_uiFileExtendSize = SFLM_DEFAULT_FILE_EXTEND_SIZE;
|
|
}
|
|
else
|
|
{
|
|
m_uiFileExtendSize = 65536;
|
|
}
|
|
|
|
m_pRfl = NULL;
|
|
|
|
f_memset( &m_lastCommittedDbHdr, 0, sizeof( m_lastCommittedDbHdr));
|
|
f_memset( &m_checkpointDbHdr, 0, sizeof( m_checkpointDbHdr));
|
|
f_memset( &m_uncommittedDbHdr, 0, sizeof( m_uncommittedDbHdr));
|
|
|
|
m_pBufferMgr = NULL;
|
|
m_pCurrLogBuffer = NULL;
|
|
m_uiCurrLogWriteOffset = 0;
|
|
m_uiCurrLogBlkAddr = 0;
|
|
m_pDbHdrWriteBuf = NULL;
|
|
m_pucUpdBuffer = NULL;
|
|
m_uiUpdBufferSize = 0;
|
|
m_uiUpdByteCount = 0;
|
|
m_uiUpdCharCount = 0;
|
|
m_ePendingDataType = SFLM_UNKNOWN_TYPE;
|
|
m_pPendingBTree = NULL;
|
|
m_pucBTreeTmpBlk = NULL;
|
|
m_pucBTreeTmpDefragBlk = NULL;
|
|
m_pucEntryArray = NULL;
|
|
m_pucSortedArray = NULL;
|
|
m_pucBtreeBuffer = NULL;
|
|
m_pucReplaceStruct = NULL;
|
|
m_pDatabaseLockObj = NULL;
|
|
m_pWriteLockObj = NULL;
|
|
m_pLockFileHdl = NULL;
|
|
m_pLockNotifies = NULL;
|
|
m_bBeingLocked = FALSE;
|
|
m_pFirstReadTrans = NULL;
|
|
m_pLastReadTrans = NULL;
|
|
m_pFirstKilledTrans = NULL;
|
|
m_uiFirstLogBlkAddress = 0;
|
|
m_uiFirstLogCPBlkAddress = 0;
|
|
m_uiLastCheckpointTime = 0;
|
|
m_pCPThrd = NULL;
|
|
m_pCPInfo = NULL;
|
|
m_CheckpointRc = NE_SFLM_OK;
|
|
m_uiBucket = 0;
|
|
m_uiFlags = 0;
|
|
m_bBackupActive = FALSE;
|
|
m_pMaintThrd = NULL;
|
|
m_hMaintSem = F_SEM_NULL;
|
|
m_bAllowLimitedMode = FALSE;
|
|
m_bInLimitedMode = FALSE;
|
|
m_pszDbPasswd = NULL;
|
|
m_pWrappingKey = NULL;
|
|
m_bHaveEncKey = FALSE;
|
|
m_rcLimitedCode = NE_SFLM_OK;
|
|
m_hMutex = F_MUTEX_NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This destructor frees all of the structures associated with an
|
|
F_Database object.
|
|
Whoever called this routine has already determined that it is safe
|
|
to do so.
|
|
Notes: The global mutex is assumed to be locked when entering the
|
|
routine. It may be unlocked and re-locked before the routine
|
|
exits, however.
|
|
****************************************************************************/
|
|
F_Database::~F_Database()
|
|
{
|
|
FNOTIFY * pCloseNotifies;
|
|
F_Dict * pDict;
|
|
F_Dict * pTmpDict;
|
|
|
|
// At this point, the use count better be zero
|
|
|
|
flmAssert( !m_uiOpenIFDbCount);
|
|
|
|
// Shut down all background threads before shutting down the CP thread.
|
|
|
|
shutdownDatabaseThreads();
|
|
|
|
if (m_pRfl)
|
|
{
|
|
m_pRfl->closeFile();
|
|
}
|
|
|
|
// At this point, the use count better be zero
|
|
|
|
flmAssert( !m_uiOpenIFDbCount);
|
|
|
|
// Unlock the mutex
|
|
|
|
f_mutexUnlock( gv_SFlmSysData.hShareMutex);
|
|
|
|
// Shut down the checkpoint thread
|
|
|
|
if( m_pCPThrd)
|
|
{
|
|
m_pCPThrd->stopThread();
|
|
m_pCPThrd->Release();
|
|
m_pCPThrd = NULL;
|
|
}
|
|
|
|
// Unlink all of the F_Dict objects that are connected to the
|
|
// database.
|
|
|
|
lockMutex();
|
|
while (m_pDictList)
|
|
{
|
|
m_pDictList->unlinkFromDatabase();
|
|
}
|
|
unlockMutex();
|
|
|
|
// Take the file out of its name hash bucket, if any.
|
|
|
|
if (m_uiBucket != 0xFFFF)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
if (m_pPrev)
|
|
{
|
|
m_pPrev->m_pNext = m_pNext;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pDatabaseHashTbl[ m_uiBucket].pFirstInBucket = m_pNext;
|
|
}
|
|
|
|
if (m_pNext)
|
|
{
|
|
m_pNext->m_pPrev = m_pPrev;
|
|
}
|
|
m_uiBucket = 0xFFFF;
|
|
|
|
// After this point, we should not need to keep the global mutex locked
|
|
// because the F_Database is no longer visible to any thread to find in
|
|
// the hash table.
|
|
|
|
f_mutexUnlock( gv_SFlmSysData.hShareMutex);
|
|
}
|
|
|
|
// Shouldn't have any queries at this point. But we will be nice in case
|
|
// we do and will unlink the queries from the list
|
|
|
|
flmAssert( !m_pFirstSQLQuery);
|
|
while (m_pFirstSQLQuery)
|
|
{
|
|
SQLQuery * pQuery = m_pFirstSQLQuery;
|
|
|
|
m_pFirstSQLQuery = m_pFirstSQLQuery->m_pNext;
|
|
pQuery->m_pPrev = NULL;
|
|
pQuery->m_pNext = NULL;
|
|
pQuery->m_pDatabase = NULL;
|
|
}
|
|
|
|
// Free the RFL data, if any.
|
|
|
|
if (m_pRfl)
|
|
{
|
|
m_pRfl->Release();
|
|
m_pRfl = NULL;
|
|
}
|
|
|
|
flmAssert( m_pOpenNotifies == NULL);
|
|
m_pOpenNotifies = NULL;
|
|
|
|
// Save pCloseNotifies -- we will notify any waiters once the
|
|
// F_Database has been freed.
|
|
|
|
pCloseNotifies = m_pCloseNotifies;
|
|
|
|
// Free any dictionary usage structures associated with the database.
|
|
|
|
pDict = m_pDictList;
|
|
while (pDict)
|
|
{
|
|
pTmpDict = pDict;
|
|
pDict = pDict->getNext();
|
|
pTmpDict->Release();
|
|
}
|
|
m_pDictList = NULL;
|
|
|
|
// Free any shared cache associated with the database.
|
|
// IMPORTANT NOTE:
|
|
// Cannot have the global mutex locked when these are called because
|
|
// these routines lock the block cache mutex and the node cache mutex.
|
|
// If both the global mutex and the block or node cache mutexes are to be
|
|
// locked, the rule is that the block or node cache mutex must be locked
|
|
// before locking the global mutex. This is because neededByReadTrans
|
|
// will end up doing it in this order - when neededByReadTrans is called
|
|
// either the block or node cache mutex is already locked, and it will
|
|
// additionally lock the global mutex. Since that order is already
|
|
// required, we cannot have anyone else attempting to lock the mutexes
|
|
// in a different order.
|
|
|
|
freeBlockCache();
|
|
freeRowCache();
|
|
|
|
// Release the lock objects.
|
|
|
|
if (m_pWriteLockObj)
|
|
{
|
|
m_pWriteLockObj->Release();
|
|
m_pWriteLockObj = NULL;
|
|
}
|
|
|
|
if (m_pDatabaseLockObj)
|
|
{
|
|
m_pDatabaseLockObj->Release();
|
|
m_pDatabaseLockObj = NULL;
|
|
}
|
|
|
|
// Close and delete the lock file.
|
|
|
|
if (m_pLockFileHdl)
|
|
{
|
|
(void)m_pLockFileHdl->closeFile();
|
|
m_pLockFileHdl->Release();
|
|
m_pLockFileHdl = NULL;
|
|
}
|
|
|
|
// Free the write buffer managers.
|
|
|
|
if (m_pBufferMgr)
|
|
{
|
|
m_pBufferMgr->Release();
|
|
m_pBufferMgr = NULL;
|
|
}
|
|
|
|
// Free the log header write buffer
|
|
|
|
if (m_pDbHdrWriteBuf)
|
|
{
|
|
f_freeAlignedBuffer( (void **)&m_pDbHdrWriteBuf);
|
|
}
|
|
|
|
// Free the update buffer
|
|
|
|
if (m_pucUpdBuffer)
|
|
{
|
|
f_free( &m_pucUpdBuffer);
|
|
m_uiUpdBufferSize = 0;
|
|
}
|
|
|
|
m_krefPool.poolFree();
|
|
|
|
if (m_ppBlocksDone)
|
|
{
|
|
f_free( &m_ppBlocksDone);
|
|
m_uiBlocksDoneArraySize = 0;
|
|
}
|
|
|
|
// Notify waiters that the F_Database is gone
|
|
|
|
while (pCloseNotifies)
|
|
{
|
|
F_SEM hSem;
|
|
|
|
*(pCloseNotifies->pRc) = NE_SFLM_OK;
|
|
hSem = pCloseNotifies->hSem;
|
|
pCloseNotifies = pCloseNotifies->pNext;
|
|
f_semSignal( hSem);
|
|
}
|
|
|
|
f_free( &m_pszDbPath);
|
|
|
|
// Encryption stuff
|
|
if (m_pszDbPasswd)
|
|
{
|
|
f_free( &m_pszDbPasswd);
|
|
}
|
|
if (m_pWrappingKey)
|
|
{
|
|
delete m_pWrappingKey;
|
|
}
|
|
|
|
flmAssert( !m_pFirstRow && !m_pLastRow && !m_pLastDirtyRow);
|
|
|
|
if (m_hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &m_hMutex);
|
|
}
|
|
|
|
// Global mutex is still expected to be locked at this point
|
|
|
|
f_mutexLock( gv_SFlmSysData.hShareMutex);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This frees an F_Database object.
|
|
Note: The global mutex is assumed to be locked when entering the
|
|
routine. It may be unlocked and re-locked during the destructor,
|
|
however. For this reason, this routine should be called instead of
|
|
directly deleting a database object - i.e., delete pDatabase.
|
|
****************************************************************************/
|
|
void F_Database::freeDatabase( void)
|
|
{
|
|
|
|
// See if another thread is in the process of freeing
|
|
// this F_Database. It is possible for this to happen, since
|
|
// the monitor thread may have selected this F_Database to be
|
|
// freed because it has been unused for a period of time.
|
|
// At the same time, a foreground thread could have called
|
|
// FlmConfig to close all unused F_Databases. Since the
|
|
// destructor for the F_Database object
|
|
// may unlock and re-lock the mutex, there is a small window
|
|
// of opportunity for both threads to try to free the same
|
|
// F_Database. -- Therefore, we must do this check while the
|
|
// mutex is still locked.
|
|
|
|
if (m_uiFlags & DBF_BEING_CLOSED)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Set the DBF_BEING_CLOSED flag
|
|
|
|
m_uiFlags |= DBF_BEING_CLOSED;
|
|
Release();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine sets up a new F_Database object, allocating member
|
|
variables, linking into lists, etc.
|
|
NOTE: This routine assumes that the global mutex has already
|
|
been locked. It may unlock it temporarily if there is an error,
|
|
but will always relock it before exiting.
|
|
****************************************************************************/
|
|
RCODE F_Database::setupDatabase(
|
|
const char * pszDbPath,
|
|
const char * pszDataDir)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMUINT uiAllocLen;
|
|
FLMUINT uiDbNameLen;
|
|
FLMUINT uiDirNameLen;
|
|
char szDbPathStr[ F_PATH_MAX_SIZE];
|
|
char szDataDirStr[ F_PATH_MAX_SIZE];
|
|
|
|
if( RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathToStorageString(
|
|
pszDbPath, szDbPathStr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiDbNameLen = f_strlen( szDbPathStr) + 1;
|
|
|
|
if( pszDataDir && *pszDataDir)
|
|
{
|
|
if( RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathToStorageString(
|
|
pszDataDir, szDataDirStr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiDirNameLen = f_strlen( szDataDirStr) + 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
szDataDirStr[0] = 0;
|
|
uiDirNameLen = 0;
|
|
}
|
|
|
|
|
|
if (RC_BAD( rc = f_mutexCreate( &m_hMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
uiAllocLen = (FLMUINT)(uiDbNameLen + uiDirNameLen);
|
|
if (RC_BAD( rc = f_alloc( uiAllocLen, &m_pszDbPath)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate a buffer for writing the DB header
|
|
// If we are a temporary database, there is no need
|
|
// for this allocation.
|
|
|
|
if (!m_bTempDb)
|
|
{
|
|
if( RC_BAD( rc = f_allocAlignedBuffer(
|
|
SFLM_MAX_BLOCK_SIZE, (void **)&m_pDbHdrWriteBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Setup the write buffer managers.
|
|
|
|
if( RC_BAD( rc = FlmAllocIOBufferMgr( MAX_PENDING_WRITES,
|
|
MAX_WRITE_BUFFER_BYTES, FALSE, &m_pBufferMgr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize members of F_Database object.
|
|
|
|
m_uiBucket = 0xFFFF;
|
|
m_uiFlags = DBF_BEING_OPENED;
|
|
|
|
// Copy the database name and directory.
|
|
// NOTE: uiDbNameLen includes the null terminating byte.
|
|
// and uiDirNameLen includes the null terminating byte.
|
|
|
|
f_memcpy( m_pszDbPath, szDbPathStr, uiDbNameLen);
|
|
if (uiDirNameLen)
|
|
{
|
|
m_pszDataDir = m_pszDbPath + uiDbNameLen;
|
|
f_memcpy( m_pszDataDir, szDataDirStr, uiDirNameLen);
|
|
}
|
|
|
|
// Link the file into the various lists it needs to be linked into.
|
|
|
|
if (RC_BAD( rc = linkToBucket()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate a lock object for write locking.
|
|
|
|
if( RC_BAD( rc = FlmAllocLockObject( &m_pWriteLockObj)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate a lock object for file locking.
|
|
|
|
if( RC_BAD( rc = FlmAllocLockObject( &m_pDatabaseLockObj)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine allocates a new F_Database object and links it
|
|
into its hash buckets.
|
|
NOTE: This routine assumes that the global mutex has already
|
|
been locked. It may unlock it temporarily if there is an error,
|
|
but will always relock it before exiting.
|
|
****************************************************************************/
|
|
RCODE F_DbSystem::allocDatabase(
|
|
const char * pszDbPath,
|
|
const char * pszDataDir,
|
|
FLMBOOL bTempDb,
|
|
F_Database ** ppDatabase)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
F_Database * pDatabase = NULL;
|
|
|
|
if ((pDatabase = f_new F_Database( bTempDb)) == NULL)
|
|
{
|
|
rc = RC_SET( NE_SFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = pDatabase->setupDatabase( pszDbPath, pszDataDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*ppDatabase = pDatabase;
|
|
|
|
Exit:
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (pDatabase)
|
|
{
|
|
pDatabase->freeDatabase();
|
|
}
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: This routine reads the header information for an existing
|
|
flaim database and makes sure we have a valid database.
|
|
*****************************************************************************/
|
|
RCODE F_Database::readDbHdr(
|
|
SFLM_DB_STATS * pDbStats,
|
|
F_SuperFileHdl * pSFileHdl,
|
|
FLMBYTE * pszPassword,
|
|
FLMBOOL bAllowLimited)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
IF_FileHdl * pCFileHdl = NULL;
|
|
|
|
if (RC_BAD( rc = pSFileHdl->getFileHdl( 0, FALSE, &pCFileHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Read and verify the database header.
|
|
|
|
if (RC_BAD( rc = flmReadAndVerifyHdrInfo( pDbStats, pCFileHdl,
|
|
&m_lastCommittedDbHdr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_uiBlockSize = (FLMUINT)m_lastCommittedDbHdr.ui16BlockSize;
|
|
m_uiDefaultLanguage = (FLMUINT)m_lastCommittedDbHdr.ui8DefaultLanguage;
|
|
m_uiMaxFileSize = (FLMUINT)m_lastCommittedDbHdr.ui32MaxFileSize;
|
|
m_uiSigBitsInBlkSize = calcSigBits( m_uiBlockSize);
|
|
|
|
// Initialize the master database key from the database header
|
|
m_bAllowLimitedMode = bAllowLimited;
|
|
|
|
if (pszPassword && *pszPassword)
|
|
{
|
|
if (m_pszDbPasswd)
|
|
{
|
|
f_free( &m_pszDbPasswd);
|
|
}
|
|
if ( RC_BAD( rc = f_alloc(
|
|
(f_strlen( (const char *)pszPassword) + 1), &m_pszDbPasswd)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_strcpy( (char *)m_pszDbPasswd, (const char *)pszPassword);
|
|
}
|
|
|
|
if ((m_pWrappingKey = f_new F_CCS()) == NULL)
|
|
{
|
|
RC_SET( rc = NE_SFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_OK( rc = m_pWrappingKey->init( TRUE, SFLM_AES_ENCRYPTION)))
|
|
{
|
|
// If the key was encrypted in a password, then the pszPassword parameter better
|
|
// be the key used to encrypt it. If the key was not encrypted in a password,
|
|
// then pszPassword parameter should be NULL.
|
|
rc = m_pWrappingKey->setKeyFromStore(
|
|
m_lastCommittedDbHdr.ucDbKey,
|
|
pszPassword, NULL);
|
|
}
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
// NE_SFLM_UNSUPPORTED_FEATURE is returned when we've been compiled
|
|
// without NICI support
|
|
if ((rc == NE_SFLM_UNSUPPORTED_FEATURE) || bAllowLimited)
|
|
{
|
|
m_bInLimitedMode = TRUE;
|
|
rc = NE_SFLM_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
// Note that we might still end up in limited mode if we can't verify all the keys
|
|
// that are stored in the dictionary.
|
|
|
|
Exit:
|
|
|
|
// Need to close the .db file so that we can set the block size.
|
|
// This will allow direct I/O to be used when accessing the file later.
|
|
|
|
if (pCFileHdl)
|
|
{
|
|
(void)pSFileHdl->releaseFiles();
|
|
pSFileHdl->setBlockSize( m_uiBlockSize);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: This routine frees a CP_INFO structure and all associated data.
|
|
*****************************************************************************/
|
|
FSTATIC void flmFreeCPInfo(
|
|
CP_INFO ** ppCPInfoRV)
|
|
{
|
|
CP_INFO * pCPInfo;
|
|
|
|
if ((pCPInfo = *ppCPInfoRV) != NULL)
|
|
{
|
|
if (pCPInfo->pSFileHdl)
|
|
{
|
|
pCPInfo->pSFileHdl->Release();
|
|
}
|
|
|
|
if (pCPInfo->bStatsInitialized)
|
|
{
|
|
flmStatFree( &pCPInfo->Stats);
|
|
}
|
|
|
|
if( pCPInfo->hWaitSem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &pCPInfo->hWaitSem);
|
|
}
|
|
|
|
f_free( ppCPInfoRV);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: This routine begins a thread that will do checkpoints for the
|
|
passed in database. It gives the thread its own FLAIM session and its
|
|
own handle to the database.
|
|
*****************************************************************************/
|
|
RCODE F_Database::startCPThread( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
CP_INFO * pCPInfo = NULL;
|
|
char szThreadName[ F_PATH_MAX_SIZE];
|
|
char szBaseName[ 32];
|
|
F_SuperFileClient * pSFileClient = NULL;
|
|
|
|
// Allocate a CP_INFO structure that will be passed into the
|
|
// thread when it is created.
|
|
|
|
if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( CP_INFO)), &pCPInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pCPInfo->pDatabase = this;
|
|
|
|
// Create a "wait" semaphore
|
|
|
|
if( RC_BAD( rc = f_semCreate( &pCPInfo->hWaitSem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate a super file handle.
|
|
|
|
if( (pCPInfo->pSFileHdl = f_new F_SuperFileHdl) == NULL)
|
|
{
|
|
rc = RC_SET( NE_SFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( (pSFileClient = f_new F_SuperFileClient) == NULL)
|
|
{
|
|
rc = RC_SET( NE_SFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pSFileClient->setup( m_pszDbPath, m_pszDataDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set up the super file
|
|
|
|
if (RC_BAD( rc = pCPInfo->pSFileHdl->setup( pSFileClient,
|
|
gv_SFlmSysData.pFileHdlCache, gv_SFlmSysData.uiFileOpenFlags,
|
|
gv_SFlmSysData.uiFileCreateFlags)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_lastCommittedDbHdr.ui32DbVersion)
|
|
{
|
|
pCPInfo->pSFileHdl->setBlockSize( m_uiBlockSize);
|
|
}
|
|
|
|
f_memset( &pCPInfo->Stats, 0, sizeof( SFLM_STATS));
|
|
pCPInfo->bStatsInitialized = TRUE;
|
|
|
|
// Generate the thread name
|
|
|
|
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathReduce( m_pszDbPath,
|
|
szThreadName, szBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_sprintf( (char *)szThreadName, "Checkpoint (%s)", (char *)szBaseName);
|
|
|
|
// Start the checkpoint thread.
|
|
|
|
if (RC_BAD( rc = gv_SFlmSysData.pThreadMgr->createThread( &m_pCPThrd,
|
|
flmCPThread, szThreadName, gv_SFlmSysData.uiCheckpointThreadGroup,
|
|
0, pCPInfo, NULL, 32000)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pCPInfo = pCPInfo;
|
|
pCPInfo = NULL;
|
|
|
|
Exit:
|
|
|
|
if( pCPInfo)
|
|
{
|
|
flmFreeCPInfo( &pCPInfo);
|
|
}
|
|
|
|
if( pSFileClient)
|
|
{
|
|
pSFileClient->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Try to perform a checkpoint on the database. Returns TRUE if we need
|
|
to terminate.
|
|
****************************************************************************/
|
|
FLMBOOL F_Database::tryCheckpoint(
|
|
IF_Thread * pThread,
|
|
CP_INFO * pCPInfo)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBOOL bTerminate = FALSE;
|
|
FLMBOOL bForceCheckpoint;
|
|
eForceCPReason eForceReason;
|
|
FLMUINT uiCurrTime;
|
|
SFLM_DB_STATS * pDbStats;
|
|
|
|
// See if we should terminate the thread.
|
|
|
|
if (pThread->getShutdownFlag())
|
|
{
|
|
// Set terminate flag to TRUE and then see if
|
|
// we have been set up to do one final checkpoint
|
|
// to flush dirty buffers to disk.
|
|
|
|
bTerminate = TRUE;
|
|
}
|
|
|
|
// Determine if we need to force a checkpoint.
|
|
|
|
bForceCheckpoint = FALSE;
|
|
eForceReason = SFLM_CP_NO_REASON;
|
|
uiCurrTime = (FLMUINT)FLM_GET_TIMER();
|
|
|
|
if (bTerminate)
|
|
{
|
|
bForceCheckpoint = TRUE;
|
|
eForceReason = SFLM_CP_SHUTTING_DOWN_REASON;
|
|
}
|
|
else if (!m_pRfl->seeIfRflVolumeOk() || RC_BAD( m_CheckpointRc))
|
|
{
|
|
bForceCheckpoint = TRUE;
|
|
eForceReason = SFLM_CP_RFL_VOLUME_PROBLEM;
|
|
}
|
|
else if ((FLM_ELAPSED_TIME( uiCurrTime, m_uiLastCheckpointTime) >=
|
|
gv_SFlmSysData.uiMaxCPInterval) ||
|
|
(!gv_SFlmSysData.uiMaxCPInterval))
|
|
{
|
|
bForceCheckpoint = TRUE;
|
|
eForceReason = SFLM_CP_TIME_INTERVAL_REASON;
|
|
}
|
|
|
|
if (gv_SFlmSysData.Stats.bCollectingStats)
|
|
{
|
|
|
|
// Statistics are being collected for the system. Therefore,
|
|
// if we are not currently collecting statistics in the
|
|
// start. If we were collecting statistics, but the
|
|
// start time was earlier than the start time in the system
|
|
// statistics structure, reset the statistics.
|
|
|
|
if (!pCPInfo->Stats.bCollectingStats)
|
|
{
|
|
flmStatStart( &pCPInfo->Stats);
|
|
}
|
|
else if (pCPInfo->Stats.uiStartTime <
|
|
gv_SFlmSysData.Stats.uiStartTime)
|
|
{
|
|
flmStatReset( &pCPInfo->Stats, FALSE);
|
|
}
|
|
(void)flmStatGetDb( &pCPInfo->Stats, this,
|
|
0, &pDbStats, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
pDbStats = NULL;
|
|
}
|
|
|
|
// Lock write object - If we are forcing a checkpoint
|
|
// wait until we get the lock. Otherwise, if we can't get
|
|
// the lock without waiting, don't do anything.
|
|
|
|
if (bForceCheckpoint ||
|
|
(gv_SFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache &&
|
|
(m_uiDirtyCacheCount + m_uiLogCacheCount) * m_uiBlockSize >
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache))
|
|
{
|
|
if (RC_BAD( rc = dbWriteLock( pCPInfo->hWaitSem, pDbStats, FLM_NO_TIMEOUT)))
|
|
{
|
|
|
|
// THIS SHOULD NEVER HAPPEN BECAUSE dbWriteLock will
|
|
// wait forever for the lock!
|
|
|
|
RC_UNEXPECTED_ASSERT( rc);
|
|
goto Exit;
|
|
}
|
|
pThread->setThreadStatusStr( "Forcing checkpoint");
|
|
|
|
// Must wait for any RFL writes to complete.
|
|
|
|
(void)m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, TRUE);
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( dbWriteLock( pCPInfo->hWaitSem, pDbStats, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);
|
|
|
|
// See if we actually need to do the checkpoint. If the
|
|
// current transaction ID and the last checkpoint transaction
|
|
// ID are the same, no updates have occurred that would require
|
|
// a checkpoint to take place.
|
|
|
|
if (m_lastCommittedDbHdr.ui64RflLastCPTransID ==
|
|
m_lastCommittedDbHdr.ui64CurrTransID ||
|
|
!m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, FALSE))
|
|
{
|
|
dbWriteUnlock();
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Do the checkpoint.
|
|
|
|
(void)doCheckpoint( pCPInfo->hWaitSem, pDbStats, pCPInfo->pSFileHdl, FALSE,
|
|
bForceCheckpoint, eForceReason, 0, 0);
|
|
if (pDbStats)
|
|
{
|
|
(void)flmStatUpdate( &pCPInfo->Stats);
|
|
}
|
|
|
|
dbWriteUnlock();
|
|
|
|
// Set the thread's status
|
|
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING);
|
|
|
|
Exit:
|
|
|
|
return( bTerminate);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine functions as a thread. It monitors open files and
|
|
frees up files which have been closed longer than the maximum
|
|
close time.
|
|
****************************************************************************/
|
|
FSTATIC RCODE FLMAPI flmCPThread(
|
|
IF_Thread * pThread)
|
|
{
|
|
CP_INFO * pCPInfo = (CP_INFO *)pThread->getParm1();
|
|
F_Database * pDatabase = pCPInfo->pDatabase;
|
|
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING);
|
|
for (;;)
|
|
{
|
|
f_sleep( 1000);
|
|
if (pDatabase->tryCheckpoint( pThread, pCPInfo))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);
|
|
|
|
flmFreeCPInfo( &pCPInfo);
|
|
return( NE_SFLM_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Recover a database on startup.
|
|
****************************************************************************/
|
|
RCODE F_Database::doRecover(
|
|
F_Db * pDb,
|
|
IF_RestoreClient * pRestoreObj,
|
|
IF_RestoreStatus * pRestoreStatus)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
SFLM_DB_HDR * pLastCommittedDbHdr;
|
|
|
|
// At this point, m_lastCommittedDbHdr contains the header
|
|
// that was read from disk, which will be the state of the
|
|
// header as of the last completed checkpoint. Therefore,
|
|
// we copy it into m_checkpointDbHdr.
|
|
|
|
pLastCommittedDbHdr = &m_lastCommittedDbHdr;
|
|
f_memcpy( &m_checkpointDbHdr, pLastCommittedDbHdr, sizeof( SFLM_DB_HDR));
|
|
|
|
// Do a physical rollback on the database to restore the last
|
|
// checkpoint.
|
|
|
|
if (RC_BAD( rc = pDb->physRollback(
|
|
(FLMUINT)pLastCommittedDbHdr->ui32RblEOF,
|
|
(FLMUINT)pLastCommittedDbHdr->ui32RblFirstCPBlkAddr,
|
|
TRUE,
|
|
pLastCommittedDbHdr->ui64RflLastCPTransID)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pLastCommittedDbHdr->ui32RblFirstCPBlkAddr = 0;
|
|
pLastCommittedDbHdr->ui32RblEOF = (FLMUINT32)m_uiBlockSize;
|
|
if (RC_BAD( rc = writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl,
|
|
pLastCommittedDbHdr,
|
|
&m_checkpointDbHdr, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set uiFirstLogCPBlkAddress to zero to indicate that no
|
|
// physical blocks have been logged for the current checkpoint.
|
|
// The above call to flmPhysRollback will have set the log header
|
|
// to the same thing.
|
|
|
|
m_uiFirstLogCPBlkAddress = 0;
|
|
|
|
// Set the checkpointDbHdr to be the same as the log header
|
|
|
|
f_memcpy( &m_checkpointDbHdr, pLastCommittedDbHdr, sizeof( SFLM_DB_HDR));
|
|
|
|
// Open roll forward log and redo the transactions that
|
|
// occurred since the last checkpoint, if any.
|
|
|
|
if( RC_BAD( rc = m_pRfl->recover( pDb, pRestoreObj, pRestoreStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Process the open database statement. The "OPEN DATABASE" keywords
|
|
// have already been parsed.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::processOpenDatabase( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
char szDatabaseName [F_PATH_MAX_SIZE + 1];
|
|
FLMUINT uiDatabaseNameLen;
|
|
char szDataDirName [F_PATH_MAX_SIZE + 1];
|
|
FLMUINT uiDataDirNameLen;
|
|
char szRflDirName [F_PATH_MAX_SIZE + 1];
|
|
FLMUINT uiRflDirNameLen;
|
|
F_DbSystem dbSystem;
|
|
char szPassword [300];
|
|
FLMUINT uiPasswordLen;
|
|
FLMUINT uiFlags;
|
|
char szToken [MAX_SQL_TOKEN_SIZE + 1];
|
|
FLMUINT uiTokenLineOffset;
|
|
|
|
// SYNTAX: OPEN DATABASE databasename
|
|
// [DATA_DIR=<DataDirName>] [RFL_DIR=<RflDirName>]
|
|
// [PASSWORD=<password>] [ALLOW_LIMITED]
|
|
|
|
// Whitespace must follow the "OPEN DATABASE"
|
|
|
|
if (RC_BAD( rc = skipWhitespace( TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the database name.
|
|
|
|
if (RC_BAD( rc = getUTF8String( FALSE, TRUE, (FLMBYTE *)szDatabaseName,
|
|
sizeof( szDatabaseName),
|
|
&uiDatabaseNameLen, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
szDataDirName [0] = 0;
|
|
szRflDirName [0] = 0;
|
|
szPassword [0] = 0;
|
|
uiFlags = 0;
|
|
|
|
// See if there are any options
|
|
|
|
for (;;)
|
|
{
|
|
if (RC_BAD( rc = getToken( szToken, sizeof( szToken), TRUE,
|
|
&uiTokenLineOffset, NULL)))
|
|
{
|
|
if (rc == NE_SFLM_EOF_HIT)
|
|
{
|
|
rc = NE_SFLM_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (f_stricmp( szToken, "data_dir") == 0)
|
|
{
|
|
if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szDataDirName,
|
|
sizeof( szDataDirName),
|
|
&uiDataDirNameLen, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (f_stricmp( szToken, "data_dir") == 0)
|
|
{
|
|
if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szRflDirName,
|
|
sizeof( szRflDirName),
|
|
&uiRflDirNameLen, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (f_stricmp( szToken, "password") == 0)
|
|
{
|
|
if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szPassword,
|
|
sizeof( szPassword),
|
|
&uiPasswordLen, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (f_stricmp( szToken, "allow_limited") == 0)
|
|
{
|
|
uiFlags |= SFLM_ALLOW_LIMITED_MODE;
|
|
}
|
|
else
|
|
{
|
|
// Move the line offset back to the beginning of the token
|
|
// so it can be processed by the next SQL statement in the
|
|
// stream.
|
|
|
|
m_uiCurrLineOffset = uiTokenLineOffset;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_pDb)
|
|
{
|
|
m_pDb->Release();
|
|
m_pDb = NULL;
|
|
}
|
|
|
|
// Open the database
|
|
|
|
if (RC_BAD( rc = dbSystem.openDatabase( szDatabaseName, szDataDirName,
|
|
szRflDirName, szPassword, uiFlags, &m_pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|