///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoDatabaseManager.cc
// ----------------------
// Cego Database Manager implementation
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoDatabaseManager
// 
// Description: General database manager
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// LFC INCLUDES
#include <lfcbase/ThreadLock.h>
#include <lfcbase/File.h>
#include <lfcbase/Datetime.h>
#include <lfcbase/Sleeper.h>
#include <lfcbase/Net.h>
#include <lfcbase/NetHandler.h>
#include <lfcxml/XMLSuite.h>
#include <lfcxml/Element.h>
#include <lfcxml/Document.h>

// CEGO INCLUDES
#include "CegoDatabaseManager.h"
#include "CegoTableManager.h"
#include "CegoTypeConverter.h"
#include "CegoDefs.h"
#include "CegoXMLdef.h"
#include "CegoXMLSpace.h"

// POSIX INCLUDES
#include <string.h>
#include <stdlib.h>

static ThreadLock dbmLock("DBM");
extern bool __lockStatOn;

//////////////////
// ObjectRecord //
//////////////////

CegoDatabaseManager::ObjectRecord::ObjectRecord()
{
}

CegoDatabaseManager::ObjectRecord::ObjectRecord(const unsigned tabSetId, const Chain& objName, CegoObject::ObjectType type)
{
    _objName = objName;
    _type = type;
    _tabSetId = tabSetId;
    _numUsed = 0;
    _mode = SHARED;
    _tid = 0;
}

CegoDatabaseManager::ObjectRecord::~ObjectRecord()
{
}

const unsigned CegoDatabaseManager::ObjectRecord::getTabSetId() const
{
    return _tabSetId;
}

void CegoDatabaseManager::ObjectRecord::incUsed()
{
    _numUsed++;
}

void CegoDatabaseManager::ObjectRecord::decUsed()
{
    if ( _numUsed > 0 )
	_numUsed--;
}

unsigned CegoDatabaseManager::ObjectRecord::numUsed() const
{
    return _numUsed;
}

void CegoDatabaseManager::ObjectRecord::setMode(ObjectUseMode mode)
{
    _mode = mode;
} 

CegoDatabaseManager::ObjectUseMode CegoDatabaseManager::ObjectRecord::getMode() const
{
    return _mode;
}

void CegoDatabaseManager::ObjectRecord::setTid(unsigned long long tid)
{
    _tid = tid;
} 

unsigned long long CegoDatabaseManager::ObjectRecord::getTid() const
{
    return _tid;
}

const Chain& CegoDatabaseManager::ObjectRecord::getName() const
{
    return _objName;
}

const CegoObject::ObjectType CegoDatabaseManager::ObjectRecord::getType() const
{
    return _type;
}

CegoDatabaseManager::ObjectRecord& CegoDatabaseManager::ObjectRecord::operator = ( const CegoDatabaseManager::ObjectRecord& t)
{
    _tabSetId = t._tabSetId;
    _objName = t._objName;
    _type = t._type;
    _numUsed = t._numUsed;
    _mode = t._mode;
    _tid = t._tid;
    return (*this);
}

bool CegoDatabaseManager::ObjectRecord::operator == ( const CegoDatabaseManager::ObjectRecord& r)
{
    bool typeMatch=false;
    if ( ( _type == CegoObject::PAVLTREE || _type == CegoObject::UAVLTREE || _type == CegoObject::AVLTREE )
	 && ( r._type == CegoObject::PAVLTREE || r._type == CegoObject::UAVLTREE || r._type == CegoObject::AVLTREE ) )
    {
	typeMatch=true;
    }
    else if ( ( _type == CegoObject::PBTREE || _type == CegoObject::UBTREE || _type == CegoObject::BTREE ) 
	&& ( r._type == CegoObject::PBTREE || r._type == CegoObject::UBTREE || r._type == CegoObject::BTREE ) )
    {
	typeMatch=true;
    }
    else 
    {
	typeMatch = _type == r._type;
    }

    if ( _tabSetId == r._tabSetId && _objName == r._objName && typeMatch )
	return true;
    return false;
}


/////////////////////////
// CegoDatabaseManager //
/////////////////////////

CegoDatabaseManager::CegoDatabaseManager(const Chain& xmlDef, const Chain& lckFileName, const Chain& lockExpire, const Chain& logFile, const Chain& progName, CegoDbHandler::ProtocolType protType) :  CegoBufferPool(xmlDef, logFile, progName)
{
    _protType = protType;
    _lckFileName = lckFileName;
    
    File lckFile(_lckFileName);
    if ( lckFile.exists() )
    {
	bool keepLock = true;

	long long expire = lockExpire.asLongLong();
	
	if ( expire > 0 )
	{	    
	    if ( expire < LCKMNG_BEATDELAY )
	    {
		Chain msg = Chain("Lock expire too small, must be at least ") + Chain(LCKMNG_BEATDELAY) + Chain(" seconds");
		throw Exception(EXLOC, msg);		
	    }
	    lckFile.open(File::READ);
	    Chain beatString;
	    if ( lckFile.readLine(beatString) )
	    {
		Datetime checkTime(beatString, DEFAULTDATETIMEFORMAT1);
		Datetime now;
		if ( checkTime.asLong() < now.asLong() - expire  )
		{
		    keepLock=false;
		}		
	    }
	    lckFile.close();
	    
	}
	if ( keepLock )
	{
	    Chain msg = Chain("Lock file ") + lckFileName + Chain(" not expired");
	    throw Exception(EXLOC, msg);
	}
	else
	{
	    lckFile.remove();
	}
    }
    else
    {	
	beat();
    }

    dbmLock.init(LCKMNG_LOCKWAITDELAY, __lockStatOn);  
    for ( unsigned i=0; i<TABMNG_MAXTABSET; i++)
    {
	_recoveryMode[i] = OFF;
	_pQueryCache[i] = 0;
	_pTableCache[i]= 0;
    }

    _logConfigured=false;

    _modId = getModId("CegoDatabaseManager");
}

CegoDatabaseManager::~CegoDatabaseManager()
{
    File lckFile(_lckFileName);
    lckFile.remove();

    for ( unsigned i=0; i<TABMNG_MAXTABSET; i++)
    {
	if ( _pQueryCache[i] )
	    delete _pQueryCache[i];
	if ( _pTableCache[i] )
	    delete _pTableCache[i];	
    }
}

void CegoDatabaseManager::allocateQueryCache(const Chain& tableSet)
{
    if ( getQueryCacheMode(tableSet) )
    {	
	unsigned maxEntry = getMaxQueryCacheEntry(tableSet);
	unsigned maxSize = getMaxQueryCacheSize(tableSet);
	unsigned threshold = getQueryCacheThreshold(tableSet);
	unsigned hashRange = getQueryCacheHashRange(tableSet);

	if ( maxEntry > 0 && maxSize > 0 && hashRange > 0)
	{
	    unsigned tabSetId = getTabSetId(tableSet);
	    
	    if ( _pQueryCache[tabSetId] )
		delete _pQueryCache[tabSetId];
	    
	    _pQueryCache[tabSetId] = new CegoQueryCache(tabSetId, maxEntry, maxSize, threshold, hashRange);
	}
	else
	{
	    log(_modId, Logger::NOTICE, Chain("Query Cache Size/Entry not appropriate, skipping cache allocation"));
	}
    }
}

void CegoDatabaseManager::allocateTableCache(const Chain& tableSet)
{
    if ( getTableCacheMode(tableSet) )
    {
	Chain cacheFilter = getTableCacheFilter(tableSet);
	unsigned maxSize = getTableCacheMaxSize(tableSet);
	unsigned maxEntry = getTableCacheMaxEntry(tableSet);
	unsigned hashRange = getTableCacheHashRange(tableSet);
	
	if (  maxSize > 0 && maxEntry > 0 && hashRange > 0 )
	{
	    unsigned tabSetId = getTabSetId(tableSet);
	    
	    if ( _pTableCache[tabSetId] )
		delete _pTableCache[tabSetId];
	    _pTableCache[tabSetId] = new CegoTableCache(tabSetId, cacheFilter, maxSize, maxEntry, hashRange, this);
	}
	else
	{
	    log(_modId, Logger::NOTICE, Chain("Table Cache Size/Entry not appropriate, skipping cache allocation"));
	}
    }
}

void CegoDatabaseManager::releaseQueryCache(const Chain& tableSet)
{
    unsigned tabSetId = getTabSetId(tableSet);
    
    if ( _pQueryCache[tabSetId] )
    {
	delete _pQueryCache[tabSetId];
	_pQueryCache[tabSetId]=0;
    }
}

void CegoDatabaseManager::releaseTableCache(const Chain& tableSet)
{
    unsigned tabSetId = getTabSetId(tableSet);
    
    if ( _pTableCache[tabSetId] )
    {
	delete _pTableCache[tabSetId];
	_pTableCache[tabSetId]=0;
    }
}

void CegoDatabaseManager::cleanCache(unsigned tabSetId, CegoObject::ObjectType objType, const Chain& objName)
{
    if ( _pQueryCache[tabSetId] )
    {
	_pQueryCache[tabSetId]->invalidate(CegoObject(objType, objName, tabSetId));
    }
    
    if ( _pTableCache[tabSetId] && objType == CegoObject::TABLE )
    {
	_pTableCache[tabSetId]->invalidate(objName);
    }
}

CegoQueryCache* CegoDatabaseManager::getQueryCache(const Chain& tableSet) const
{
    unsigned tabSetId = getTabSetId(tableSet);
    return _pQueryCache[tabSetId];
}

CegoQueryCache* CegoDatabaseManager::getQueryCache(unsigned tabSetId) const
{
    return _pQueryCache[tabSetId];
}

CegoTableCache* CegoDatabaseManager::getTableCache(const Chain& tableSet) const
{
    unsigned tabSetId = getTabSetId(tableSet);
    return _pTableCache[tabSetId];
}

CegoTableCache* CegoDatabaseManager::getTableCache(unsigned tabSetId) const
{
    return _pTableCache[tabSetId];
}

void CegoDatabaseManager::beat()
{
    File lckFile(_lckFileName);
    lckFile.open(File::WRITE);
    Datetime dt;
    lckFile.writeChain(dt.asChain(DEFAULTDATETIMEFORMAT1) + Chain("\n"));
    lckFile.close();    
}

void CegoDatabaseManager::checkTableSetRunState(unsigned tabSetId)
{
    Chain runState = getTableSetRunState(tabSetId);

    if ( runState != Chain(XML_ONLINE_VALUE)
	 && runState != Chain(XML_BACKUP_VALUE)
	 && runState != Chain(XML_RECOVERY_VALUE)
	 && runState != Chain(XML_CHECKPOINT_VALUE) )
    {
	Chain msg = Chain("Tableset ") + getTabSetName(tabSetId) + Chain(" not online ( run state is ") + runState + Chain(" )");

	throw Exception(EXLOC, msg);
    }
}


void CegoDatabaseManager::PR()
{
    dbmLock.readLock(DBM_LOCKTIMEOUT);
}

void CegoDatabaseManager::PW()
{
    dbmLock.writeLock(DBM_LOCKTIMEOUT);
}

void CegoDatabaseManager::V()
{
    dbmLock.unlock();
}

bool CegoDatabaseManager::isLoggerConfigured()
{
    return _logConfigured;
}

CegoDatabaseManager::RecoveryMode CegoDatabaseManager::getRecoveryMode(unsigned tabSetId)
{
    return _recoveryMode[tabSetId];
}

void CegoDatabaseManager::setRecoveryMode(unsigned tabSetId, CegoDatabaseManager::RecoveryMode m)
{
    _recoveryMode[tabSetId] = m;
}

void CegoDatabaseManager::setAllRecoveryOff()
{    
    for ( unsigned i=0; i<TABMNG_MAXTABSET; i++)
    {
	_recoveryMode[i] = OFF;
    }
}

void CegoDatabaseManager::startRecovery(const Chain& tableSet)
{
    PW();
    _recoveryList.Insert(tableSet);
    V();
}

bool CegoDatabaseManager::nextRecovery(Chain& tableSet)
{
    PW();
    Chain *pS = _recoveryList.First();
    if ( pS )
    {
	tableSet = *pS;
	_recoveryList.Remove(tableSet);
	V();
	return true;
    }
    V();
    return false;
}


void CegoDatabaseManager::useObject(unsigned tabSetId, const Chain& objName, CegoObject::ObjectType type, ObjectUseMode mode, CegoTableManager *pTabMng)
{
    // In SHARED mode, an object can be use in parallel by several threads. Normally, this is done, if the object is accessed read only.

    unsigned long long tid = pTabMng->getThreadId();    
	
    // we fist check, if corresponding tableset is really not offline 
    checkTableSetRunState(tabSetId);
    
    if ( mode == SHARED )
    {
        PW();
        ObjectRecord *pOR = _objList.Find(ObjectRecord(tabSetId, objName, type));
        if ( pOR == 0 )
        {
            V();
	    Chain msg = Chain("Cannot access object <") + objName + Chain(">");
            throw Exception(EXLOC, msg); 
        }
	
	// cout << "Using obj " << objName << " numused = " << pOR->numUsed() << endl;
	if ( pOR->numUsed() > MAX_OBJECT_USAGE )
	{
            V();
	    Chain msg = Chain("Usage exceeded for <") + objName + Chain(">");
            throw Exception(EXLOC, msg); 	    
	}
	
	if ( pOR->getTid() != 0 && pOR->getTid() == tid ) 
	{
	    pOR->incUsed();
	    V();
	}
	else
	{
	    unsigned numTries=0;

	    while ( pOR && pOR->getMode() == EXCLUSIVE_WRITE && numTries < DBM_MAXLOCKTRIES )
	    {
		V();

		if ( numTries > 0 )
		{
		    if ( numTries > 1 )
		    {
			log(_modId, Logger::NOTICE, Chain("Repeated shared lock delay on ") + objName + Chain(" ( ") + Chain(numTries) + Chain(" tries )"));
		    }

		    checkTableSetRunState(tabSetId);
		    		    
		    Sleeper ns;
		    unsigned sec = DBM_LOCKDELAY / 1000;
		    if ( sec > 0 )
			ns.secSleep(sec);
		    unsigned msec = DBM_LOCKDELAY % 1000;
		    if ( msec > 0 )
			ns.milliSleep(msec);
		}
		
		PW();

		pOR = _objList.Find(ObjectRecord(tabSetId, objName, type));
		
		if ( pOR == 0 )
		{
		    V();
		    Chain msg = Chain("Cannot access object ") + objName;
		    throw Exception(EXLOC, msg);
		}
		numTries++;
	    }

	    if ( numTries == DBM_MAXLOCKTRIES )
	    {
		V();
		Chain msg = Chain("Access timeout on object ") + objName;
		throw Exception(EXLOC, msg); 
	    }

	    pOR->incUsed();
	    V();
	}
    }
    
    // In EXCLUSIVE_WRITE mode, an object is accessed exclusively by one thread only
    
    else if ( mode == EXCLUSIVE_WRITE )
    {
        PW();
        ObjectRecord *pOR = _objList.Find(ObjectRecord(tabSetId, objName, type));
        if ( pOR == 0 )
        {
            V();
            Chain msg = Chain("Cannot access object ") + objName;
            throw Exception(EXLOC, msg);
        }
	
	if ( pOR->getTid() != 0 && pOR->getTid() == tid ) 
	{	    
	    pOR->incUsed();
	    V();
	}
	else
	{
	    if ( pOR->getMode() == SHARED && pOR->numUsed() == 0 )
	    {
		pOR->setMode(mode);
		pOR->setTid(tid);
	    }
	    else
	    {	    
		unsigned numTries=0;
		while ( pOR && pOR->numUsed() != 0 && numTries < DBM_MAXLOCKTRIES )
		{
		    V();
		    
		    if ( numTries > 0 )
		    {
			if ( numTries > 1 )
			{
			    log(_modId, Logger::NOTICE, Chain("Repeated exclusive write lock delay on ") + objName + Chain(" ( ") + Chain(numTries) + Chain(" tries )"));
			}

			checkTableSetRunState(tabSetId);
			
			Sleeper ns;			
			unsigned sec = DBM_LOCKDELAY / 1000;
			if ( sec > 0 )
			    ns.secSleep(sec);
			unsigned msec = DBM_LOCKDELAY % 1000;
			if ( msec > 0 )
			    ns.milliSleep(msec);
		    }
		    
		    PW();

		    pOR = _objList.Find(ObjectRecord(tabSetId, objName, type));
		    if ( pOR == 0 )
		    {
			V();
			Chain msg = Chain("Cannot access object ") + objName;
			throw Exception(EXLOC, msg);
		    }
		    
		    numTries++;		   
		}

		if ( numTries == DBM_MAXLOCKTRIES )
		{
		    V();
		    Chain msg = Chain("Access timeout on object ") + objName;
		    throw Exception(EXLOC, msg); 
		}
		pOR->setMode(mode);
		pOR->setTid(tid);		
	    }
	    
	    pOR->incUsed();
	    V();
	}
    }
    return;
}

void CegoDatabaseManager::unuseObject(unsigned tabSetId, const Chain& objName, CegoObject::ObjectType type, unsigned long long tid )
{        
    // we fist check, if corresponding tableset is really not offline 
    checkTableSetRunState(tabSetId);

    PW();
    ObjectRecord *pOR = _objList.Find(ObjectRecord(tabSetId, objName, type));

    if ( pOR == 0 )
    {
        V();
	Chain msg = Chain("Cannot access object ") + objName;
	throw Exception(EXLOC, msg);
	return;
    }

    // we just unuse object, if tid not specified or lockmode of object is SHARED or if transaction owns object lockmode of obejct id EXCLUSIVE_WRITE 
    if ( ( tid == pOR->getTid() && pOR->getMode() == EXCLUSIVE_WRITE ) || tid == 0 || pOR->getMode() == SHARED )
    {
	pOR->decUsed();
	
	if ( pOR->numUsed() == 0 )
	{
	    pOR->setMode(SHARED);
	    pOR->setTid(0);
	}
    }
    else
    {
        V();
	Chain msg = Chain("Table ") + objName + Chain(" not owned by thread");
	throw Exception(EXLOC, msg);
	return;       
    }    
    V();    
}

void CegoDatabaseManager::addObject(unsigned tabSetId, const Chain& objName, CegoObject::ObjectType type)
{
    PW();
    _objList.Insert(ObjectRecord(tabSetId, objName, type));
    V();
}

void CegoDatabaseManager::removeObject(unsigned tabSetId, const Chain& objName, CegoObject::ObjectType type)
{
    PW();
    _objList.Remove(ObjectRecord(tabSetId, objName, type));
    V();
}

void CegoDatabaseManager::removeAllObjects(unsigned tabSetId) 
{ 
    PW(); 
    ObjectRecord *pOR = _objList.First(); 
    while ( pOR ) 
    { 
	if ( pOR->getTabSetId() == tabSetId ) 
	{ 
	    _objList.Remove(*pOR); 
	    pOR = _objList.First(); 
	} 
	else 
	{ 
	    pOR = _objList.Next(); 
	} 
    } 
    V(); 
} 

bool CegoDatabaseManager::objectExists(unsigned tabSetId, const Chain& objName, CegoObject::ObjectType type)
{
    // we fist check, if corresponding tableset is really not offline
    checkTableSetRunState(tabSetId);

    PR();
    ObjectRecord *pTR = _objList.Find(ObjectRecord(tabSetId, objName, type));
    V();    
    if ( pTR ) 
    {	
	return true;
    }
    else
    {
	return false;
    }
}

void CegoDatabaseManager::printObjectList()
{
    PW();
    ObjectRecord *pOR = _objList.First();
    while ( pOR )
    {
	cout << "ObjListEntry : " << pOR->getName() << " Type = " << pOR->getType() << endl;
	pOR = _objList.Next();
    }
    V();
}

void CegoDatabaseManager::setThreadInfo(unsigned numDbThread, unsigned numAdmThread)
{
    _numDbThread = numDbThread;
    _numAdmThread = numAdmThread;
    _activeDbThread = 0;
    _activeAdmThread = 0;
}
    
void CegoDatabaseManager::getThreadInfo(unsigned& numDbThread, unsigned& numAdmThread,
		   unsigned& activeDbThread, unsigned& activeAdmThread, unsigned& activeLogThread)
{
    numDbThread = _numDbThread;
    numAdmThread = _numAdmThread;
    activeDbThread = _activeDbThread;
    activeAdmThread = _activeAdmThread;
}

void CegoDatabaseManager::increaseActiveAdmThread()
{
    _activeAdmThread++;
}

void CegoDatabaseManager::decreaseActiveAdmThread()
{
    _activeAdmThread--;
}

void CegoDatabaseManager::increaseActiveDbThread()
{
    _activeDbThread++;
}

void CegoDatabaseManager::decreaseActiveDbThread()
{
    _activeDbThread--;
}

void CegoDatabaseManager::getDBMLockStat(Chain& lockName, unsigned& lockCount, unsigned long long &numRdLock, unsigned long long &numWrLock, unsigned long long &sumRdDelay, unsigned long long &sumWrDelay)
{
    lockName = dbmLock.getId();
    lockCount= dbmLock.numLockTry();
    numRdLock = dbmLock.numReadLock();
    numWrLock = dbmLock.numWriteLock();
    sumRdDelay = 0;
    sumWrDelay = 0;

    if ( dbmLock.numReadLock() > 0 )
	sumRdDelay = dbmLock.sumReadDelay() / LCKMNG_DELRES;
    if ( dbmLock.numWriteLock() > 0 )
	sumWrDelay = dbmLock.sumWriteDelay() / LCKMNG_DELRES;
}

void CegoDatabaseManager::configureLogger(Logger::LogLevel level)
{
    for ( unsigned i=1; i< getMapSize() ; i++)
    {
	logModule(i, getModName(i), level);
    }
    _logConfigured=true;
}
    
void CegoDatabaseManager::configureLogger()
{
    ListT<Chain> modList;
    _logConfigured =  getModuleList(modList);

    Chain *pMod = modList.First();
    while ( pMod )
    {
	if ( (Chain)pMod->toUpper() == Chain("ALL"))
	{
	    Logger::LogLevel level = getLogLevel(*pMod);
	    for ( unsigned i=1; i< getMapSize() ; i++)
	    {
		logModule(i, getModName(i), level);
	    }
	}
	else
	{
	    unsigned long modId = getModId(*pMod);
	    logModule(modId, *pMod, getLogLevel(*pMod));
	}
	pMod = modList.Next();
    }
}

bool CegoDatabaseManager::verifyJDBC(const Chain& user)
{
    SetT<Chain> roleSet;
    getRoleSet(user, roleSet);
    return roleSet.Find(Chain(ROLE_JDBC));
}

bool CegoDatabaseManager::verifyAccess(const unsigned tabSetId, const Chain& objName, CegoObject::ObjectType type, CegoXMLSpace::AccessMode mode, const Chain& user)
{
    SetT<Chain> roleSet;

    getRoleSet(user, roleSet);
    Chain tableSet = getTabSetName(tabSetId);

    Chain *pRole = roleSet.First();
    while ( pRole ) 
    {	
	Chain objPattern = objName;

	if ( matchRole( *pRole, tableSet, objPattern, mode ) )
	    return true;
	pRole = roleSet.Next();
    }
    return false;
}

void CegoDatabaseManager::initLogFiles(const Chain& tableSet, bool overwrite)
{
    ListT<Chain> lfList;
    ListT<unsigned> sizeList;
    ListT<Chain> statusList;
    
    unsigned tabSetId = getTabSetId(tableSet);
    getLogFileInfo(tableSet, lfList, sizeList, statusList);

    Chain *pLog = lfList.First();
    unsigned *pSize = sizeList.First();

    bool isFirst = true;
    while ( pLog ) 
    {	
	if ( isFirst )
	    setLogFileStatus(tableSet, *pLog, XML_ACTIVE_VALUE);		
	else
	    setLogFileStatus(tableSet, *pLog,  XML_FREE_VALUE);	
	
	isFirst=false;

	log(_modId, Logger::NOTICE, Chain("Initializing logfile ") + *pLog + Chain(" ..."));
	
	if ( overwrite == false )
	{
	    File checkLog(*pLog);
	    if ( checkLog.exists() ) 
	    {
		Chain msg = Chain("Cannot initialize logfile <") + *pLog + Chain(">, file already exists");
		throw Exception(EXLOC, msg);
	    }
	}

	setLogFile(tabSetId, *pLog, false);
	initLog(tabSetId, *pSize);
	
	pLog = lfList.Next();
	pSize = sizeList.Next();	
    }
}

void CegoDatabaseManager::releaseLogFiles(const Chain& tableSet, bool waitForArch)
{
    ListT<Chain> lfList;
    ListT<unsigned> sizeList;
    ListT<Chain> statusList;
    
    unsigned tabSetId = getTabSetId(tableSet);
    getLogFileInfo(tableSet, lfList, sizeList, statusList);

    Chain *pLog = lfList.First(); 
    Chain *pStatus = statusList.First(); 

    while ( pLog && pStatus ) 
    { 
        if ( *pStatus == Chain(XML_ACTIVE_VALUE) && File::exists(*pLog) ) 
        { 
            setLogFile(tabSetId, *pLog, true); 
            // unsigned long long minlsn = getMinLSN(tabSetId); 
            //if ( minlsn > 0 ) 
            // { 
	    log(_modId, Logger::NOTICE, Chain("Releasing logfile ") + *pLog); 
	    setLogFileStatus(tableSet, *pLog, XML_OCCUPIED_VALUE); 
            // } 
        } 
        pStatus = statusList.Next(); 
        pLog = lfList.Next(); 
    }
 
    if ( waitForArch )
    {
	bool notArchived = true;

	while ( notArchived )
	{
	    log(_modId, Logger::NOTICE, Chain("Waiting for archive ... "));	    

	    ListT<Chain> lfList;
	    ListT<unsigned> sizeList;
	    ListT<Chain> statusList;
	
	    getLogFileInfo(tableSet, lfList, sizeList, statusList);
	    
	    notArchived = false;

	    Chain *pStatus = statusList.First(); 
            Chain *pLog = lfList.First(); 
            while ( pLog && pStatus ) 
            { 
                if ( *pStatus != Chain(XML_FREE_VALUE) && File::exists(*pLog) ) 
                    notArchived = true; 
                pStatus = statusList.Next(); 
                pLog = lfList.Next(); 
            } 
	    
	    lfList.Empty();
	    sizeList.Empty();
	    statusList.Empty();

	    Sleeper s;
	    s.secSleep(LOGMNG_RECOVERY_DELAY);
	}
    }
}

CegoDbHandler::ProtocolType CegoDatabaseManager::getProtType() const
{
    return _protType;
}

bool CegoDatabaseManager::getQCLockStat(unsigned tabSetId, unsigned& lockCount, unsigned long long &numRdLock, unsigned long long &numWrLock, unsigned long long &sumRdDelay, unsigned long long &sumWrDelay)
{
    if ( _pQueryCache[tabSetId] )
    {
	_pQueryCache[tabSetId]->getQCLockStat(lockCount, numRdLock, numWrLock, sumRdDelay, sumWrDelay);
	return true;
    }
    return false;
}

void CegoDatabaseManager::getAggQCLockStat(Chain& lockGroup, unsigned& numLock, unsigned& lockCount, unsigned long long &numRdLock, unsigned long long &numWrLock, unsigned long long &sumRdDelay, unsigned long long &sumWrDelay)
{
    lockGroup = Chain("QCLCK");
    
    lockCount = 0;
    numRdLock = 0;
    numWrLock = 0;
    sumRdDelay = 0;
    sumWrDelay = 0;
    numLock = 0;
   
    for ( unsigned i=0; i<TABMNG_MAXTABSET; i++)
    {
	if ( _pQueryCache[i] )
	{
	    unsigned qcLockCount;
	    unsigned long long qcNumRdLock;
	    unsigned long long qcNumWrLock;
	    unsigned long long qcSumRdDelay;
	    unsigned long long qcSumWrDelay;
	    
	    _pQueryCache[i]->getQCLockStat(qcLockCount, qcNumRdLock, qcNumWrLock, qcSumRdDelay, qcSumWrDelay);
	    
	    numLock++;
	    
	    lockCount += qcLockCount;
	    numRdLock += qcNumRdLock;
	    numWrLock += qcNumWrLock;
	    
	    sumRdDelay += qcSumRdDelay;
	    sumWrDelay += qcSumWrDelay;
	}
    }
}

bool CegoDatabaseManager::getTCLockStat(unsigned tabSetId, unsigned& lockCount, unsigned long long &numRdLock, unsigned long long &numWrLock, unsigned long long &sumRdDelay, unsigned long long &sumWrDelay)
{
    if ( _pTableCache[tabSetId] )
    {
	_pTableCache[tabSetId]->getTCLockStat(lockCount, numRdLock, numWrLock, sumRdDelay, sumWrDelay);
	return true;
    }
    return false;
}

void CegoDatabaseManager::getAggTCLockStat(Chain& lockGroup, unsigned& numLock, unsigned& lockCount, unsigned long long &numRdLock, unsigned long long &numWrLock, unsigned long long &sumRdDelay, unsigned long long &sumWrDelay)
{
    lockGroup = Chain("TCLCK");
    
    lockCount = 0;
    numRdLock = 0;
    numWrLock = 0;
    sumRdDelay = 0;
    sumWrDelay = 0;
    numLock = 0;

    for ( unsigned i=0; i<TABMNG_MAXTABSET; i++)
    {
	if ( _pTableCache[i] )
	{
	    unsigned qcLockCount;
	    unsigned long long qcNumRdLock;
	    unsigned long long qcNumWrLock;
	    unsigned long long qcSumRdDelay;
	    unsigned long long qcSumWrDelay;
	    
	    _pTableCache[i]->getTCLockStat(qcLockCount, qcNumRdLock, qcNumWrLock, qcSumRdDelay, qcSumWrDelay);
	    
	    numLock++;
	    
	    lockCount += qcLockCount;
	    numRdLock += qcNumRdLock;
	    numWrLock += qcNumWrLock;
	    
	    sumRdDelay += qcSumRdDelay;
	    sumWrDelay += qcSumWrDelay;
	}
    }
}

Chain CegoDatabaseManager::getTSTicket(const Chain& tableSet) const
{
    Chain tsTicket = getTSRoot(tableSet) + Chain("/") + tableSet + Chain("_ticket.xml"); 
    return tsTicket;
}

void CegoDatabaseManager::verifyLocks(CegoAdminHandler*pAH)
{

    unsigned timeout = 3000;
    
    Chain info;

    info = Chain("DBM Lock - Read ...");
    if ( pAH )
	pAH->sendInfo(info);
	
    dbmLock.readLock(timeout);
    dbmLock.unlock();

    if ( pAH )
	pAH->sendInfo(Chain("ok\n"));

    info = Chain("DBM Lock - Write ...");
    if ( pAH )
	pAH->sendInfo(info);
    
    dbmLock.writeLock(timeout);
    dbmLock.unlock();
    
    if ( pAH )
	pAH->sendInfo(Chain("ok\n"));

    verifyXMLLock(pAH);
            
    ListT<Chain> tsList;
    getActiveTableSet( tsList, true, false);
	    
    Chain *pTabSet = tsList.First();
    while ( pTabSet )
    {
	unsigned tabSetId = getTabSetId(*pTabSet);

	verifyLogLocks(pAH);
	
	if ( _pQueryCache[tabSetId] )
	{
	    _pQueryCache[tabSetId]->verifyLock(pAH);
	}

	if ( _pTableCache[tabSetId] )
	{
	    _pTableCache[tabSetId]->verifyLock(pAH);
	}
	
	pTabSet = tsList.Next();
    }
}
