///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoTableManager.cc
// -------------------
// Cego table manager class implementation
//                                                         
// Design and Implementation by Bjoern Lemke               
//
// (C)opyright 2000-2010 Bjoern Lemke
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
// 
// 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; see the file COPYING.  If not, write to
// the Free Software Foundation, 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// IMPLEMENTATION MODULE
//
// Class: CegoTableManager
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// base includes
#include <lfc/Sleeper.h>
#include <lfc/Exception.h>
#include <lfc/Timer.h>
#include <lfc/SetT.h>
#include <lfc/Host.h>
#include <lfc/Datetime.h>
#include <lfc/ThreadLock.h>

// cego includes
#include "CegoTableManager.h"
#include "CegoTransactionManager.h"
#include "CegoBufferPage.h"
#include "CegoObject.h"
#include "CegoAVLIndexManager.h"
#include "CegoAVLIndexEntry.h"
#include "CegoAVLIndexCursor.h"
#include "CegoBTreeCursor.h"
#include "CegoBTreeManager.h"
#include "CegoTableCursor.h"
#include "CegoAttrCond.h"
#include "CegoXMLdef.h"
#include "CegoTypeConverter.h"
#include "CegoCheckObject.h"
#include "CegoSelect.h"
#include "CegoDbThreadPool.h"

#include <string.h>
#include <stdlib.h>

static ThreadLock tsLock[TABMNG_MAXTABSET];
extern bool __lockStatOn;

CegoTableManager::CegoTableManager(CegoDatabaseManager *pDBMng) : CegoSystemObject(pDBMng)
{
    _pTM = new CegoTransactionManager(this);
    for ( int i=0;i<TABMNG_MAXTABSET; i++)
    {
	_tid[i]=0;
	_tastep[i]=0;
    }

    _isAborted = false;
    _autoCommit = true;
    _doAppend = true;
    _isolationLevel = CegoTableManager::READ_COMMITTED;
    _modId = pDBMng->getModId("CegoTableManager");
    _pPool = 0;
}

CegoTableManager::~CegoTableManager()
{
    delete _pTM;
}

void CegoTableManager::setPoolSyncInfo(CegoDbThreadPool *pPool, int thrIdx)
{
    _pPool = pPool;
    _thrIdx = thrIdx;
}

void CegoTableManager::setAppend(bool doAppend)
{
    _doAppend = doAppend;
}

void CegoTableManager::createTableSet(const Chain& tableSet)
{
    int tabSetId =_pDBMng->getTabSetId(tableSet);
    int sysSize = _pDBMng->getSysSize(tableSet);
    int tmpFileId = _pDBMng->getTmpFid(tableSet);
    int tmpSize = _pDBMng->getTmpSize(tableSet);
    Chain dbSysFileName = _pDBMng->getSysFileName(tableSet);
		
    if ( sysSize < TABMNG_HASHSIZE ) 
    {
	Chain msg = Chain("System space must be at least ") + Chain(TABMNG_HASHSIZE) + Chain(" pages ");
	throw Exception(EXLOC, msg);
    }

    if ( tmpSize < TABMNG_HASHSIZE ) 
    {
	Chain msg = Chain("Tmp space must be at least ") + Chain(TABMNG_HASHSIZE) + Chain(" pages ");
	throw Exception(EXLOC, msg);
    }  
	
    _pDBMng->log(_modId, Logger::NOTICE, Chain("Initializing sysfile ") + dbSysFileName + Chain(" ..."));
    _pDBMng->initDataFile(tabSetId, dbSysFileName, tabSetId,  sysSize, CegoFileHandler::SYSTEMFILE);

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Registrating sysfile ") + dbSysFileName + Chain(" ..."));
    _pDBMng->regDataFile(tabSetId, dbSysFileName, tabSetId, _pLockHandle);
    
    for ( int i = 0 ; i < TABMNG_HASHSIZE ; i++ )
    {
	_pDBMng->claimPage(tabSetId, i, _pLockHandle);
	
	CegoBufferPage bp;
	_pDBMng->bufferFix(bp, tabSetId, tabSetId, i, CegoBufferPool::PERSISTENT, _pLockHandle);
	
	bp.initPage(CegoBufferPage::TABLE);
	_pDBMng->bufferUnfix(bp, true, _pLockHandle);
    }
    
    Chain dbTempFileName = _pDBMng->getTmpFileName(tableSet);
    
    _pDBMng->log(_modId, Logger::NOTICE, Chain("Initializing tempfile ") + dbTempFileName + Chain(" ..."));
    _pDBMng->initDataFile(tabSetId, dbTempFileName, tmpFileId, tmpSize, CegoFileHandler::TEMP);
    _pDBMng->log(_modId, Logger::NOTICE, Chain("Registrating tempfile ") + dbTempFileName + Chain(" ..."));
    _pDBMng->regDataFile(tabSetId, dbTempFileName, tmpFileId, _pLockHandle);
    
    for ( int i = 0 ; i < TABMNG_HASHSIZE ; i++ )
    {
	_pDBMng->claimPage(tmpFileId, i, _pLockHandle);	
	
	CegoBufferPage bp;
	_pDBMng->bufferFix(bp, tabSetId, tmpFileId, i, CegoBufferPool::PERSISTENT, _pLockHandle);
	bp.initPage(CegoBufferPage::TABLE);
	_pDBMng->bufferUnfix(bp, true, _pLockHandle);
    }
    

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Creating system space ..."));
    
    initLock(tabSetId);
    
    createSystemSpace(tabSetId);
    
    _pDBMng->initLogFiles(tableSet, false);
    
    ListT<Chain> dfList;
    ListT<int> fidList;
    ListT<int> sizeList;
    
    _pDBMng->getDataFileInfo(tableSet, Chain(XML_APPFILE_VALUE), dfList, fidList, sizeList);
    
    Chain *pFileName = dfList.First();
    int *pFid = fidList.First();
    int *pSize = sizeList.First();
    
    while ( pFileName && pFid && pSize ) 
    {

	_pDBMng->log(_modId, Logger::NOTICE, Chain("Init datafile ") + *pFileName + Chain(" ..."));	
	
	_pDBMng->initDataFile(tabSetId, *pFileName, *pFid, *pSize, CegoFileHandler::DATAFILE);
	_pDBMng->regDataFile(tabSetId, *pFileName, *pFid, _pLockHandle);
	
	pFileName = dfList.Next();
	pFid = fidList.Next();
	pSize = sizeList.Next();
	
    }

    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();

    _pDBMng->getDataFileInfo(tableSet, Chain(XML_SYSFILE_VALUE), dfList, fidList, sizeList);
    
    pFileName = dfList.First();
    pFid = fidList.First();
    pSize = sizeList.First();
    
    while ( pFileName && pFid && pSize ) 
    {

	_pDBMng->log(_modId, Logger::NOTICE, Chain("Init sysfile ") + *pFileName + Chain(" ..."));	
		
	_pDBMng->initDataFile(tabSetId, *pFileName, *pFid, *pSize, CegoFileHandler::SYSTEMFILE);
	_pDBMng->regDataFile(tabSetId, *pFileName, *pFid, _pLockHandle);
	
	pFileName = dfList.Next();
	pFid = fidList.Next();
	pSize = sizeList.Next();
	
    }

    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();

    _pDBMng->getDataFileInfo(tableSet, Chain(XML_TEMPFILE_VALUE), dfList, fidList, sizeList);
    
    pFileName = dfList.First();
    pFid = fidList.First();
    pSize = sizeList.First();
    
    while ( pFileName && pFid && pSize ) 
    {
	
	_pDBMng->log(_modId, Logger::NOTICE, Chain("Init tempfile ") + *pFileName + Chain(" ..."));	
	
	_pDBMng->initDataFile(tabSetId, *pFileName, *pFid, *pSize, CegoFileHandler::TEMP);
	_pDBMng->regDataFile(tabSetId, *pFileName, *pFid, _pLockHandle);
	
	pFileName = dfList.Next();
	pFid = fidList.Next();
	pSize = sizeList.Next();
	
    }
    
    _pDBMng->setTableSetRunState(tableSet, XML_OFFLINE_VALUE);
    
    _pDBMng->log(_modId, Logger::NOTICE, Chain("Tableset ") + tableSet + Chain(" allocated. Syncing..."));	
    
    _pDBMng->writeCheckPoint(tabSetId, false, Chain(""), 0,  _pLockHandle);
    
    _pDBMng->doc2Xml();
     
    _pDBMng->log(_modId, Logger::NOTICE, Chain("Tableset ") + tableSet + Chain(" created succesful"));	
    
}

void CegoTableManager::dropTableSet(const Chain& tableSet)
{

    Chain status = _pDBMng->getTableSetRunState(tableSet);
    
    if ( status == Chain(XML_OFFLINE_VALUE) )
    {
	
	Chain dbSysFileName = _pDBMng->getSysFileName(tableSet); 		
	Chain dbTempFileName = _pDBMng->getTmpFileName(tableSet);

	int tabSetId = _pDBMng->getTabSetId(tableSet);
	_pDBMng->writeAndRemoveTabSet(tabSetId, _pLockHandle);

	File dbSysFile(dbSysFileName);
	
	try {
	    
	    dbSysFile.remove();
	}
	catch ( Exception e )
	{
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("System file ") + dbSysFileName + Chain(" does not exist ( Ignored )"));	
	}
	
	File dbTempFile(dbTempFileName);

	try {
	    
	    dbTempFile.remove();
	}
	catch ( Exception e )
	{
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Temp file ") + dbTempFileName + Chain(" does not exist ( Ignored )"));
	}
	
	ListT<Chain> lfList;
	ListT<int> lfsizeList;
	ListT<Chain> statusList;

	_pDBMng->getLogFileInfo(tableSet, lfList, lfsizeList, statusList);
	
	Chain *pLog = lfList.First();

	while ( pLog ) 
	{
	    
	    File logFile(*pLog);
	    try {

		logFile.remove();
	    }
	    catch ( Exception e )
	    {
		_pDBMng->log(_modId, Logger::NOTICE, Chain("Log file ") + *pLog + Chain(" does not exist ( Ignored )"));
	    }
	

	    pLog = lfList.Next();
	    
	}
	
	ListT<Chain> dfList;
	ListT<int> fidList;
	ListT<int> dfsizeList;
	
	_pDBMng->getDataFileInfo(tableSet, Chain(XML_APPFILE_VALUE), dfList, fidList, dfsizeList);
	
	Chain *pFileName = dfList.First();
	
	while ( pFileName ) 
	{
	    
	    File dataFile(*pFileName);

	    try {

		dataFile.remove();
	    }
	    catch ( Exception e )
	    {
		_pDBMng->log(_modId, Logger::NOTICE, Chain("Data file ") + *pFileName + Chain(" does not exist ( Ignored )"));
	    }
	    
	    pFileName = dfList.Next();
	    
	}

	dfList.Empty();
	fidList.Empty();
	dfsizeList.Empty();

	_pDBMng->getDataFileInfo(tableSet, Chain(XML_TEMPFILE_VALUE), dfList, fidList, dfsizeList);
	
	pFileName = dfList.First();
	
	while ( pFileName ) 
	{
	    
	    File dataFile(*pFileName);

	    try {

		dataFile.remove();
	    }
	    catch ( Exception e )
	    {
		_pDBMng->log(_modId, Logger::NOTICE, Chain("Temp file ") + *pFileName + Chain(" does not exist ( Ignored )"));
	    }
	    
	    pFileName = dfList.Next();
	    
	}

	dfList.Empty();
	fidList.Empty();
	dfsizeList.Empty();

	_pDBMng->getDataFileInfo(tableSet, Chain(XML_SYSFILE_VALUE), dfList, fidList, dfsizeList);
	
	pFileName = dfList.First();
	
	while ( pFileName ) 
	{
	    
	    File dataFile(*pFileName);

	    try {

		dataFile.remove();
	    }
	    catch ( Exception e )
	    {
		_pDBMng->log(_modId, Logger::NOTICE, Chain("Sys file ") + *pFileName + Chain(" does not exist ( Ignored )"));
	    }
	    
	    pFileName = dfList.Next();	    
	}

	_pDBMng->setLSN(tabSetId, 0);
	_pDBMng->setTableSetRunState(tableSet,XML_DEFINED_VALUE);
	_pDBMng->doc2Xml();

	_pDBMng->log(_modId, Logger::NOTICE, Chain("Tableset ") + tableSet + Chain(" dropped"));
	
    }
    else
    {
	throw Exception(EXLOC, Chain("Tableset must be in status offline to drop"));
    }
}

void CegoTableManager::cleanTableSet(const Chain& tableSet)
{
    int tabSetId =_pDBMng->getTabSetId(tableSet);
    _pTM->reorgSystemSpace(tabSetId);
}

void CegoTableManager::stopTableSet(const Chain& tableSet, bool archComplete)
{

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Stopping tableset ") + tableSet + Chain(" ..."));

    int tabSetId = _pDBMng->getTabSetId(tableSet);
    
    if ( _pDBMng->hasLogConnection(tabSetId) )
    {
	_pDBMng->releaseLogConnection(tabSetId);
    }
    else
    {
	long lsn = _pDBMng->getLSN(tabSetId);
	_pDBMng->setCommittedLSN(tabSetId, lsn - 1);
	writeCheckPoint(tableSet, true, archComplete);
    }
    
    _pTM->release(tabSetId);
    
    _pDBMng->writeAndRemoveTabSet(tabSetId, _pLockHandle);
    
    _pDBMng->setTableSetRunState(tableSet, XML_OFFLINE_VALUE);

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Tableset ") + tableSet + Chain(" stopped"));
}

void CegoTableManager::resetTableSet(const Chain& tableSet)
{

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Resetting tableset ") + tableSet + Chain(" ..."));

    int tabSetId = _pDBMng->getTabSetId(tableSet);
    
    if ( _pDBMng->hasLogConnection(tabSetId) )
    {
	_pDBMng->releaseLogConnection(tabSetId);
    }
    else
    {
	long lsn = _pDBMng->getLSN(tabSetId);
	_pDBMng->setCommittedLSN(tabSetId, lsn - 1);
	writeCheckPoint(tableSet, false, false);
    }
    
    _pTM->release(tabSetId);

    if ( _pDBMng->getTableSetRunState(tableSet) == Chain(XML_BACKUP_VALUE) )
    {
	endBackup(tableSet, Chain("Tableset reset"), false); 
    }

    if ( _pDBMng->getTableSetRunState(tableSet) == Chain(XML_ONLINE_VALUE) )
    {    
	_pDBMng->writeAndRemoveTabSet(tabSetId, _pLockHandle);
    }

    Chain primary = _pDBMng->getPrimary(tableSet);
    _pDBMng->setSecondary(tableSet, primary);

    _pDBMng->setTableSetRunState(tableSet, XML_OFFLINE_VALUE);
    _pDBMng->setTableSetSyncState(tableSet, XML_SYNCHED_VALUE);
    
    _pDBMng->log(_modId, Logger::NOTICE, Chain("Tableset ") + tableSet + Chain(" reset"));
}


void CegoTableManager::beginBackup(const Chain& tableSet, const Chain& msg)
{

    if ( _pDBMng->getTableSetRunState(tableSet) != Chain(XML_ONLINE_VALUE) )
    {
	Chain msg = Chain("Tableset ") + tableSet + Chain(" must be in runstate ONLINE to begin backup mode");
	throw Exception(EXLOC, msg);
    }

    if ( _pDBMng->isArchiveMode(tableSet) == false )
    {
	Chain msg = Chain("Archive not enabled for tableset ") + tableSet;
	throw Exception(EXLOC, msg);
    }

    
    long lsn = writeCheckPoint(tableSet, true, false);
    
    Chain tsTicketName = _pDBMng->getTSTicket(tableSet);
    
    File tsTicket(tsTicketName);
    if ( tsTicket.exists()) 
	throw Exception(EXLOC, Chain("Backup tableset ticket exists"));
    
    XMLSuite xml;
    Chain tsTicketData;
    
    Document *pDoc = new Document;	

    try
    {
	pDoc->setAttribute(XML_VERSION_ATTR, XML_VERSION_VALUE);
	pDoc->setDocType(XML_TSTICKET_DOC);
	
	xml.setDocument(pDoc);
	
	Element *pRoot = _pDBMng->getTableSetInfo(tableSet);
	pDoc->setRootElement(pRoot);
	

	xml.getXMLChain( tsTicketData );
	
	delete pDoc;
    }
    catch ( Exception e )
    {
	delete pDoc;
	Chain msg;
	e.pop(msg);
	throw Exception(EXLOC, Chain("Cannot write ticket data") + msg);
    }
    
    tsTicket.open(File::WRITE);
    tsTicket.writeChain(tsTicketData);
    tsTicket.close();
    
    _pDBMng->setTableSetRunState(tableSet, XML_BACKUP_VALUE);
    
    int tabSetId =_pDBMng->getTabSetId(tableSet);
    _pDBMng->setBackup(tabSetId, true);
    int tmpFileId = _pDBMng->getTmpFid(tableSet);	
    _pDBMng->setBackup(tmpFileId, true);
    
    ListT<Chain> dfList;
    ListT<int> fidList;
    ListT<int> sizeList;
    
    _pDBMng->getDataFileInfo(tableSet, Chain(XML_APPFILE_VALUE), dfList, fidList, sizeList);
    
    int *pFid = fidList.First();
    
    while ( pFid ) 
    {
	_pDBMng->setBackup(*pFid, true);
	pFid = fidList.Next();	    
    }
    
    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();
    
    _pDBMng->getDataFileInfo(tableSet, Chain(XML_TEMPFILE_VALUE), dfList, fidList, sizeList);
    
    pFid = fidList.First();
    
    while ( pFid ) 
    {
	_pDBMng->setBackup(*pFid, true);
	pFid = fidList.Next();	    
    }    

    addBUStat(tabSetId, Chain("BEGIN BACKUP"), msg);

}

void CegoTableManager::endBackup(const Chain& tableSet, const Chain& msg, bool keepTicket)
{

    if ( _pDBMng->getTableSetRunState(tableSet) != Chain(XML_BACKUP_VALUE) )
    {
	Chain msg = Chain("Tableset ") + tableSet + Chain(" must be in runstate BACKUP to end backup mode");
	throw Exception(EXLOC, msg);
    }

    if ( _pDBMng->isArchiveMode(tableSet) == false )
    {
	Chain msg = Chain("Archive not enabled for tableset ") + tableSet;
	throw Exception(EXLOC, msg);
    }

    int tabSetId =_pDBMng->getTabSetId(tableSet);
    _pDBMng->setBackup(tabSetId, false);
    int tmpFileId = _pDBMng->getTmpFid(tableSet);	
    _pDBMng->setBackup(tmpFileId, false);
    
    ListT<Chain> dfList;
    ListT<int> fidList;
    ListT<int> sizeList;
    
    _pDBMng->getDataFileInfo(tableSet, Chain(XML_APPFILE_VALUE), dfList, fidList, sizeList);
    
    int *pFid = fidList.First();
    
    while ( pFid ) 
    {
	_pDBMng->setBackup(*pFid, false);
	pFid = fidList.Next();	    
    }
        
    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();
    
    _pDBMng->getDataFileInfo(tableSet, Chain(XML_TEMPFILE_VALUE), dfList, fidList, sizeList);
    
    pFid = fidList.First();
    
    while ( pFid ) 
    {
	_pDBMng->setBackup(*pFid, false);
	pFid = fidList.Next();	    
    }
    
    CegoLogRecord logRec;    
    CegoLogRecord lr;
    lr.setAction(CegoLogRecord::LOGREC_BUFIN);    
    logIt(tabSetId, lr);
    
    writeCheckPoint(tableSet, true, false);
    
    if ( keepTicket == false)
    {
	Chain tsTicketName = _pDBMng->getTSTicket(tableSet);
	File tsTicket(tsTicketName);
	tsTicket.remove();
    }
    _pDBMng->setTableSetRunState(tableSet, XML_ONLINE_VALUE);    

    addBUStat(tabSetId, Chain("END BACKUP"), msg);

}

void CegoTableManager::resetBUStat(const Chain& tableSet)
{
    int tabSetId =_pDBMng->getTabSetId(tableSet);
    truncateObject(tabSetId, Chain(SYSTAB_BUSTAT_ID), CegoObject::SYSTEM);
}

void CegoTableManager::syncTableSet(const Chain& tableSet, const Chain& msg, const Chain& escCmd, int timeout)
{

    if ( _pDBMng->getTableSetRunState(tableSet) != Chain(XML_ONLINE_VALUE) )
    {
	Chain msg = Chain("Tableset ") + tableSet + Chain(" must be in runstate ONLINE to perform sync");
	throw Exception(EXLOC, msg);
    }

    writeCheckPoint(tableSet, true, true, escCmd, timeout);
    int tabSetId =_pDBMng->getTabSetId(tableSet);
    addBUStat(tabSetId, Chain("EXTERNAL SYNC"), msg);
}

void CegoTableManager::logTo(const Chain& tableSet, const Chain& secondary)
{

    Host h;    

    int tabSetId = _pDBMng->getTabSetId(tableSet);
  
    _pDBMng->releaseLogConnection(tabSetId);
    
    if ( secondary != h.getName() )
    {
	int logPort;
	_pDBMng->getLogPort(logPort);
	
	_pDBMng->allocateLogConnection(tabSetId, tableSet, secondary, logPort);	
    }
    else
    {
	_pDBMng->setActiveLogFile(tableSet);
    }
    
    long cplsn = _pDBMng->getCommittedLSN(tableSet);
    
    _pDBMng->setLSN(tabSetId, cplsn+1);
    _pDBMng->startLog(tabSetId);    
}

void CegoTableManager::addDataFile(const Chain& tableSet, const Chain& type, int fileId, const Chain& dataFile, int fileSize)
{
    
    Chain runState = _pDBMng->getTableSetRunState(tableSet);
    
    if ( runState != Chain(XML_DEFINED_VALUE) )
    {
	int tabSetId = _pDBMng->getTabSetId(tableSet);

	_pDBMng->log(_modId, Logger::NOTICE, Chain("Initializing datafile ") + dataFile + Chain(" ..."));	
	
	if ( type == Chain(XML_APPFILE_VALUE) )
	{
	    _pDBMng->initDataFile(tabSetId, dataFile, fileId, fileSize, CegoFileHandler::DATAFILE);
	}
	else if ( type == Chain(XML_TEMPFILE_VALUE) )
	{
	    _pDBMng->initDataFile(tabSetId, dataFile, fileId, fileSize, CegoFileHandler::TEMP);
	}
	else if ( type == Chain(XML_SYSFILE_VALUE) )
	{
	    _pDBMng->initDataFile(tabSetId, dataFile, fileId, fileSize, CegoFileHandler::SYSTEMFILE);
	}
	
	_pDBMng->regDataFile(tabSetId, dataFile, fileId, _pLockHandle);
    }

    // _pDBMng->addDataFile(tableSet, type, fileId, dataFile, fileSize);    
}

long CegoTableManager::writeCheckPoint(const Chain& tableSet, bool switchLog, bool archComplete, const Chain& escCmd, int timeout)
{

    int tabSetId = _pDBMng->getTabSetId(tableSet);    

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Writing checkpoint for tableset ") + tableSet + Chain(" as lsn = ") 
		 + Chain(_pDBMng->getLSN(tabSetId) - 1) + Chain(" ..."));    
    
    long lsn = _pDBMng->writeCheckPoint(tabSetId, switchLog, escCmd, timeout, _pLockHandle);

    if ( archComplete )
    {	
	while ( archiveComplete(tableSet) == false )
	{
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Waiting to complete archiving in tableset ") + tableSet + Chain(" ...")); 

	    Sleeper s;
	    s.secSleep(LOGMNG_LOGSWITCH_WAIT_DELAY);

	}
    }
    return lsn;
}

void CegoTableManager::setIsolationLevel(IsolationLevel level)
{
    _isolationLevel = level;
}

CegoTableManager::IsolationLevel CegoTableManager::getIsolationLevel() const
{
    return _isolationLevel;
}

void CegoTableManager::setAutoCommit(bool autoCommit)
{
    _autoCommit = autoCommit;
}

bool CegoTableManager::getAutoCommit() const
{
    return _autoCommit;
}

void CegoTableManager::beginTransaction(int tabSetId)
{    

    if (_tid[tabSetId] == 0)
    {
	_tid[tabSetId]=_pDBMng->nextTID(tabSetId);
	_tastep[tabSetId]=0;

	// create log entry
	CegoLogRecord lr;
	lr.setAction(CegoLogRecord::LOGREC_BEGIN);
	lr.setTID(_tid[tabSetId]);

	logIt(tabSetId, lr);

    }
    else
    {
	Chain msg = "Already active transaction on tableset " + Chain(tabSetId);
	throw Exception(EXLOC, msg);	
    }
}

void CegoTableManager::getTransactionAffectedTables(int tabSetId, SetT<Chain>& tableList)
{
    int ctid = _tid[tabSetId];
    if ( ctid > 0 )
	_pTM->getTransactionAffectedTables(tabSetId, ctid, tableList);
}

long CegoTableManager::commitTransaction(int tabSetId)
{

    if (_tid[tabSetId] != 0)
    {
	int ctid = _tid[tabSetId];
	_tid[tabSetId] = 0;
	_tastep[tabSetId] = 0;

	// create log entry
	CegoLogRecord lr;
	lr.setAction(CegoLogRecord::LOGREC_COMMIT);
	lr.setTID(ctid);

	long n = _pTM->commitTransaction(tabSetId, ctid);

	logIt(tabSetId, lr);

	return n;
    }
    else
    {
	// nothing to do
	return 0;
    }
}

long CegoTableManager::rollbackTransaction(int tabSetId)
{
    if (_tid[tabSetId] != 0)
    {
	int ctid = _tid[tabSetId];
	_tid[tabSetId] = 0;

	// create log entry
	CegoLogRecord lr;
	lr.setAction(CegoLogRecord::LOGREC_ABORT);
	lr.setTID(_tid[tabSetId]);
	logIt(tabSetId, lr);

	return _pTM->rollbackTransaction(tabSetId, ctid);
    }
    else
    {
	// nothing to do
	return 0;
    }
}

int CegoTableManager::getTID(int tabSetId)
{
    return(_tid[tabSetId]);
}

int CegoTableManager::getTAStep(int tabSetId)
{
    return(_tastep[tabSetId]);
}

void CegoTableManager::setTID(int tabSetId, int tid)
{
    _tid[tabSetId] = tid;
}

void CegoTableManager::setTupleInfo(int tabSetId, const CegoDataPointer dp, int tid, int tastep, CegoTupleState ts)
{
    
    char *pc;
    int len;

    unsigned long lockId = claimDataPtr(tabSetId, CegoLockHandler::WRITE, CegoBufferPool::SYNC, dp, pc, len);
    
    memcpy(pc, &tid, sizeof(int));
    pc += sizeof(int);
    memcpy(pc, &tastep, sizeof(int));
    pc += sizeof(int);

    memcpy(pc, &ts, sizeof(CegoTupleState)); 

    releaseDataPtr(lockId, true);

}

void CegoTableManager::getTupleInfo(int tabSetId, const CegoDataPointer dp, int& tid, int& tastep, CegoTupleState& ts)
{
    
    char *pc;
    int len;

    unsigned long lockId = claimDataPtr(tabSetId, CegoLockHandler::WRITE, CegoBufferPool::SYNC, dp, pc, len);

    memcpy(&tid, pc, sizeof(int));
    pc = pc + sizeof(int);    
    memcpy(&tastep, pc, sizeof(int));
    pc = pc + sizeof(int);
    memcpy(&ts, pc, sizeof(CegoTupleState));
         
    releaseDataPtr(lockId, true);

}

void CegoTableManager::createDataTable(int tabSetId, const Chain& tableName, CegoObject::ObjectType type, const ListT<CegoField>& fl, bool useColumnId)
{

    // check duplicate column names

    ListT<CegoField> cfl = fl;
    CegoField* pF = fl.First();
    while (pF)
    {
	int count=0;
	CegoField* pCF = cfl.First();
	while ( pCF )
	{
	    if ( pCF->getAttrName() == pF->getAttrName() )
		count++;	    
	    pCF = cfl.Next();
	}

	if ( count > 1 )
	{
	    Chain msg = Chain("Column ") + pF->getAttrName() + Chain(" multiple defined");
	    throw Exception(EXLOC, msg);
	}
	
	pF = fl.Next();
    }
    
    
    // define field id's
    int id;

    if ( useColumnId == false )
    {
	id = 1;
	CegoField* pF = fl.First();
	while (pF)
	{
	    pF->setId(id);
	    pF = fl.Next();
	    if ( pF )
		id++;
	}
    }
    else
    {
	// set max id
	id = 1;
	CegoField* pF = fl.First();
	while (pF)
	{
	    if ( id < pF->getId() )
		id = pF->getId();
	    pF = fl.Next();
	}
	
    }
    
    CegoTableObject oe(tabSetId, type, tableName, fl, tableName);
    oe.setMaxFid(id);
    createTableObject(oe);
    
    if ( oe.getType() != CegoObject::RBSEG )
    {
	// create log entry
	CegoLogRecord lr;
	lr.setObjectInfo(oe.getName(), oe.getType());
	lr.setAction(CegoLogRecord::LOGREC_CREATE);
	
	char *buf;
	buf = (char*)malloc(oe.getEntrySize());
	if (buf == NULL)
	{
	    throw Exception(EXLOC, Chain("malloc system error"));
	}
	oe.encode(buf);
	lr.setData(buf);
	lr.setDataLen(oe.getEntrySize());

	logIt(oe.getTabSetId(), lr);
	free(buf);
    }

}

void CegoTableManager::alterDataTable(int tabSetId, const Chain& tableName, CegoObject::ObjectType type, const ListT<CegoAlterDesc>& alterList)
{

    CegoTableObject oe;
    getObject(tabSetId, tableName, type, oe);

    ListT<CegoKeyObject> keyAlterList;
    ListT<CegoTableObject> idxAlterList;

    ListT<CegoKeyObject> keyDropList;
    ListT<CegoTableObject> idxDropList;
    ListT<CegoBTreeObject> btreeDropList;

    // getting max id
    
    ListT<CegoField> alterSchema = oe.getSchema();

    int maxFid = oe.getMaxFid();
    
    // define field id's

    CegoAlterDesc* pAD = alterList.First();
    while (pAD)
    {
	switch ( pAD->getType() )
	{
	case CegoAlterDesc::ADD:
	{
	    
	    CegoField* pSF = alterSchema.Find(CegoField(tableName, pAD->getAttrName()));
	    if (pSF)
	    { 
		Chain msg = Chain("Column ") + pAD->getAttrName() + Chain(" already exists");
		throw Exception(EXLOC, msg);
	    }
	    else
	    {

		if ( pAD->getField().isNullable() == false )
		{
		
		    CegoObjectCursor *pOC = getObjectCursor(tabSetId, tableName, tableName, type);
		    int len;
		    CegoDataPointer dp;
		    if ( pOC->getFirst(len, dp) )
		    {
			
			delete pOC;
			throw Exception(EXLOC, Chain("Column ") + pAD->getAttrName() + Chain(" cannot be added as not nullable to non-empty table"));
		    }
		    delete pOC;
		}

		maxFid++;
		pAD->getField().setId(maxFid);
		alterSchema.Insert(pAD->getField());
		
	    }
	    break;
	}
	case CegoAlterDesc::DROP:
	{
	    
	    getKeyAndIdxRef(tabSetId, tableName, pAD->getAttrName(), keyDropList, idxDropList, btreeDropList);
		
	    CegoField* pSF = alterSchema.Find(CegoField(tableName, pAD->getAttrName()));
	    if (pSF)
	    { 
		alterSchema.Remove(*pSF);		
	    }
	    else
	    {
		Chain msg = Chain("Column ") + pAD->getAttrName() + Chain(" does not exist");
		throw Exception(EXLOC, msg);
	    }
	    break;
	}
	case CegoAlterDesc::MODIFY:
	{

	    ListT<CegoKeyObject> keyList;
	    ListT<CegoTableObject> idxList;
	    ListT<CegoBTreeObject> btreeList;
    
	    getKeyAndIdxRef(tabSetId, tableName, pAD->getAttrName(), keyList, idxList, btreeList);

	    CegoKeyObject* pKey = keyList.First();
	    while ( pKey )
	    {
		if ( (Chain)pKey->getTabName() == (Chain)tableName )
		{		
		    CegoField *pF = pKey->getKeySchema().First();
		    while ( pF )
		    {
			if ( pF->getAttrName() == pAD->getAttrName() )
			{
			    pF->setValue(pAD->getField().getValue());
			}
			pF = pKey->getKeySchema().Next();
		    }
		}
		else if ( (Chain)pKey->getRefTable() == (Chain)tableName )
		{
		    CegoField *pF = pKey->getRefSchema().First();
		    while ( pF )
		    {
			if ( pF->getAttrName() == pAD->getAttrName() )
			{
			    pF->setValue(pAD->getField().getValue());
			}
			pF = pKey->getRefSchema().Next();
		    }	      
		}
		pKey = keyList.Next();
	    }
	    
	    CegoTableObject* pIdx = idxList.First();
	    while ( pIdx )
	    {
		CegoField *pF = pIdx->getSchema().First();
		while ( pF )
		{
		    if ( pF->getAttrName() == pAD->getAttrName() )
		    {
			pF->setValue(pAD->getField().getValue());
		    }
		    pF = pIdx->getSchema().Next();
		}	      
		pIdx = idxList.Next();
	    }
	    
	    keyAlterList += keyList;
	    idxAlterList += idxList;

	    CegoField* pSF = alterSchema.Find(CegoField(tableName, pAD->getAttrName()));
	    if (pSF)
	    {
		int id = pSF->getId();
		*pSF = pAD->getField();
		pSF->setId(id);
	    }
	    else
	    {
		Chain msg = Chain("Column ") + pAD->getAttrName() + Chain(" does not exist");
		throw Exception(EXLOC, msg);
	    }
	    break;    
	}
	case CegoAlterDesc::RENAME:
	{

	    ListT<CegoKeyObject> keyList;
	    ListT<CegoTableObject> idxList;
	    ListT<CegoBTreeObject> btreeList;

	    getKeyAndIdxRef(tabSetId, tableName, pAD->getAttrName(), keyList, idxList, btreeList);

	    CegoKeyObject* pKey = keyList.First();
	    while ( pKey )
	    {
		if ( (Chain)pKey->getTabName() == (Chain)tableName )
		{		
		    CegoField *pF = pKey->getKeySchema().First();
		    while ( pF )
		    {
			if ( pF->getAttrName() == pAD->getOldAttrName() )
			{
			    pF->setAttrName(pAD->getAttrName());
			}
			pF = pKey->getKeySchema().Next();
		    }
		}
		else if ( (Chain)pKey->getRefTable() == (Chain)tableName )
		{
		    CegoField *pF = pKey->getRefSchema().First();
		    while ( pF )
		    {
			if ( pF->getAttrName() == pAD->getOldAttrName() )
			{
			    pF->setAttrName(pAD->getAttrName());
			}
			pF = pKey->getRefSchema().Next();
		    }	      
		}
		pKey = keyList.Next();
	    }
	    
	    CegoTableObject* pIdx = idxList.First();
	    while ( pIdx )
	    {
		CegoField *pF = pIdx->getSchema().First();
		while ( pF )
		{
		    if ( pF->getAttrName() == pAD->getOldAttrName() )
		    {			
			pF->setAttrName(pAD->getAttrName());
		    }
		    pF = pIdx->getSchema().Next();
		}	      
		pIdx = idxList.Next();
	    }
	    
	    keyAlterList += keyList;
	    idxAlterList += idxList;

	    CegoField* pSF = alterSchema.Find(CegoField(tableName, pAD->getOldAttrName()));
	    if (pSF)
	    { 
		pSF->setAttrName(pAD->getField().getAttrName());
	    }
	    else
	    {
		Chain msg = Chain("Column ") + pAD->getAttrName() + Chain("does not exist");
		throw Exception(EXLOC, msg);
	    }
	    break;
	}
       	
	}
	pAD = alterList.Next();
    }

    CegoKeyObject* pDropKey = keyDropList.First();
    while ( pDropKey )
    {
	
	removeObject(tabSetId, pDropKey->getName(), pDropKey->getType());
	
	// create log entry
	CegoLogRecord lr;
	lr.setObjectInfo(pDropKey->getName(), pDropKey->getType());
	lr.setAction(CegoLogRecord::LOGREC_DROP);    
	lr.setData(0);
	lr.setDataLen(0);
	logIt(tabSetId, lr);
	
	pDropKey = keyDropList.Next();
    }

    CegoTableObject* pDropIdx = idxDropList.First();
    while ( pDropIdx )
    {
	removeObject(tabSetId, pDropIdx->getName(), pDropIdx->getType());
	
	// create log entry
	CegoLogRecord lr;
	lr.setObjectInfo(pDropIdx->getName(), pDropIdx->getType());
	lr.setAction(CegoLogRecord::LOGREC_DROP);    
	lr.setData(0);
	lr.setDataLen(0);
	logIt(tabSetId, lr);
		
	pDropIdx = idxDropList.Next();
    }

    CegoBTreeObject* pDropBTree = btreeDropList.First();
    while ( pDropBTree )
    {
	removeObject(tabSetId, pDropBTree->getName(), pDropBTree->getType());
	
	// create log entry
	CegoLogRecord lr;
	lr.setObjectInfo(pDropBTree->getName(), pDropBTree->getType());
	lr.setAction(CegoLogRecord::LOGREC_DROP);    
	lr.setData(0);
	lr.setDataLen(0);
	logIt(tabSetId, lr);
		
	pDropBTree = btreeDropList.Next();
    }


    CegoKeyObject* pKey = keyAlterList.First();
    while ( pKey )
    {
	alterKeyObject(tabSetId, tableName, CegoObject::FKEY, *pKey);

	// create log entry 
	CegoLogRecord lr;
	lr.setObjectInfo(pKey->getName(), pKey->getType());
	lr.setAction(CegoLogRecord::LOGREC_ALTER);
	
	char *buf;
	buf = (char*)malloc(pKey->getEntrySize());
	if (buf == NULL)
	{
	    throw Exception(EXLOC, Chain("malloc system error"));
	}
    
	pKey->encode(buf);
	lr.setData(buf);
	lr.setDataLen(pKey->getEntrySize());
	
	logIt(tabSetId, lr);
	
	free(buf);


	pKey = keyAlterList.Next();
    }

    CegoTableObject* pIdx = idxAlterList.First();
    while ( pIdx )
    {

	alterTableObject(tabSetId, pIdx->getName(), pIdx->getType(), *pIdx);

	// create log entry 

	CegoLogRecord lr;
	lr.setObjectInfo(pIdx->getName(), pIdx->getType());
	lr.setAction(CegoLogRecord::LOGREC_ALTER);
	
	char *buf;
	buf = (char*)malloc(pIdx->getEntrySize());
	if (buf == NULL)
	{
	    throw Exception(EXLOC, Chain("malloc system error"));
	}

	pIdx->encode(buf);
	lr.setData(buf);
	lr.setDataLen(pIdx->getEntrySize());
	
	logIt(tabSetId, lr);
	
	free(buf);

	pIdx = idxAlterList.Next();
    }
 
    CegoTableObject aoe(tabSetId, type, tableName, alterSchema, tableName);
    aoe.setMaxFid(maxFid);

    alterTableObject(tabSetId, tableName, type, aoe);
    
    // create log entry 
    CegoLogRecord lr;
    lr.setObjectInfo(aoe.getName(), aoe.getType());
    lr.setAction(CegoLogRecord::LOGREC_ALTER);

    char *buf;
    buf = (char*)malloc(aoe.getEntrySize());
    if (buf == NULL)
    {
	throw Exception(EXLOC, Chain("malloc system error"));
    }
    
    aoe.encode(buf);
    lr.setData(buf);
    lr.setDataLen(aoe.getEntrySize());

    logIt(aoe.getTabSetId(), lr);

    free(buf);
}

void CegoTableManager::renameObject(int tabSetId, const Chain& objName,  CegoObject::ObjectType type, const Chain& newObjName)
{
    switch ( type )
    {
    case CegoObject::TABLE:
    {
	renameTable(tabSetId, objName, newObjName);
	break;
    }
    case CegoObject::INDEX:
    case CegoObject::UINDEX:
    case CegoObject::PINDEX:
    {
	renameIndex(tabSetId, objName, type, newObjName);
	break;
    }
    case CegoObject::FKEY:
    {
	renameKey(tabSetId, objName, newObjName);
	break;
    }
    case CegoObject::PROCEDURE:
    {
	renameProcedure(tabSetId, objName, newObjName);
	break;
    }
    case CegoObject::VIEW:
    {
	renameView(tabSetId, objName, newObjName);
	break;
    }
    case CegoObject::CHECK:
    {
	renameCheck(tabSetId, objName, newObjName);
	break;
    }
    case CegoObject::RBSEG:
    {
	renameRBO(tabSetId, objName, newObjName);
	break;
    }
    default:
	throw Exception(EXLOC, Chain("Cannot rename object type"));
    }
}

void CegoTableManager::renameTable(int tabSetId, const Chain& tableName, const Chain& newTableName)
{

    CegoTableObject oe;
    getObject(tabSetId, tableName, CegoObject::TABLE, oe);

    CegoObject::ObjectType type = oe.getType();
    oe.setName(newTableName);
    oe.setTabName(newTableName);    

    ListT<CegoTableObject> idxList;
    ListT<CegoBTreeObject> btreeList;
    ListT<CegoKeyObject> keyList;
    ListT<CegoCheckObject> checkList;

    getObjectListByTable(tabSetId, tableName, idxList, btreeList, keyList, checkList);
    
    CegoTableObject *pIO = idxList.First();
    while ( pIO )
    {
	if ( pIO->getType() == CegoObject::INDEX 
	     || pIO->getType() == CegoObject::UINDEX
	     || pIO->getType() == CegoObject::PINDEX )
	{

	    CegoTableObject idx;
	    getObject(tabSetId, pIO->getName(), pIO->getType(), idx);
	    idx.setTabName( newTableName );
	
	    alterTableObject(tabSetId, pIO->getName(), pIO->getType(), idx);
	    
	}
	pIO = idxList.Next();
    }

    CegoBTreeObject *pBTO = btreeList.First();
    while ( pBTO )
    {
	if ( pBTO->getType() == CegoObject::BTREE
	     || pBTO->getType() == CegoObject::UBTREE
	     || pBTO->getType() == CegoObject::PBTREE )
	{

	    CegoBTreeObject btree;
	    getObject(tabSetId, pBTO->getName(), pBTO->getType(), btree);
	    btree.setTabName( newTableName );
	
	    alterBTreeObject(tabSetId, pBTO->getName(), pBTO->getType(), btree);
	    
	}
	pIO = idxList.Next();
    }


    CegoKeyObject *pKO = keyList.First();
    while ( pKO )
    {

	CegoKeyObject keyObj;
	getObject(tabSetId, pKO->getName(), pKO->getType(), keyObj);
	keyObj.setTabName( newTableName );
	
	alterKeyObject(tabSetId, pKO->getName(), pKO->getType(), keyObj);

	pKO = keyList.Next();
    }

    CegoCheckObject *pCO = checkList.First();
    while ( pKO )
    {

	CegoCheckObject checkObj;
	getObject(tabSetId, pCO->getName(), pCO->getType(), checkObj);
	checkObj.setTabName( newTableName );
	
	alterCheckObject(tabSetId, pCO->getName(), pCO->getType(), checkObj);

	pCO = checkList.Next();
    }

    alterTableObject(tabSetId, tableName, type, oe);

    // create log entry 
    CegoLogRecord lr;
    lr.setObjectInfo(tableName, type);
    lr.setAction(CegoLogRecord::LOGREC_RENAME);

    lr.setData((char*)newTableName);
    lr.setDataLen(newTableName.length());

    logIt(oe.getTabSetId(), lr);

}

void CegoTableManager::renameIndex(int tabSetId, const Chain& idxName, CegoObject::ObjectType type, const Chain& newIdxName)
{

    if ( type != CegoObject::PINDEX && type !=  CegoObject::UINDEX && type != CegoObject::INDEX )
	throw Exception(EXLOC, Chain("Invalid index type"));	
    
    CegoTableObject oe;
    getObject(tabSetId, idxName, type, oe);

    oe.setName(newIdxName);
        
    alterTableObject(tabSetId, idxName, type, oe);

    // create log entry 
    CegoLogRecord lr;
    lr.setObjectInfo(idxName, type);
    lr.setAction(CegoLogRecord::LOGREC_RENAME);


    lr.setData((char*)newIdxName);
    lr.setDataLen(newIdxName.length());

    logIt(oe.getTabSetId(), lr);

}

void CegoTableManager::renameKey(int tabSetId, const Chain& keyName, const Chain& newKeyName)
{
    
    CegoKeyObject oe;
    getObject(tabSetId, keyName, CegoObject::FKEY, oe);

    oe.setName(newKeyName);
        
    alterKeyObject(tabSetId, keyName, CegoObject::FKEY, oe);

    // create log entry 
    CegoLogRecord lr;
    lr.setObjectInfo(keyName, CegoObject::FKEY);
    lr.setAction(CegoLogRecord::LOGREC_RENAME);

    lr.setData((char*)newKeyName);
    lr.setDataLen(newKeyName.length());

    logIt(tabSetId, lr);

}

void CegoTableManager::renameRBO(int tabSetId, const Chain& rboName, const Chain& newRboName)
{
    
    CegoTableObject rbo;
    getObject(tabSetId, rboName, CegoObject::RBSEG, rbo);

    rbo.setName(newRboName);
        
    alterTableObject(tabSetId, rboName, CegoObject::RBSEG, rbo);

    // no need to create log entry

    /*
    CegoLogRecord lr;
    lr.setObjectInfo(rboName, CegoObject::RBSEG);
    lr.setAction(CegoLogRecord::LOGREC_RENAME);

    lr.setData((char*)newRboName);
    lr.setDataLen(newRboName.length());
    lr.setTID(0);

    logIt(tabSetId, lr);
    
    */

}


void CegoTableManager::renameProcedure(int tabSetId, const Chain& procName, const Chain& newProcName)
{
    
    CegoProcObject oe;
    getObject(tabSetId, procName, CegoObject::FKEY, oe);

    oe.setName(newProcName);
        
    alterProcObject(tabSetId, procName, oe);

    // create log entry 
    CegoLogRecord lr;
    lr.setObjectInfo(procName, CegoObject::FKEY);
    lr.setAction(CegoLogRecord::LOGREC_RENAME);


    lr.setData((char*)newProcName);
    lr.setDataLen(newProcName.length());

    logIt(tabSetId, lr);

}

void CegoTableManager::renameView(int tabSetId, const Chain& viewName, const Chain& newViewName)
{

    CegoViewObject oe;
    getObject(tabSetId, viewName, CegoObject::VIEW, oe);

    oe.setName(newViewName);
        
    alterViewObject(tabSetId, viewName, oe);

    // create log entry 
    CegoLogRecord lr;
    lr.setObjectInfo(viewName, CegoObject::VIEW);
    lr.setAction(CegoLogRecord::LOGREC_RENAME);
    lr.setData((char*)newViewName);
    lr.setDataLen(newViewName.length());

    logIt(tabSetId, lr);

}

void CegoTableManager::renameCheck(int tabSetId, const Chain& checkName, const Chain& newCheckName)
{

    CegoCheckObject oe;
    getObject(tabSetId, checkName, CegoObject::CHECK, oe);

    oe.setName(newCheckName);
        
    alterCheckObject(tabSetId, checkName, oe);

    // create log entry 
    CegoLogRecord lr;
    lr.setObjectInfo(checkName, CegoObject::CHECK);
    lr.setAction(CegoLogRecord::LOGREC_RENAME);
    lr.setData((char*)newCheckName);
    lr.setDataLen(newCheckName.length());

    logIt(tabSetId, lr);

}

void CegoTableManager::reorgTable(int tabSetId, const Chain& tableName)
{
    reorgObject(tabSetId, tableName, CegoObject::TABLE);
}

void CegoTableManager::invalidateIndexForTable(int tabSetId, const Chain& tableName)
{

    ListT<CegoTableObject> idxList;
    ListT<CegoBTreeObject> btreeList;
    ListT<CegoKeyObject> keyList;
    ListT<CegoCheckObject> checkList;
    
    getObjectListByTable(tabSetId, tableName, idxList, btreeList, keyList, checkList);

    CegoTableObject *pIdx = idxList.First();
    while ( pIdx )
    {
	invalidateObject(tabSetId, pIdx->getName(), pIdx->getType());
	pIdx = idxList.Next();
    }
}

void CegoTableManager::insertDataTable(CegoTableObject& oe, ListT<CegoField>& fvl, CegoDataPointer& dp, bool doLogging)
{
    // nextTAStep
    if ( getTID(oe.getTabSetId()) != 0 ) 
	_tastep[oe.getTabSetId()]++;

    ListT<CegoTableObject> idxList;
    ListT<CegoBTreeObject> btreeList;
    ListT<CegoKeyObject> keyList;
    ListT<CegoCheckObject> checkList;
    
    getObjectListByTable(oe.getTabSetId(), oe.getName(), idxList, btreeList, keyList, checkList);
    
    CegoDataPointer sysEntry;
    Chain virginIndex;
    insertDataTable(oe, fvl, idxList, btreeList, keyList, checkList, sysEntry, virginIndex, dp, doLogging);
}

void CegoTableManager::insertDataTable(CegoTableObject& oe, ListT<CegoField>& nfvl, 
				       const ListT<CegoTableObject>& idxList, 
				       const ListT<CegoBTreeObject>& btreeList, 
				       const ListT<CegoKeyObject>& keyList, 
				       const ListT<CegoCheckObject>& checkList, 
				       const CegoDataPointer& sysEntry,
				       const Chain& virginIndex,
				       CegoDataPointer& dp, bool doLogging)
{

    CegoDataPointer nil;   

    dp = nil;

    int buflen = 0;
    char *pBufBase = 0;
    int tid = 0;
    int tastep = 0;
    CegoTupleState ts = COMMITTED;

    int idxInsertCount=0;

    /////////////////////////////////////////////
    // Step 1 : Setting ids and check null values
    /////////////////////////////////////////////
    // after setting ids, the fvl must be sorted in terms of the id


    CegoField* pF = nfvl.First();
    while (pF)
    {
	
	// cout << "Searching for " << oe.getName() << "/" << pF->getAttrName() << endl;
	CegoField* pSF = oe.getSchema().Find(CegoField(oe.getName(), pF->getAttrName()));
	
	// check for allowed null values
	if ( pF->getValue().getType() == NULL_TYPE && pSF->isNullable() == false )
	{
	    throw Exception(EXLOC, Chain("Invalid null value"));
	}
	
	if ( pSF )
	{
	    pF->setId(pSF->getId());
	}
	pF = nfvl.Next();
    }
    
    ListT<CegoField> fvl;
    
    pF = nfvl.First();
    int maxId=0;
    while ( pF )
    {
	if ( pF->getId() > maxId )
	    maxId = pF->getId();
	pF = nfvl.Next();
    }
    for ( int id=0; id<=maxId; id++ )
    {
	CegoField *pF = nfvl.First();
	while ( pF )
	{
	    if ( pF->getId() == id )
	    {
		fvl.Insert(*pF);
		pF = 0;
	    }
	    else
	    {
		pF = nfvl.Next();
	    }
	}
    }
    
    try 
    {
       
       
	/////////////////////////////////////////////
	// Step 2 : Insert table data
	/////////////////////////////////////////////

	if ( oe.getType() == CegoObject::TABLE )
	{
	    
	    tid = getTID(oe.getTabSetId());
	    tastep = getTAStep(oe.getTabSetId());
	    if ( tid > 0 )
		ts = INSERTED;
	}

	_qh.encodeFVL(tid, tastep, ts, fvl, pBufBase, buflen);

	int numTries=0;
	while ( dp == nil )
	{
	    try
	    {
		if ( sysEntry == nil )
		{
		    dp = insertData(oe, pBufBase, buflen, _doAppend);
		}
		else
		{
		    dp = insertData(sysEntry, oe, pBufBase, buflen, _doAppend);
		}
	    }
	    catch ( Exception e)
	    {

		Chain msg;
		e.pop(msg);
		_pDBMng->log(_modId, Logger::NOTICE, Chain("Retry insert after :  ") +  msg);
		
		if ( numTries == LCKMNG_NUMLOCKTRIES )
		{
		    throw Exception(EXLOC, msg);
		}
		numTries++;
	    }
	}

	///////////////////////////////////
	// Step 3 : check key contraints //
	///////////////////////////////////
	
	// Note: since fkeys can refer to a primary key inside the same table and the same row, 
	// this must be done as the second step
	    
	if ( ! keyList.isEmpty() )
	{
	    CegoKeyObject* pKey = keyList.First();
	    while ( pKey )
	    {
		if ( (Chain)pKey->getTabName() == (Chain)oe.getName() )
		{
		    
		    Chain idxName = pKey->getRefTable() + Chain(TABMNG_PIDX_SUFFIX);
		    
		    if ( objectExists(oe.getTabSetId(), idxName, CegoObject::PINDEX ))
		    {

			CegoTableObject poe;
			getObject(oe.getTabSetId(), idxName, CegoObject::PINDEX, poe);
			
			CegoField *pIF = poe.getSchema().First();
			CegoField *pDF = fvl.First();
			bool firstFieldFound = false;
			while ( pDF && ! firstFieldFound ) 
			{
			    if ( (Chain)pDF->getAttrName() == (Chain)pIF->getAttrName() )
			    {
				CegoAttrCond ac;
				ac.add(CegoAttrComp(pDF->getAttrName(), EQUAL, pDF->getValue())); 
				CegoAVLIndexCursor ic(this, oe.getTabSetId(), idxName, CegoObject::PINDEX, &ac, false, false);
				CegoDataPointer dp;
				ListT<CegoField> ifl = poe.getSchema();
				bool isMatch=false;
				bool moreTuple = ic.getFirst(ifl, dp);
				while ( moreTuple && ! isMatch) 
				{
				    isMatch=true;
				    CegoField *pIF = ifl.First();
				    while ( pIF && isMatch )
				    {
					CegoField *pDF = fvl.Find(*pIF);
					if ( pDF )
					{
					    if ( ! ( (CegoFieldValue)pIF->getValue() == (CegoFieldValue)pDF->getValue() ) )
						isMatch=false;
					    
					}
					pIF = ifl.Next();
					
				    }
				    if ( isMatch == false )
					moreTuple = ic.getNext(ifl,dp);
				}
				
				if ( ! isMatch )
				{
				    Chain msg = Chain("Missing foreign key value in reference index <") + idxName + Chain(">");
				    throw Exception(EXLOC, msg);
				}
				
				firstFieldFound = true;
				
			    }
			    else
			    {
				pDF = fvl.Next(); 
			    }	
			}
		    }
		    else
		    {
			Chain btreeName = pKey->getRefTable() + Chain(TABMNG_PBTREE_SUFFIX);
			CegoBTreeObject pbt;
			getObject(oe.getTabSetId(), btreeName, CegoObject::PBTREE, pbt);

			CegoField *pIF = pbt.getSchema().First();
			CegoField *pDF = fvl.First();
			bool firstFieldFound = false;
			while ( pDF && ! firstFieldFound ) 
			{
			    if ( (Chain)pDF->getAttrName() == (Chain)pIF->getAttrName() )
			    {
				CegoAttrCond ac;
				ac.add(CegoAttrComp(pDF->getAttrName(), EQUAL, pDF->getValue())); 
				CegoBTreeCursor btc(this, oe.getTabSetId(), btreeName, CegoObject::PBTREE, &ac, false, false);
				CegoDataPointer dp;
				ListT<CegoField> ifl = pbt.getSchema();
				bool isMatch=false;
				bool moreTuple = btc.getFirst(ifl, dp);
				while ( moreTuple && ! isMatch) 
				{
				    isMatch=true;
				    CegoField *pIF = ifl.First();
				    while ( pIF && isMatch )
				    {
					CegoField *pDF = fvl.Find(*pIF);
					if ( pDF )
					{
					    if ( ! ( (CegoFieldValue)pIF->getValue() == (CegoFieldValue)pDF->getValue() ) )
						isMatch=false;
					    
					}
					pIF = ifl.Next();				    
				    }
				    if ( isMatch == false )
					moreTuple = btc.getNext(ifl,dp);
				}
				
				if ( ! isMatch )
				{
				    Chain msg = Chain("Missing foreign key value in reference btree <") + btreeName + Chain(">");
				    throw Exception(EXLOC, msg);
				}
				
				firstFieldFound = true;
				
			    }
			    else
			    {
				pDF = fvl.Next(); 
			    }	
			}
		    }			
		}
		pKey = keyList.Next();
	    }
	}

       	
	///////////////////////////////////
	// Step 4 : Check check constraints
	///////////////////////////////////
	
	if ( ! checkList.isEmpty() )
	{
	    CegoCheckObject* pCheck = checkList.First();
	    while ( pCheck )
	    {
		
		CegoQueryHelper qh;
	    
		// cout << "======= Check Constraint .." << endl;
		pCheck->getPredDesc()->clearAttrCache();
		if ( qh.evalPredicate(0, 0, 0, &fvl, 0, 1, pCheck->getPredDesc(), 0) == false )
		{
		    Chain msg = Chain("Check constraint ") + pCheck->getName() + Chain(" violated");
		    throw Exception(EXLOC, msg);
		}

		// cout << "======= Check Constraint done .." << endl;

		pCheck = checkList.Next();
	    }
	}

	
	///////////////////////////////////
	// Step 5: Add index ref         //
	///////////////////////////////////
	       	
	CegoTableObject* pOE = idxList.First();

	idxInsertCount=0;
	while (pOE)
	{
	    
	    if (pOE->getName() != virginIndex &&
		( pOE->getType() == CegoObject::INDEX 
		  || pOE->getType() == CegoObject::UINDEX 
		  || pOE->getType() == CegoObject::PINDEX ) )
	    {
		
		char p[TABMNG_MAXINDEXVALUE];
		int len = TABMNG_MAXINDEXVALUE;
		int idxLen;
		CegoDataPointer ritp;
		
		extractIndexValue(fvl, pOE->getSchema(), p, len, idxLen);
		
		bool resolveIt = true;
		
		// Chain dump;
		// dumpObject( oe.getTabSetId(), pOE->getName(), CegoObject::INDEX, dump);
		// cout << dump << endl;;
		
		// int h = checkIndex(oe.getTabSetId(), pOE->getName(), CegoObject::INDEX);
		// cout << "Index check ok, height = " << h << endl;

		CegoDataPointer nil;

		CegoAVLIndexManager idxMng(this);
		idxMng.insertNativeIndexTable(*pOE,
					      nil, 
					      dp, 
					      p,
					      idxLen, 
					      getTID(oe.getTabSetId()), 					      
					      _doAppend,
					      ritp);
		idxInsertCount++;

	    }
	    pOE = idxList.Next();
	}


	///////////////////////////////////
	// Step 6: Add btree ref         //
	///////////////////////////////////
	       	
	CegoBTreeObject* pBTO = btreeList.First();

	idxInsertCount=0;
	while (pBTO)
	{
	    
	    if (pBTO->getName() != virginIndex &&
		( pBTO->getType() == CegoObject::BTREE 
		  || pBTO->getType() == CegoObject::UBTREE 
		  || pBTO->getType() == CegoObject::PBTREE ) )
	    {
		
		
		CegoBTreeValue iv;
		iv.valueFromSchema(fvl, pBTO->getSchema());

		CegoBTreeManager btreeMng(this, pBTO);
		btreeMng.insertBTree(dp,
				     iv,
				     getTID(oe.getTabSetId()));

		idxInsertCount++;

	    }

	    pBTO = btreeList.Next();
	}

	///////////////////////////////////////////////

    }
    catch ( Exception e)
    {   	
	if ( dp != nil )
	{
	    deleteData(oe.getType(), oe.getTabSetId(), dp);	   

	    int i=0;
	    CegoTableObject* pOE = idxList.First();
	    while (pOE && i < idxInsertCount)
	    {
		
		if (pOE->getName() != virginIndex &&
		    ( pOE->getType() == CegoObject::INDEX 
		      || pOE->getType() == CegoObject::UINDEX 
		      || pOE->getType() == CegoObject::PINDEX ) )
		{
		    		    
		    char p[TABMNG_MAXINDEXVALUE];
		    int len = TABMNG_MAXINDEXVALUE;
		    int idxLen;
		    
		    extractIndexValue(fvl, pOE->getSchema(), p, len, idxLen);
		    
		    try
		    {
			// cout << "Deleting index for " << pOE->getName() << " of len " << idxLen << endl;		       
			CegoAVLIndexManager idxMng(this);
			idxMng.deleteIndexTable(oe.getTabSetId(), oe.getName(), oe.getType(), 
						pOE->getName(), pOE->getType(), pOE->getSchema(), dp, p, idxLen);		    
		    }
		    catch ( Exception e )
		    {
			Chain msg;
			e.pop(msg);
			throw Exception(EXLOC, Chain("Cannot delete index row : ") + msg);      
		    }
		    i++;
		}
		pOE = idxList.Next();

	    }
	}
	
	if ( pBufBase )
	{
	    free (pBufBase);
	}
	throw e;
    }

    // if all successful, make RB entry and log the insert action

    if (oe.getType() == CegoObject::TABLE && getTID(oe.getTabSetId()) != 0)
    {
	_pTM->newRBEntry(oe.getTabSetId(), getTID(oe.getTabSetId()), dp.getFileId(), dp.getPageId(), dp.getOffset(), oe.getName());
    }
    
    if (oe.getType() == CegoObject::TABLE && doLogging == true)
    {
	ListT<CegoBlob> blobList = getBlobs(oe.getTabSetId(), fvl);
	if ( blobList.Size() > 0 )
	{
	    free ( pBufBase );
	    _qh.encodeFVL(tid, tastep, ts, fvl, blobList, pBufBase, buflen);
	    CegoBlob *pBlob = blobList.First();
	    while ( pBlob )
	    {
		free ( pBlob->getBufPtr());
		pBlob = blobList.Next();
	    }
	}

	CegoLogRecord lr;
	lr.setObjectInfo(oe.getName(), CegoObject::TABLE);
	
	lr.setAction(CegoLogRecord::LOGREC_INSERT);
	lr.setData(pBufBase);
	lr.setDataLen(buflen);
	lr.setTID(getTID(oe.getTabSetId()));
	
	logIt(oe.getTabSetId(), lr);
	
    }

    if ( pBufBase )
    {
	free (pBufBase);
    }
}

long CegoTableManager::updateDataTable(int tabSetId, const Chain& tableName, const Chain& tableAlias, CegoPredDesc* pPred, const ListT<CegoField>& updList, ListT<CegoExpr*>& exprList, CegoProcBlock* pBlock)
{

    long updCount = 0;

    int tid = getTID(tabSetId);
    
    // nextTAStep
    if ( tid != 0 )
	_tastep[tabSetId]++;
    
    CegoTableObject oe;    
    CegoBufferPage bp;
    getObjectWithFix(tabSetId, tableName, CegoObject::TABLE, oe, bp);

    // make log entry first 

    CegoLogRecord lr;
    lr.setObjectInfo(tableName, CegoObject::TABLE);    
    lr.setAction(CegoLogRecord::LOGREC_UPDATE);

    char *pBuf = 0;
    int buflen = 0;
    
    _qh.encodeUpdRec(tableAlias, pPred, updList, exprList, pBlock, pBuf, buflen);

    lr.setData(pBuf);
    lr.setDataLen(buflen);
    lr.setTID(tid);
    
    logIt(oe.getTabSetId(), lr);
    if ( buflen > 0 )
	free ( pBuf );

    try
    {
	
	CegoDataPointer sysEntry(bp.getFileId(), bp.getPageId(), bp.getEntryPos());
	
	ListT<CegoTableObject> idxList;
	ListT<CegoBTreeObject> btreeList;
	ListT<CegoKeyObject> keyList;
	ListT<CegoCheckObject> checkList;
	
	getObjectListByTable(tabSetId, tableName, idxList, btreeList, keyList, checkList);
	
	if (pPred == 0)
	{	
	    
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Updating ") + oe.getTabName() + Chain(" with fulltablescan"));
#endif	    

	    CegoTableCursor* pTC = new CegoTableCursor(this, tabSetId, oe.getTabName(), true);
	    
	    try
	    {
		bool moreTuple;
		
		CegoDataPointer dp;
		ListT<CegoField> fl = oe.getSchema();
		
		// checkTypes(fl, updList);
	        		    
		moreTuple = pTC->getFirst(fl, dp);
		
		while ( moreTuple && _isAborted == false )
		{
		    
		    /*
		      
		      Since we are inside a transaction and use ObjectCursor, 
		      we don't have to reset the Tablecursor to the beginning after modifying the
		      table :
		      
		      delete  pTC;		
		      updateTuple(tabSetId, oe.getTabName(), CegoObject::TABLE, dp, fl, updSchema);
		      pTC = new CegoTableCursor(this, tabSetId, oe.getTabName(), true);		
		      moreTuple = pTC->getFirst(fl, dp);
		      
		    */
		    
		    updCount++;
		    
		    Chain virginIndex;
		    updateTuple(oe, sysEntry, dp, fl, updList, exprList, idxList, btreeList, keyList, checkList, virginIndex, pBlock);
		    
		    moreTuple = pTC->getNext(fl, dp);
		    
		}
	    }
	    catch ( Exception e )
	    {		
		delete pTC;
		throw e;
	    }
			
	    delete pTC;
	    
	    if ( _isAborted )
	    {	    
		throw Exception(EXLOC, Chain("Update aborted by user"));
	    } 		
	} 
	else
	{

	    ListT<CegoField> fl = oe.getSchema();
	    if ( tableName != tableAlias )
	    {
		// set up the defined alias for attribute evaluation
		CegoField *pF =  fl.First();
		while (pF)
		{	    
		    pF->setTableAlias(tableAlias);
		    pF = fl.Next();
		}
	    }

	    ListT<CegoSelect*> queryList;
	    pPred->getSelectQueryList(queryList);
	    CegoSelect **pSelect = queryList.First();
	    while ( pSelect )
	    {
		(*pSelect)->setParentJoinBuf(&fl, 1);
		pSelect = queryList.Next();
	    }
	    pPred->analyzeSelect();

	    CegoAttrCond ac;	    
	    CegoQueryHelper::AttrCondMatch m = _qh.checkAttrCond(ac, pPred, fl, &fl,1, pBlock);

	    if  ( m == CegoQueryHelper::COMPLETE )
	    {
		bool isAffected = false;

		CegoAttrComp *pAC = 0;
		pAC = ac.getAttrCompSet().First();
		
		while ( pAC )
		{
		    
		    CegoField *pF = updList.First();
		    while ( pF && isAffected == false )
		    {
			if ( pF->getAttrName() == pAC->getAttrName() )
			{
			    isAffected = true;
			}
			else
			{
			    pF = updList.Next();
			}
		    }

		    pAC = ac.getAttrCompSet().Next();

		}
		if ( isAffected == true )
		    m = CegoQueryHelper::INAPP;
	    }


	    if ( m == CegoQueryHelper::INAPP )
	    {

#ifdef CGDEBUG	
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Updating ") + oe.getTabName() + Chain(" with fulltablescan"));	    
#endif		

		CegoTableCursor* pTC = new CegoTableCursor(this, tabSetId, oe.getTabName(), true);

		try 
		{		

		    bool moreTuple;
		    
		    CegoDataPointer dp;
		    // ListT<CegoField> fl = (ListT<CegoField>)oe.getSchema();
		    
		    checkTypes(fl, updList);
				
		    moreTuple = pTC->getFirst(fl, dp);
		
		    while ( moreTuple && _isAborted == false )
		    {		    

			pPred->clearAttrCache();

			if (_qh.evalPredicate(0, 0, 0, &fl, 0, 1, pPred, pBlock))
			{
			    updCount++;
			    
			    Chain virginIndex;

			    updateTuple(oe, sysEntry, dp, fl, updList, exprList, idxList, btreeList, keyList, checkList, virginIndex, pBlock);
			    moreTuple = pTC->getNext(fl, dp);
			}
			else
			{
			    moreTuple = pTC->getNext(fl, dp);   
			}	 
		    }
		}
		catch ( Exception e )
		{
		    // now we can release the index root page
		    delete pTC;
		    throw e;
		}
		
		delete pTC;
		
		if ( _isAborted )
		{	    
		    throw Exception(EXLOC, Chain("Update aborted by user"));
		} 		
	    }
	    else
	    {
		
#ifdef CGDEBUG	
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Updating ") + oe.getTabName() + Chain(" using AttrCond ") + ac.toChain());		
#endif

		CegoTableCursor *pTC = new CegoTableCursor(this, tabSetId, oe.getTabName(), true);
		
		try 
		{
		    		    
		    checkTypes(fl, updList);
		    
		    CegoAttrCond::IndexMatch indexMatch = pTC->setup(ac);
		    
		    Chain virginIndex;
		    if ( indexMatch == CegoAttrCond::FULL || indexMatch == CegoAttrCond::PART )
			virginIndex = pTC->getIndexName();

#ifdef CGDEBUG	
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Virgin index is ") + virginIndex);		    
#endif

		    bool moreTuple;
		    CegoDataPointer dp;
		    
		    moreTuple = pTC->getFirst(fl, dp);
		    
		    while ( moreTuple && _isAborted == false )
		    {
#ifdef CGDEBUG	
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Checking pred for update ..."));
#endif
			pPred->clearAttrCache();
			if (_qh.evalPredicate(0, 0, 0, &fl, 0, 1,  pPred, pBlock))
			{			      
			    updCount++;
			    
#ifdef CGDEBUG	
			    _pDBMng->log(_modId, Logger::DEBUG, Chain("Updating tuple..."));
#endif
			    updateTuple(oe, sysEntry, dp, fl, updList, exprList, idxList, btreeList, keyList, checkList, virginIndex, pBlock);
			    moreTuple = pTC->getNext(fl, dp);
			}    
			else
			{
			    moreTuple = pTC->getNext(fl, dp);   
			}
		    }
		    		    
		    if ( virginIndex != Chain() )
			_pTM->commitUpdate(tabSetId, virginIndex, getTID(tabSetId), _doAppend);
		    

		}
		catch ( Exception e )
		{		   
		    delete pTC;
		    throw e;
		}


		delete pTC;
		
		if ( _isAborted )
		{	    
		    throw Exception(EXLOC, Chain("Update aborted by user"));
		}		    
	    }
	}
    }
    catch ( Exception e )
    {	
	// now we can release the index root page
	_pDBMng->bufferUnfix(bp, true, _pLockHandle);
	throw e;
    }

    _pDBMng->bufferUnfix(bp, true, _pLockHandle); 
 
#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Update finished, updCount = ") + Chain(updCount));
#endif

    // commit update record

    CegoLogRecord lrc;
    lrc.setObjectInfo(tableName, CegoObject::TABLE);    
    lrc.setAction(CegoLogRecord::LOGREC_UPDATE_COMMIT);
    lrc.setTID(tid);
   
    logIt(oe.getTabSetId(), lrc);

    return updCount;
}

void CegoTableManager::updateTuple(CegoTableObject& oe, const CegoDataPointer& sysEntry, const CegoDataPointer& dp, 
				   ListT<CegoField>& fl, 
				   const ListT<CegoField>& nfvl,
				   ListT<CegoExpr*>& exprList,
				   const ListT<CegoTableObject>& idxList,
				   const ListT<CegoBTreeObject>& btreeList,
				   const ListT<CegoKeyObject>& keyList, 
				   const ListT<CegoCheckObject>& checkList, 
				   const Chain& virginIndex,
				   CegoProcBlock* pBlock)
{
 
#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Updating tuple for ") + oe.getTabName());
#endif

    CegoField* pFV = nfvl.First();
    CegoExpr** pExpr = exprList.First();
    
    int pos=1;
    while ( pFV && pExpr)
    {
	(*pExpr)->setBlock(pBlock);
	(*pExpr)->setFieldListArray(&fl, 1);

	(*pExpr)->clearAttrCache();
	
	CegoFieldValue fv = (*pExpr)->evalFieldValue();

	_qh.prepareFieldValue(pFV, fv, pos);
	
	pFV->setValue(fv);
	pFV = nfvl.Next();
	pExpr = exprList.Next();	    
	pos++;
    }

    if ( pFV || pExpr )
    {
	throw Exception(EXLOC, Chain("Mismatched argument count for value list"));
    }
        
    ListT<CegoField> updSchema = nfvl;
    
    // complete updSchema
    CegoField* pUF = fl.First();
    while ( pUF )
    { 
	if ( ! updSchema.Find( *pUF ) )
	{
	    updSchema.Insert( *pUF );
	}
	pUF = fl.Next();
    }

#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Checking integrity ... "));
#endif

    checkIntegrity( oe.getTabSetId(), oe.getTabName(), dp, fl, updSchema);

#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Integrity ok"));    
#endif

    CegoDataPointer ndp;
    
    deleteDataTable(oe.getTabSetId(), oe.getTabName(), oe.getType(), dp, fl, idxList, btreeList, keyList, false);
    insertDataTable(oe, updSchema, idxList, btreeList, keyList, checkList, sysEntry, virginIndex, ndp, false);

    if ( virginIndex != Chain() )
	_pTM->recordUpdate(oe.getTabSetId(), getTID(oe.getTabSetId()), ndp);

}

void CegoTableManager::getKeyAndIdxRef(int tabSetId, const Chain& tableName, const Chain& attrName, 
				       ListT<CegoKeyObject>& refKeyList,
				       ListT<CegoTableObject>& refIdxList,
				       ListT<CegoBTreeObject>& refBTreeList)
{    
    ListT<CegoTableObject> idxList;
    ListT<CegoBTreeObject> btreeList;
    ListT<CegoKeyObject> keyList;
    ListT<CegoCheckObject> checkList;
    
    getObjectListByTable(tabSetId, tableName, idxList, btreeList, keyList, checkList);

    if ( ! keyList.isEmpty() )
    {
	
	CegoKeyObject* pKey = keyList.First();
	while ( pKey )
	{
	    if ( (Chain)pKey->getTabName() == (Chain)tableName )
	    {		
		CegoField *pF = pKey->getKeySchema().First();
		while ( pF )
		{
		    if ( pF->getAttrName() == attrName )
		    {
			refKeyList.Insert(*pKey);
		    }
		    pF = pKey->getKeySchema().Next();
		}
	    }
	    else if ( (Chain)pKey->getRefTable() == (Chain)tableName )
	    {
		CegoField *pF = pKey->getRefSchema().First();
		while ( pF )
		{
		    if ( pF->getAttrName() == attrName )
		    {
			refKeyList.Insert(*pKey);
		    }
		    pF = pKey->getRefSchema().Next();
		}	      
	    }
	    pKey = keyList.Next();
	}
    }
    if ( ! idxList.isEmpty() )
    {
	CegoTableObject* pIdx = idxList.First();
	while ( pIdx )
	{
	    
	    CegoField *pF = pIdx->getSchema().First();
	    while ( pF )
	    {
		
		if ( pF->getAttrName() == attrName )
		{
		    refIdxList.Insert(*pIdx);
		    // return true;
		}
		pF = pIdx->getSchema().Next();
	    }
	    pIdx = idxList.Next();
	}	
    }

    return;
}

// keyReferenceExists returns true, if there is any existing reference on the given tuple value (fl)
// based on the given keylist and tableName values
bool CegoTableManager::keyReferenceExists(int tabSetId, const Chain& tableName, const ListT<CegoField>& fl, const ListT<CegoKeyObject>& keyList)
{
        
    if ( ! keyList.isEmpty() )
    {
	CegoKeyObject* pKey = keyList.First();
	while ( pKey )
	{
	    if ( (Chain)pKey->getRefTable() == (Chain)tableName )
	    {
		
		CegoTableCursor tc(this, tabSetId, pKey->getTabName());
		
		CegoField *pDF = pKey->getKeySchema().First();
		CegoField *pF = fl.Find(*pDF);
		if ( pF )
		{
		    
		    CegoAttrCond ac;
		    ac.add(CegoAttrComp(pF->getAttrName(), EQUAL, pF->getValue()));
 
		    ListT<CegoField> tfl;
		    
		    CegoAttrCond::IndexMatch indexMatch = tc.setup(ac);

		    if ( indexMatch == CegoAttrCond::FULL || indexMatch == CegoAttrCond::PART )
		    {
			tc.getIdxSchema(tfl);
		    }
		    else
		    {
			CegoTableObject oe;
			getObject(tabSetId, pKey->getTabName(), CegoObject::TABLE, oe);
			tfl = oe.getSchema();
		    }

		    ListT<CegoField> kfl = pKey->getKeySchema();
                    CegoField *pKF = kfl.First();
                    while ( pKF )
                    {                        
                        pKF->setTableName(Chain());
                        pKF->setTableAlias(Chain());
                        CegoField *pF = tfl.Find(*pKF);
                        if ( pF )
                            pKF->setId(pF->getId());
                        
                        pKF = kfl.Next();                        
                    }
                    
                    CegoDataPointer dp;
                    		    		    
                    bool moreTuple = tc.getFirst(kfl, dp);
                    
                    bool isMatch = false;
                    while ( moreTuple && ! isMatch) 
                    {		
                        CegoField *pF = kfl.First();
                        while ( pF )
                        {
                            isMatch = true;
                            CegoField *pD = fl.Find(*pF);
                            if ( pD )
                            {				
                                if ( ! ( (CegoFieldValue)pD->getValue() == (CegoFieldValue)pF->getValue()) )
                                {
                                    isMatch=false;
                                }				
                            }
                            pF = kfl.Next();
                        }
                        moreTuple = tc.getNext(kfl, dp);
                    }
                    if ( isMatch )
                        return true;
		}
	    }
	    pKey = keyList.Next();
	}
    }
    return false;
}

void CegoTableManager::checkIntegrity(int tabSetId, const Chain& tableName, const CegoDataPointer& dp, const ListT<CegoField>& fvl, const ListT<CegoField>& nfvl)
{
    
    ListT<CegoTableObject> idxList;
    ListT<CegoBTreeObject> btreeList;
    ListT<CegoKeyObject> keyList;
    ListT<CegoCheckObject> checkList;
    
    getObjectListByTable(tabSetId, tableName, idxList, btreeList, keyList, checkList);

    if ( ! idxList.isEmpty() )
    {
	if ( checkIndexIntegrity(idxList, tabSetId, dp, fvl, nfvl) == false )
	    throw Exception(EXLOC, Chain("Constraint violation on unique index"));
    }
    if ( ! btreeList.isEmpty() )
    {
	if ( checkBTreeIntegrity(btreeList, tabSetId, dp, fvl, nfvl) == false )
	    throw Exception(EXLOC, Chain("Constraint violation on unique btree"));
    }
    if ( ! keyList.isEmpty() )
    {
	if ( checkKeyIntegrity(keyList, tabSetId, tableName, fvl, nfvl) == false )
	    throw Exception(EXLOC, Chain("Constraint violation on foreign key"));
    }
}

bool CegoTableManager::checkKeyIntegrity(const ListT<CegoKeyObject>& keyList, int tabSetId, const Chain& tableName, const ListT<CegoField>& fvl, const ListT<CegoField>& nfvl)
{

    CegoKeyObject* pKey = keyList.First();
    while ( pKey )
    {
	if ( (Chain)pKey->getRefTable() == (Chain)tableName )
	{
	    
	    CegoTableCursor tc(this, tabSetId, pKey->getTabName());
	    
	    CegoField *pDF = pKey->getKeySchema().First();
	    CegoField *pF = fvl.Find(*pDF);
	    if ( pF )
	    {
		
		CegoAttrCond ac;
		ac.add(CegoAttrComp(pF->getAttrName(), EQUAL, pF->getValue())); 
		ListT<CegoField> kfl;
		
		if ( tc.setup(ac) == false )
		{
		    CegoTableObject oe;
		    getObject(tabSetId, pKey->getTabName(), CegoObject::TABLE, oe);
		    kfl = oe.getSchema();
		}
		else
		{
		    tc.getIdxSchema(kfl);
		}
		
		CegoDataPointer dp;
		
		bool moreTuple = tc.getFirst(kfl, dp);
		
		bool isMatch = false;
		while ( moreTuple && ! isMatch) 
		{
		    CegoField *pKF = kfl.First();
		    while ( pKF )
		    {
			isMatch = true;
			CegoField *pD = fvl.Find(*pKF);
			if ( pD )
			{
			    if ( ! ( (CegoFieldValue)pD->getValue() == (CegoFieldValue)pKF->getValue()) )
			    {
				isMatch=false;
			    }
			}
			pKF = kfl.Next();
		    }
		    moreTuple = tc.getNext(kfl, dp);
		}
		if ( isMatch )
		{
		    CegoField *pNF = nfvl.Find(*pF);
		    if ( pNF )
		    {
			if ( ! ( (CegoFieldValue)pNF->getValue() == (CegoFieldValue)pF->getValue()) )
			    return false;
		    }				
		}
	    }
	}
	pKey = keyList.Next();
    }
    return true;
}



bool CegoTableManager::checkIndexIntegrity(const ListT<CegoTableObject>& idxList, int tabSetId, const CegoDataPointer& dp, const ListT<CegoField>& fvl, const ListT<CegoField>& nfvl)
{

    CegoTableObject* pOE = idxList.First();
    
    while (pOE)
    {
	if ( pOE->getType() == CegoObject::UINDEX 
	     || pOE->getType() == CegoObject::PINDEX )
	{
	    
	    CegoFieldValue fv = getIndexSearchValue(pOE->getSchema(), nfvl);
	    
	    CegoField *pF = pOE->getSchema().First();
	    
	    CegoAttrCond ac;
	    ac.add(CegoAttrComp( pF->getAttrName(), EQUAL, fv));
	    
	    CegoAVLIndexCursor ic(this, tabSetId, pOE->getName(), pOE->getType(), &ac, false, true);
	    
	    CegoDataPointer ndp;
	    bool attrMatch = ic.getFirst(pOE->getSchema(), ndp);
	    
	    while ( attrMatch )
	    {
		
		CegoField *pIF = pOE->getSchema().First();
		while ( pIF && attrMatch )
		{

		    CegoField *pNF = nfvl.Find(*pIF);
		    
		    if ( pNF )
		    {

			if ( pNF->getValue() != pIF->getValue() )
			    attrMatch = false;
			else
			    pIF = pOE->getSchema().Next();
		    }
		    else
		    {
			pIF = pOE->getSchema().Next();
		    }			
		}
		
		if ( ndp != dp && attrMatch )
		{
		    ic.abort();
		    return false;
		}		
		
		attrMatch = ic.getNext(pOE->getSchema(), ndp);
	    }
	}
	pOE = idxList.Next();
    }
    return true;
}


bool CegoTableManager::checkBTreeIntegrity(const ListT<CegoBTreeObject>& btreeList, int tabSetId, const CegoDataPointer& dp, const ListT<CegoField>& fvl, const ListT<CegoField>& nfvl)
{

    CegoBTreeObject* pOE = btreeList.First();
    
    while (pOE)
    {
	if ( pOE->getType() == CegoObject::UBTREE 
	     || pOE->getType() == CegoObject::PBTREE )
	{
	    
	    CegoFieldValue fv = getIndexSearchValue(pOE->getSchema(), nfvl);
	    
	    CegoField *pF = pOE->getSchema().First();
	    
	    CegoAttrCond ac;
	    ac.add(CegoAttrComp( pF->getAttrName(), EQUAL, fv));
	    

	    CegoBTreeCursor btc(this, tabSetId, pOE->getName(), pOE->getType(), &ac, false, true);
	    
	    CegoDataPointer ndp;
	    bool attrMatch = btc.getFirst(pOE->getSchema(), ndp);
	    
	    while ( attrMatch )
	    {
		
		CegoField *pIF = pOE->getSchema().First();
		while ( pIF && attrMatch )
		{

		    CegoField *pNF = nfvl.Find(*pIF);
		    
		    if ( pNF )
		    {

			if ( pNF->getValue() != pIF->getValue() )
			    attrMatch = false;
			else
			    pIF = pOE->getSchema().Next();
		    }
		    else
		    {
			pIF = pOE->getSchema().Next();
		    }			
		}
		
		if ( ndp != dp && attrMatch )
		{
		    btc.abort();
		    return false;
		}		
		
		attrMatch = btc.getNext(pOE->getSchema(), ndp);
	    }
	    
	}
	pOE = btreeList.Next();
    }
    return true;
}


void CegoTableManager::checkTypes(const ListT<CegoField>& fl, const ListT<CegoField>& nfl)
{
    
    CegoField *pF = fl.First();
    while ( pF )
    {
	CegoField *pD = nfl.Find(*pF);
	if ( pD )
	{
	    if ( pD->getType() != pF->getType() )
	    {
		throw Exception(EXLOC, Chain("Mismatched data type <")
				+ CEGO_TYPE_MAP[(int)(pD->getType())] 
				+ Chain("> for attribute ") + pF->getAttrName() 
				+ Chain(", expected type <")
				+ CEGO_TYPE_MAP[(int)(pF->getType())]
				+ Chain(">")); 

	    }
	}
	pF = fl.Next();
    }
}

const CegoFieldValue& CegoTableManager::getIndexSearchValue(const ListT<CegoField>& idxSchema, const ListT<CegoField>& nfvl)
{
    
    CegoField *pSF = idxSchema.First();

    CegoField *pVF = nfvl.First();
    while ( pVF )
    {
	if ( (Chain)pSF->getAttrName() == (Chain)pVF->getAttrName() ) 
	    return pVF->getValue();
	pVF = nfvl.Next();
    }
    
    throw Exception(EXLOC, Chain("No index value found"));

}

long CegoTableManager::deleteDataTable(CegoTableObject& oe, CegoPredDesc* pPred, CegoProcBlock* pBlock)
{

    long delCount = 0;

    int tid = getTID(oe.getTabSetId());

    if ( tid != 0 ) 
	_tastep[oe.getTabSetId()]++;

    ListT<CegoTableObject> idxList;
    ListT<CegoBTreeObject> btreeList;
    ListT<CegoKeyObject> keyList;
    ListT<CegoCheckObject> checkList;
    
    getObjectListByTable(oe.getTabSetId(), oe.getTabName(), idxList, btreeList, keyList, checkList);

    // make log entry first
    
    CegoLogRecord lr;
    lr.setObjectInfo(oe.getName(), oe.getType());
    lr.setAction(CegoLogRecord::LOGREC_DELETE);

    char *pBuf = 0;
    int buflen = 0;

    _qh.encodeDelRec(oe.getTabAlias(), pPred, pBlock, pBuf, buflen);

    lr.setData(pBuf);
    lr.setDataLen(buflen);
    lr.setTID(tid);
    
    logIt(oe.getTabSetId(), lr);
    
    if ( buflen > 0 )
	free ( pBuf );


    if (pPred == 0)
    {
	
	CegoTableCursor* pTC = new CegoTableCursor(this, oe.getTabSetId(), oe.getTabName(), true);
	
	try
	{
	    bool moreTuple;
	    
	    CegoDataPointer dp;
	    ListT<CegoField> fl = oe.getSchema();
	    
	    moreTuple = pTC->getFirst(fl, dp);
	    
	    while (moreTuple && _isAborted == false )
	    {	    
		ListT<CegoField> nfl = oe.getSchema();	    
		CegoDataPointer ndp;
		moreTuple = pTC->getNext(nfl, ndp);
		
		deleteDataTable(oe.getTabSetId(), oe.getTabName(), CegoObject::TABLE, dp, fl, idxList, btreeList, keyList);		  	    
		delCount++;
		
		if ( moreTuple )
		{
		    dp = ndp;
		    fl = nfl;
		}	    
	    }
	    
	    delete pTC;

	}
	catch ( Exception e)
	{
	    delete pTC;
	    throw e; 
	}
    } 
    else
    {
	
	if ( oe.getTabName() != oe.getTabAlias() )
	{
	    // set up the defined alias for attribute evaluation
	    CegoField *pF = oe.getSchema().First();
	    while (pF)
	    {
		pF->setTableAlias(oe.getTabAlias());
		// pF->setTableName(oe.getTabAlias());
		pF = oe.getSchema().Next();
	    }
	}

	ListT<CegoField> fl = oe.getSchema();
	
	ListT<CegoSelect*> queryList;
	pPred->getSelectQueryList(queryList);
	CegoSelect **pSelect = queryList.First();
	while ( pSelect )
	{
	    (*pSelect)->setParentJoinBuf(&fl, 1);
	    pSelect = queryList.Next();
	}
	pPred->analyzeSelect();

	CegoAttrCond ac;
	CegoQueryHelper::AttrCondMatch m = _qh.checkAttrCond(ac, pPred, fl, &fl, 1, pBlock);
		
	if ( m == CegoQueryHelper::INAPP ) 
	{
	    
	    CegoTableCursor* pTC = new CegoTableCursor(this, oe.getTabSetId(), oe.getTabName(), true);
	    
	    try
	    {	     
		ListT<CegoField> fl = (ListT<CegoField>)oe.getSchema();

		CegoDataPointer dp;
		bool moreTuple;		 		
		moreTuple = pTC->getFirst(fl, dp);		
		while (moreTuple && _isAborted == false)
		{
		    
		    pPred->clearAttrCache();
		    if (_qh.evalPredicate(0, 0, 0, &fl, 0, 1, pPred, pBlock))
		    {
			
			if ( tid == 0 )
			{
			    // before deleting, we first have to skip to next valid tuple
			    ListT<CegoField> nfl = oe.getSchema();	    
			    CegoDataPointer ndp;
			    moreTuple = pTC->getNext(nfl, ndp);
			    
			    deleteDataTable(oe.getTabSetId(), oe.getTabName(), CegoObject::TABLE, dp, fl, idxList, btreeList, keyList);		  	    
			    delCount++;
			    
			    if ( moreTuple )
			    {
				dp = ndp;
				fl = nfl;
			    }			
			}
			else
			{
			    deleteDataTable(oe.getTabSetId(), oe.getTabName(), CegoObject::TABLE, dp, fl, idxList, btreeList, keyList);		  
			    delCount++;
			    moreTuple = pTC->getNext(fl, dp);
			}
		    }
		    else
		    {
			moreTuple = pTC->getNext(fl, dp);   
		    }
		}
		
		delete pTC;
	    }
	    catch ( Exception e )
	    {
		delete pTC;
		throw e;		
	    }	    
	}
	else
	{
	    	    
	    CegoTableCursor *pTC = new CegoTableCursor(this, oe.getTabSetId(), oe.getTabName(), true);

	    try
	    {	     

		pTC->setup(ac);

		ListT<CegoField> fl = (ListT<CegoField>)oe.getSchema();

		bool moreTuple;
		
		CegoDataPointer dp;
		
		moreTuple = pTC->getFirst(fl, dp);
		
		while (moreTuple && _isAborted == false)
		{
		    pPred->clearAttrCache();
		    if ( _qh.evalPredicate(0, 0, 0, &fl, 0, 1, pPred, pBlock))
		    {	
			
			if ( tid == 0 )
			{
			    pTC->abort();	
			 
			    deleteDataTable(oe.getTabSetId(), oe.getTabName(), CegoObject::TABLE, dp, fl, idxList, btreeList, keyList);
			    delCount++;
			    
			    moreTuple = pTC->getFirst(fl, dp);
			}
			else
			{		    
			    deleteDataTable(oe.getTabSetId(), oe.getTabName(), CegoObject::TABLE, dp, fl, idxList, btreeList, keyList);		  
			    delCount++;
			    moreTuple = pTC->getNext(fl, dp);
			}		    
		    }    
		    else
		    {
			moreTuple = pTC->getNext(fl, dp);   
		    }
		    
		}
		delete pTC;
	    }
	    catch ( Exception e )
	    {
		delete pTC;
		throw e;		
	    }	    
	}
    }

    if ( _isAborted )
    {	    
	throw Exception(EXLOC, Chain("Delete aborted by user"));
    }	

#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Delete finished, delCount = ") + Chain(delCount));
#endif

    // commit delete record

    CegoLogRecord lrc;
    lrc.setObjectInfo(oe.getName(), oe.getType());
    lrc.setAction(CegoLogRecord::LOGREC_DELETE_COMMIT);

    lrc.setData(pBuf);
    lrc.setDataLen(buflen);
    lrc.setTID(tid);
    
    logIt(oe.getTabSetId(), lrc);

    return delCount;

}

void CegoTableManager::deleteDataTable(int tabSetId, const Chain& tableName, CegoObject::ObjectType type, 
				       const CegoDataPointer& dp, const ListT<CegoField>& fvl, 
				       const ListT<CegoTableObject>& idxList,
				       const ListT<CegoBTreeObject>& btreeList,
				       const ListT<CegoKeyObject>& keyList, 
				       bool doCheckKey, bool doIgnoreIndexError)
{

    if ( doCheckKey )
	if ( keyReferenceExists(tabSetId, tableName, fvl, keyList) )
	    throw Exception(EXLOC, Chain("Constraint violation on foreign key"));
   
    // we just delete entries on committed tuples
    if ( getTID(tabSetId) == 0 )
    {

	// delete index entries
	
	CegoTableObject* pOE = idxList.First();
	while (pOE)
	{
	    if (pOE->getType() == CegoObject::INDEX 
		|| pOE->getType() == CegoObject::UINDEX
		|| pOE->getType() == CegoObject::PINDEX )
		
	    {
		
		char p[TABMNG_MAXINDEXVALUE];
		int len = TABMNG_MAXINDEXVALUE;
		int idxLen;
		
		extractIndexValue(fvl, pOE->getSchema(), p, len, idxLen);

		try
		{
		    CegoAVLIndexManager idxMng(this);
		    idxMng.deleteIndexTable(tabSetId, tableName, type, pOE->getName(), pOE->getType(), pOE->getSchema(), dp, p, idxLen, true);		    
		}
		catch ( Exception e )
		{
		    if ( doIgnoreIndexError == false )
			throw e;  
		}
	    }
	    pOE = idxList.Next();
	}


	// delete btree entries
	
	CegoBTreeObject* pBTO = btreeList.First();
	while (pBTO)
	{
	    if (pBTO->getType() == CegoObject::BTREE
		|| pBTO->getType() == CegoObject::UBTREE
		|| pBTO->getType() == CegoObject::PBTREE )
		
	    {
		
		char p[TABMNG_MAXINDEXVALUE];
		int len = TABMNG_MAXINDEXVALUE;
		int idxLen;
		
		CegoBTreeValue btv;
		btv.valueFromSchema(fvl, pBTO->getSchema());
		
		try
		{
		    CegoBTreeManager btreeMng(this, pBTO);
		    btreeMng.deleteBTree(btv, dp);
		}
		catch ( Exception e )
		{
		    if ( doIgnoreIndexError == false )
			throw e;  
		}
	    }
	    pBTO = btreeList.Next();
	}


	
        // delete blob entries
	CegoField *pF = fvl.First();
	while ( pF )
	{
	    if ( pF->getValue().getType() == BLOB_TYPE && pF->getValue().getValue() != 0 )
	    {
		int fileId;
		int pageId;
		memcpy(&fileId, pF->getValue().getValue(), sizeof(int));
		memcpy(&pageId, (void*)((long)pF->getValue().getValue() + sizeof(int)), sizeof(int));
		
		releaseBlob(tabSetId, fileId, pageId);
	    }
	    pF = fvl.Next();
	}


	int numTries=0;
	bool isDeleted=false;
	while ( isDeleted == false )
	{
	    try
	    {
		deleteData(type, tabSetId, dp);
		isDeleted=true;
	    }
	    catch ( Exception e )
	    {
		Chain msg;
		e.pop(msg);
		_pDBMng->log(_modId, Logger::NOTICE, Chain("Retry delete after :  ") + msg);
		
		if ( numTries == LCKMNG_NUMLOCKTRIES )
		{
		    throw Exception(EXLOC, msg);
		}
		numTries++;
	    }			
	}
	
    }
    else //  getTID(tabSetId) != 0 
    {
	int tid;
	int tastep;
	CegoTupleState ts;

	_pTM->newRBEntry(tabSetId, getTID(tabSetId), dp.getFileId(), dp.getPageId(), dp.getOffset(), tableName);

	getTupleInfo(tabSetId, dp, tid, tastep, ts);
	if ( ts == INSERTED )
	    setTupleInfo(tabSetId, dp, getTID(tabSetId), getTAStep(tabSetId), OBSOLETE);
	else 
	    setTupleInfo(tabSetId, dp, getTID(tabSetId), getTAStep(tabSetId), DELETED);
    }
}

void CegoTableManager::dropTable(int tabSetId, 
				 const Chain& tableName, 
				 CegoObject::ObjectType type,
				 ListT<CegoTableObject>& idxList, 
				 ListT<CegoBTreeObject>& btreeList, 
				 ListT<CegoKeyObject>& keyList, 
				 ListT<CegoCheckObject>& checkList)
{
#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Start of call dropTable(") + Chain(tabSetId)
		 + Chain(",") + tableName
		 + Chain(",") + Chain(type) + Chain(")"));
		
#endif
 
    getObjectListByTable(tabSetId, tableName, idxList, btreeList, keyList, checkList, false);

    CegoTableObject *pIO = idxList.First();
    while ( pIO )
    {
	if ( pIO->getType() == CegoObject::INDEX 
	     || pIO->getType() == CegoObject::UINDEX
	     || pIO->getType() == CegoObject::PINDEX )
	{

#ifdef CGDEBUG	
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Dropping index ") + pIO->getName() + Chain(" ..."));
#endif

	    removeObject(tabSetId, pIO->getName(), pIO->getType());

	    // create log entry
	    CegoLogRecord lr;
	    lr.setObjectInfo(pIO->getName(), pIO->getType());
	    lr.setAction(CegoLogRecord::LOGREC_DROP);    
	    lr.setData(0);
	    lr.setDataLen(0);
	    // lr.setTID(0);
	    logIt(tabSetId, lr);
	    
	}
	pIO = idxList.Next();
    }

    CegoBTreeObject *pBTO = btreeList.First();
    while ( pBTO )
    {
	if ( pBTO->getType() == CegoObject::BTREE 
	     || pBTO->getType() == CegoObject::UBTREE
	     || pBTO->getType() == CegoObject::PBTREE )
	{

#ifdef CGDEBUG	
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Dropping btree ") + pBTO->getName() + Chain(" ..."));
#endif

	    removeObject(tabSetId, pBTO->getName(), pBTO->getType());

	    // create log entry
	    CegoLogRecord lr;
	    lr.setObjectInfo(pBTO->getName(), pBTO->getType());
	    lr.setAction(CegoLogRecord::LOGREC_DROP);    
	    lr.setData(0);
	    lr.setDataLen(0);
	    // lr.setTID(0);
	    logIt(tabSetId, lr);
	    
	}
	pBTO = btreeList.Next();
    }


    CegoKeyObject *pKO = keyList.First();
    while ( pKO )
    {
#ifdef CGDEBUG	
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Dropping key ") + pKO->getName() + Chain(" ..."));
#endif

	removeObject(tabSetId, pKO->getName(), pKO->getType());

	// create log entry
	CegoLogRecord lr;
	lr.setObjectInfo(pKO->getName(), pKO->getType());
	lr.setAction(CegoLogRecord::LOGREC_DROP);    
	lr.setData(0);
	lr.setDataLen(0);
	// lr.setTID(0);
	logIt(tabSetId, lr);

	pKO = keyList.Next();
    }

    CegoCheckObject *pCO = checkList.First();
    while ( pCO )
    {
#ifdef CGDEBUG	
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Dropping check ") + pCO->getName() + Chain(" ..."));
#endif

	removeObject(tabSetId, pCO->getName(), pCO->getType());

	// create log entry
	CegoLogRecord lr;
	lr.setObjectInfo(pCO->getName(), pCO->getType());
	lr.setAction(CegoLogRecord::LOGREC_DROP);    
	lr.setData(0);
	lr.setDataLen(0);
	// lr.setTID(0);
	logIt(tabSetId, lr);

	pCO = checkList.Next();
    }

    // check for blobs and remove 

    CegoTableObject oe;
    getObject(tabSetId, tableName, CegoObject::TABLE, oe);
    ListT<CegoField> fl = oe.getSchema();
    
    bool hasBlobField = false;
    CegoField *pF = fl.First();
    while ( pF )
    {
	if ( pF->getType() == BLOB_TYPE )
	    hasBlobField = true;
	pF = fl.Next();
    }

    if ( hasBlobField )
    {
	
	CegoTableCursor* pTC = new CegoTableCursor(this, tabSetId, tableName, true);

	try
	{

	    bool moreTuple;
	    CegoDataPointer dp;
	    moreTuple = pTC->getFirst(fl, dp);
	    
	    while (moreTuple)
	    {
		
		CegoField *pF = fl.First();
		while ( pF )
		{
		    if ( pF->getType() == BLOB_TYPE )
		    {
			int fileId;
			int pageId;
			memcpy(&fileId, pF->getValue().getValue(), sizeof(int));
			memcpy(&pageId, (void*)((long)pF->getValue().getValue() + sizeof(int)), sizeof(int));
			
#ifdef CGDEBUG	
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Releasing blob [") + Chain(fileId) + Chain(",") + Chain(pageId) + Chain("]"));
#endif

			releaseBlob(tabSetId, fileId, pageId);
		    }   
		    pF = fl.Next();
		}
		moreTuple = pTC->getNext(fl, dp);	    
	    }
	    delete pTC;
	}
	catch ( Exception e )
	{
	    delete pTC;
	    throw e;	    
	}
    }

#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Dropping table ") + tableName + Chain(" ..."));
#endif

    removeObject(tabSetId, tableName, CegoObject::TABLE);
    
    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(tableName, type);
    lr.setAction(CegoLogRecord::LOGREC_DROP);    
    lr.setData(0);
    lr.setDataLen(0);
    // lr.setTID(0);

    logIt(tabSetId, lr);

#ifdef CGDEBUG	
    _pDBMng->log(_modId, Logger::DEBUG, Chain("End of call dropTable"));   
#endif

}

void CegoTableManager::dropIndex(int tabSetId, const Chain& idxName)
{
    CegoObject::ObjectType idxType;

    if ( objectExists(tabSetId, idxName, CegoObject::INDEX) )
	idxType = CegoObject::INDEX;
    else if ( objectExists(tabSetId, idxName, CegoObject::UINDEX) )
	idxType = CegoObject::UINDEX;
    else if ( objectExists(tabSetId, idxName, CegoObject::PINDEX) )
	idxType = CegoObject::PINDEX;
    else
    {
	Chain msg = "Unknown index " + idxName; 
	throw Exception(EXLOC, msg);
    }

    removeObject(tabSetId, idxName, idxType);

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(idxName, idxType);
    lr.setAction(CegoLogRecord::LOGREC_DROP);    
    lr.setData(0);
    lr.setDataLen(0);
    // lr.setTID(0);


    logIt(tabSetId, lr);
    
}


void CegoTableManager::dropBTree(int tabSetId, const Chain& btreeName)
{
    CegoObject::ObjectType btType;

    if ( objectExists(tabSetId, btreeName, CegoObject::BTREE) )
	btType = CegoObject::BTREE;
    else if ( objectExists(tabSetId, btreeName, CegoObject::UBTREE) )
	btType = CegoObject::UBTREE;
    else if ( objectExists(tabSetId, btreeName, CegoObject::PBTREE) )
	btType = CegoObject::PBTREE;
    else
    {
	Chain msg = "Unknown btree " + btreeName; 
	throw Exception(EXLOC, msg);
    }

    removeObject(tabSetId, btreeName, btType);

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(btreeName, btType);
    lr.setAction(CegoLogRecord::LOGREC_DROP);    
    lr.setData(0);
    lr.setDataLen(0);
    // lr.setTID(0);


    logIt(tabSetId, lr);
    
}



void CegoTableManager::dropView(int tabSetId, const Chain& viewName)
{

    removeObject(tabSetId, viewName, CegoObject::VIEW);

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(viewName, CegoObject::VIEW);
    lr.setAction(CegoLogRecord::LOGREC_DROP);    
    lr.setData(0);
    lr.setDataLen(0);
    // lr.setTID(0);

    logIt(tabSetId, lr);
}

void CegoTableManager::dropProcedure(int tabSetId, const Chain& procName)
{
    removeObject(tabSetId, procName, CegoObject::PROCEDURE);

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(procName, CegoObject::PROCEDURE);
    lr.setAction(CegoLogRecord::LOGREC_DROP);    
    lr.setData(0);
    lr.setDataLen(0);
    // lr.setTID(0);
    logIt(tabSetId, lr);
}

void CegoTableManager::dropFKey(int tabSetId, const Chain& fkey)
{
    removeObject(tabSetId, fkey, CegoObject::FKEY);

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(fkey, CegoObject::FKEY);
    lr.setAction(CegoLogRecord::LOGREC_DROP);    
    lr.setData(0);
    lr.setDataLen(0);
    // lr.setTID(0);
    logIt(tabSetId, lr);
}


void CegoTableManager::dropCheck(int tabSetId, const Chain& check)
{
    removeObject(tabSetId, check, CegoObject::CHECK);

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(check, CegoObject::CHECK);
    lr.setAction(CegoLogRecord::LOGREC_DROP);    
    lr.setData(0);
    lr.setDataLen(0);
    // lr.setTID(0);
    logIt(tabSetId, lr);
}


bool CegoTableManager::getFirstTuple(CegoObjectCursor* pC, ListT<CegoField>& fl, CegoDataPointer& dp)
{

    int len;
    char* pc = (char*)pC->getFirst(len, dp);

    if ( pc && len > 0 )
    {

	int tid;
	int tastep; 
	CegoTupleState ts;
	memcpy(&tid, pc, sizeof(int));

	char* pctastep = pc + sizeof(int);
	memcpy(&tastep, pctastep, sizeof(int));

	char* pcts = pc + sizeof(int) + sizeof(int);;
	memcpy(&ts, pcts, sizeof(CegoTupleState));
	
	if ( tid != 0 ) 
	{
	    
	    if  ( ( ts == INSERTED && tid == getTID(pC->getTabSetId()) )
		  || ( ts == DELETED && tid != getTID(pC->getTabSetId()) ) )
	    {	
		CegoTupleState ts;
		_qh.decodeFVL(fl, pc, len, tid, tastep, ts);
	    }
	    else 
	    {
		return getNextTuple(pC, fl, dp);		
	    }	       
	}
	else
	{
	    CegoTupleState ts;
	    _qh.decodeFVL(fl, pc, len, tid, tastep, ts);
	}

	return true;
    }
    return false;
}

bool CegoTableManager::getNextTuple(CegoObjectCursor* pC, ListT<CegoField>& fl, CegoDataPointer& dp)
{
    
    int tid = 0;

    do {
	
	int len;
	char* pc = (char*)pC->getNext(len, dp);
	
	if ( pc && len > 0 )
	{
	    
	    int tid;
	    int tastep;

	    CegoTupleState ts;
	    memcpy(&tid, pc, sizeof(int));

	    char* pctastep = pc + sizeof(int);
	    memcpy(&tastep, pctastep, sizeof(int));

	    char* pcts = pc + sizeof(int) + sizeof(int);
	    memcpy(&ts, pcts, sizeof(CegoTupleState));
	    
	    if ( tid != 0 ) 
	    {
		if ( ( ts == INSERTED && tid == getTID(pC->getTabSetId()) )
		     || ( ts == DELETED && tid == getTID(pC->getTabSetId()) ) )
		{	
		    CegoTupleState ts;
		    _qh.decodeFVL(fl, pc, len, tid, tastep, ts);
		    return true;
		}
	    }
	    else
	    {
		CegoTupleState ts;
		_qh.decodeFVL(fl, pc, len, tid, tastep, ts);
		return true;
	    }
	}
	else
	{
	    return false;
	}
    
    }  while ( 1 ) ;
}

void CegoTableManager::createPrimaryIndexTable(int tabSetId, const Chain& indexName, const Chain& tableName, ListT<CegoField>& schema)
{
    CegoObject::ObjectType type = CegoObject::PINDEX;
    createIndexTable(tabSetId, indexName, tableName, schema, type);
}

void CegoTableManager::createIndexTable(int tabSetId, const Chain& indexName, const Chain& tableName, ListT<CegoField>& schema, CegoObject::ObjectType type)
{
    if ( type == CegoObject::PINDEX
	 || type == CegoObject::UINDEX
	 || type == CegoObject::INDEX )
    {
	createAVLIndexTable(tabSetId, indexName, tableName, schema, type);
    }
    else if ( type == CegoObject::PBTREE
	 || type == CegoObject::UBTREE
	 || type == CegoObject::BTREE )
    {
	createBTree(tabSetId, indexName, tableName, schema, type);
    } 
    else
    {
	throw Exception(EXLOC, Chain("Unknown index type"));
    }
}

void CegoTableManager::createBTree(int tabSetId, const Chain& indexName, const Chain& tableName, ListT<CegoField>& schema, CegoObject::ObjectType type)
{
    CegoTableObject toe;

    getObject(tabSetId, tableName, CegoObject::TABLE, toe);

    CegoField* pF = schema.First();
    while (pF)
    {
	CegoField* pSF = toe.getSchema().Find(CegoField(tableName, pF->getAttrName()));
	    
	if ( pSF )
	{
	    pF->setType(pSF->getType());
	    pF->setLength(pSF->getLength());
	    pF->setId(pSF->getId());		
	}
	else
	{
	    Chain msg = "Unknown field <" + pF->getAttrName()  + ">";
	    throw Exception(EXLOC, msg);	    
	}
	
	if ( type == CegoObject::PBTREE && pSF->isNullable() )
	{
	    Chain msg = "Primary btree attribute must be not nullable";
	    throw Exception(EXLOC, msg);
	}

	pF = schema.Next();
    }

    CegoBTreeObject btoe(tabSetId, type, indexName, schema, tableName);
    
    createBTreeObject(btoe);
	    
    // building up index

    CegoObjectCursor* pC = 0;
    CegoBufferPage bp;

    try {

	pC = getObjectCursor(tabSetId, tableName, tableName, CegoObject::TABLE);
    	
	// we have to keep the index object in buffer pool 
	getObjectWithFix(tabSetId, indexName, type, btoe, bp);
	
	CegoDataPointer dp;
	int tid = 0;
	
	char idxBuf[TABMNG_MAXINDEXVALUE];	    
	
	bool moreTuple = getFirstTuple(pC, schema, dp);
	
	CegoDataPointer sysEntry(bp.getFileId(), bp.getPageId(), bp.getEntryPos());

	// get Index len
	int len = 0;
	CegoField* pF = schema.First();
	while (pF)
	{
	    len += pF->getValue().getLength();
	    pF = schema.Next();
	}

	CegoBTreeManager btreeMng(this, &btoe);

	btreeMng.fixRoot();

	while (moreTuple && _isAborted == false)
	{
	    CegoBTreeValue iv;
	    iv.valueFromSchema(&schema, len);	    

	    // cout << "Inserting " << iv.toChain(&schema) << endl;
	    btreeMng.insertBTree(sysEntry, dp, iv, tid);

	    /*
	    cout << "Dumping btree ..." << endl;
	    cout << "+++++++++++++++++++++++++++++++++++++++++++++" << endl;
	    btreeMng.dumpBTree();
	    cout << "+++++++++++++++++++++++++++++++++++++++++++++" << endl;
	    */
	    

	    moreTuple = getNextTuple(pC, schema, dp);
	}

	btreeMng.unfixRoot();
	
       
	if ( _isAborted )
	{	    
	    throw Exception(EXLOC, Chain("Index creation aborted by user"));
	}	
    }
    catch ( Exception e )
    {

	if  ( bp.isFixed() )
	    _pDBMng->bufferUnfix(bp, true, _pLockHandle);
	if ( pC )
	    pC->abort();

	// _pLockHandle->unlockRecord(recLock);

	delete pC;
	
	// dropIndex(tabSetId, indexName);
	
	Chain msg;
	e.pop(msg);
	throw Exception(EXLOC, Chain("Create of index failed : ") + msg);
    }

    // now we can release the index root page
    _pDBMng->bufferUnfix(bp, true, _pLockHandle);
    // _pLockHandle->unlockRecord(recLock);

    // we also can release the object cursor
    pC->abort();

    delete pC;

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(btoe.getName(), btoe.getType());
    lr.setAction(CegoLogRecord::LOGREC_CREATE);
    
    char *buf;
    buf = (char*)malloc(btoe.getEntrySize());

    btoe.encode(buf);
    lr.setData(buf);
    lr.setDataLen(btoe.getEntrySize());
    // lr.setTID(0);
    logIt(btoe.getTabSetId(), lr);

    free(buf);
    

}


void CegoTableManager::createAVLIndexTable(int tabSetId, const Chain& indexName, const Chain& tableName, ListT<CegoField>& schema, CegoObject::ObjectType type)
{
    CegoTableObject toe;

    getObject(tabSetId, tableName, CegoObject::TABLE, toe);

    CegoField* pF = schema.First();
    while (pF)
    {
	CegoField* pSF = toe.getSchema().Find(CegoField(tableName, pF->getAttrName()));
	    
	if ( pSF )
	{
	    pF->setType(pSF->getType());
	    pF->setLength(pSF->getLength());
	    pF->setId(pSF->getId());		
	}
	else
	{
	    Chain msg = "Unknown field <" + pF->getAttrName()  + ">";
	    throw Exception(EXLOC, msg);	    
	}
	
	if ( type == CegoObject::PINDEX && pSF->isNullable() )
	{
	    Chain msg = "Primary index attribute must be not nullable";
	    throw Exception(EXLOC, msg);
	}

	pF = schema.Next();
    }

    CegoTableObject ioe(tabSetId, type, indexName, schema, tableName);
    
    createTableObject(ioe);
	
    CegoAVLIndexEntry base;
    CegoDataPointer nil;
    
    base.initEntry(nil);
    
    base.setLeftBranch(nil);
    base.setParent(nil);
    base.setRightBranch(nil);
    
    int len;

    CegoDataPointer dp = insertData(ioe, (char*)base.getPtr(), base.getLen(), true);

    CegoBufferPage zp;
    unsigned long recLock = _pLockHandle->lockRecord(zp, dp, CegoLockHandler::WRITE);
    
    // building up index

    CegoObjectCursor* pC = 0;
    CegoBufferPage bp;

    CegoAVLIndexManager idxMng(this);

    try {

	pC = getObjectCursor(tabSetId, tableName, tableName, CegoObject::TABLE);
    	
	// we have to keep the index object in buffer pool 
	getObjectWithFix(tabSetId, indexName, type, ioe, bp);
	
	CegoDataPointer dp;
	int tid = 0;
	
	char idxBuf[TABMNG_MAXINDEXVALUE];	    
	
	bool moreTuple = getFirstTuple(pC, schema, dp);
	
	bool isFirst = true;
	CegoDataPointer ritp;

	CegoDataPointer sysEntry(bp.getFileId(), bp.getPageId(), bp.getEntryPos());

	while (moreTuple && _isAborted == false)
	{

	    // get Index len
	    int idxLen = 0;
	    CegoField* pF = schema.First();
	    while (pF)
	    {
		idxLen += pF->getValue().getLength() + sizeof(int);
		pF = schema.Next();
	    }
	    	    
	    char* idxPtr = idxBuf;
	    pF = schema.First();
	    while (pF)
	    {
		int len = pF->getValue().getLength();
		memcpy(idxPtr, &len, sizeof(int));
		idxPtr+=sizeof(int);
		if ( len > 0 )
		{
		    memcpy(idxPtr, pF->getValue().getValue(), len);	   
		    idxPtr += len;
		}
		pF = schema.Next();
	    }

	    if ( isFirst )
	    {
		// since we have locked the index root page in bufferpool, we can call with no resolution of object entry
		idxMng.insertNativeIndexTable(ioe, sysEntry, dp, idxBuf, idxLen, tid, true, ritp);
		isFirst=false;
	    }
	    else
	    {
		
		bool isUnique = false;
		if ( type == CegoObject::UINDEX || type == CegoObject::PINDEX )
		    isUnique = true;
		
		idxMng.insertIndexTable(ioe, sysEntry, ritp, isUnique, dp, idxBuf, idxLen, tid, true, true);
	    }
	    
	    moreTuple = getNextTuple(pC, schema, dp);
	}    

	if ( _isAborted )
	{	    
	    throw Exception(EXLOC, Chain("Index creation aborted by user"));
	}	
    }
    catch ( Exception e )
    {

	if  ( bp.isFixed() )
	    _pDBMng->bufferUnfix(bp, true, _pLockHandle);
	if ( pC )
	    pC->abort();

	_pLockHandle->unlockRecord(recLock);

	delete pC;
	dropIndex(tabSetId, indexName);
	
	Chain msg;
	e.pop(msg);
	throw Exception(EXLOC, Chain("Create of index failed : ") + msg);
    }

    // now we can release the index root page
    _pDBMng->bufferUnfix(bp, true, _pLockHandle);
    _pLockHandle->unlockRecord(recLock);

    // we also can release the object cursor
    pC->abort();

    delete pC;

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(ioe.getName(), ioe.getType());
    lr.setAction(CegoLogRecord::LOGREC_CREATE);
    
    char *buf;
    buf = (char*)malloc(ioe.getEntrySize());

    ioe.encode(buf);
    lr.setData(buf);
    lr.setDataLen(ioe.getEntrySize());
    // lr.setTID(0);
    logIt(ioe.getTabSetId(), lr);

    free(buf);

}

void CegoTableManager::createForeignKey(int tabSetId, const Chain& fkey, const Chain& tableName, const ListT<CegoField>& keyList, const Chain& refTable, const ListT<CegoField>& refList)
{
        		
    CegoObjectCursor* pC = 0;
    
    try {
	

	// check if key references primary key
	
	ListT<CegoTableObject> checkIdxList;
	ListT<CegoBTreeObject> checkBTreeList;
	ListT<CegoKeyObject> checkKeyList;
	ListT<CegoCheckObject> checkList;
	
	getObjectListByTable(tabSetId, refTable, checkIdxList, checkBTreeList, checkKeyList, checkList);
    
	CegoTableObject *pIO = checkIdxList.First();
	bool idxFound = false;
	while ( pIO && idxFound == false)
	{
	    if ( pIO->getType() == CegoObject::PINDEX )
	    {
		if ( pIO->getSchema().Size() == keyList.Size() )
		{
		    
		    CegoField *pK = refList.First();
		    idxFound = true;
		    while ( pK && idxFound )
		    {
			if ( pIO->getSchema().Find(*pK) )
			{
			    pK = refList.Next();			
			}
			else
			{
			    idxFound = false;
			}		    
		    }
		}
		// just one primary index can be found
		pIO = 0;
	    }
	    else
	    {
		pIO = checkIdxList.Next();
	    }
	}
	if ( idxFound == false )
	{
	    CegoBTreeObject *pBTO = checkBTreeList.First();
	    while ( pBTO && idxFound == false)
	    {
		if ( pBTO->getType() == CegoObject::PBTREE )
		{
		    if ( pBTO->getSchema().Size() == keyList.Size() )
		    {
		    
			CegoField *pK = refList.First();
			idxFound = true;
			while ( pK && idxFound )
			{
			    if ( pBTO->getSchema().Find(*pK) )
			    {
				pK = refList.Next();			
			    }
			    else
			    {
				idxFound = false;
			    }		    
			}
		    }
		    
		    // just one primary btree can be found
		    pBTO = 0;
		}
		else
		{
		    pBTO = checkBTreeList.Next();
		}
	    }
	}
	
	if ( idxFound == false )
	    throw Exception(EXLOC, Chain("Primary index or btree not found"));
	  
	CegoTableObject toe;
	getObject(tabSetId, tableName, CegoObject::TABLE, toe);

	ListT<CegoField> schema = toe.getSchema();	
	
	pC = getObjectCursor(tabSetId, tableName, tableName, CegoObject::TABLE);
	
	CegoDataPointer dp;

	bool moreTuple = getFirstTuple(pC, schema, dp);
	
	while ( moreTuple && _isAborted == false)
	{	    
	    CegoTableCursor tc(this, tabSetId, refTable);

	    CegoAttrCond ac;
	    
	    CegoField *pDF = keyList.First();
	    while ( pDF )
	    {
		CegoField *pF = schema.Find(*pDF);
		if ( pF )
		{	   
		    ac.add(CegoAttrComp(pF->getAttrName(), EQUAL, pF->getValue())); 
		}
		else
		{
		    throw Exception(EXLOC, Chain("Unknown key attribute"));
		}
		pDF = keyList.Next();
	    }
		
	    ListT<CegoField> fl;
			    
	    if ( tc.setup(ac) == false )
	    {
		CegoTableObject oe;
		getObject(tabSetId, refTable, CegoObject::TABLE, oe);
		fl = oe.getSchema();
	    }
	    else
	    {
		tc.getIdxSchema(fl);
	    }
	    
	    CegoDataPointer dp;
		
	    bool moreRefTuple = tc.getFirst(fl, dp);
	    
	    bool isMatch = false;
	    while ( moreRefTuple && ( ! isMatch ) ) 
	    {
		CegoField *pF = fl.First();
		while ( pF )
		{
		    isMatch = true;
		    CegoField *pD = schema.Find(*pF);
		    if ( pD )
		    {			   
			if ( ! ( (CegoFieldValue)pD->getValue() == (CegoFieldValue)pF->getValue()) )
			    isMatch=false;
		    }
		    pF = fl.Next();
		}
		moreRefTuple = tc.getNext(fl, dp);
	    }
	    if ( ! isMatch )
		throw Exception(EXLOC, Chain("Foreign key reference does not exist"));
	    	    
	    moreTuple = getNextTuple(pC, schema, dp);
	}

	if ( _isAborted )
	{	    
	    throw Exception(EXLOC, Chain("Foreign key creation aborted by user"));
	}	
		    
    }
    catch ( Exception e )
    {
	
	if ( pC )
	    pC->abort();
	
	throw e;

    }
    
    delete pC;
    
    CegoKeyObject oe(tabSetId, fkey, tableName, keyList, refTable, refList );

    createKeyObject(oe);

    // create log entry
    CegoLogRecord lr;
    lr.setObjectInfo(oe.getName(), oe.getType());
    lr.setAction(CegoLogRecord::LOGREC_CREATE);
    
    char *buf;
    buf = (char*)malloc(oe.getEntrySize());

    oe.encode(buf);
    lr.setData(buf);
    lr.setDataLen(oe.getEntrySize());
    // lr.setTID(0);
    logIt(oe.getTabSetId(), lr);
    free(buf);
}

void CegoTableManager::createCheck(int tabSetId, const Chain& checkName, const Chain& tableName, CegoPredDesc *pPredDesc)
{

    CegoObjectCursor* pC = 0;
    
    try {
	
	  
	CegoTableObject toe;
	getObject(tabSetId, tableName, CegoObject::TABLE, toe);

	ListT<CegoField> schema = toe.getSchema();	
	
	pC = getObjectCursor(tabSetId, tableName, tableName, CegoObject::TABLE);
	
	CegoDataPointer dp;

	bool moreTuple = getFirstTuple(pC, schema, dp);

	if ( moreTuple )
	{
	    while ( moreTuple && _isAborted == false)
	    {	    
		CegoQueryHelper qh;
		
		pPredDesc->clearAttrCache();
		if ( qh.evalPredicate(0, 0, 0, &schema, 0, 1, pPredDesc ) == false )
		    throw Exception(EXLOC, Chain("Check constraint violated"));
		
	    
		moreTuple = getNextTuple(pC, schema, dp);
	    }
	}
	else
	{
	    // check attribute match for empty tables
	    CegoExpr *pExpr = pPredDesc->getExpr1();
	    if ( pExpr )
	    {
		ListT<CegoAttrDesc*> adl = pExpr->getAttrRefList();
		CegoAttrDesc **pAD = adl.First();
		while ( pAD )
		{
		    if ( ! schema.Find(CegoField((*pAD)->getTableName(), (*pAD)->getAttrName())))
		    {
			Chain msg = Chain("Unknown attribute " ) + (*pAD)->getAttrName();
			throw Exception(EXLOC, msg);
		    }
		    pAD = adl.Next();
		}
	    }
	    pExpr = pPredDesc->getExpr2();
	    if ( pExpr )
	    {
		ListT<CegoAttrDesc*> adl = pExpr->getAttrRefList();
		CegoAttrDesc **pAD = adl.First();
		while ( pAD )
		{
		    if ( ! schema.Find(CegoField((*pAD)->getTableName(), (*pAD)->getAttrName())))
		    {
			Chain msg = Chain("Unknown attribute " ) + (*pAD)->getAttrName();
			throw Exception(EXLOC, msg);
		    }
		    pAD = adl.Next();
		}
	    }
	    pExpr = pPredDesc->getExpr3();
	    if ( pExpr )
	    {
		ListT<CegoAttrDesc*> adl = pExpr->getAttrRefList();
		CegoAttrDesc **pAD = adl.First();
		while ( pAD )
		{
		    if ( ! schema.Find(CegoField((*pAD)->getTableName(), (*pAD)->getAttrName())))
		    {
			Chain msg = Chain("Unknown attribute " ) + (*pAD)->getAttrName();
			throw Exception(EXLOC, msg);
		    }
		    pAD = adl.Next();
		}
	    }
	}
	if ( _isAborted )
	{	    
	    throw Exception(EXLOC, Chain("Check constraint creation aborted by user"));
	}	
		    
    }
    catch ( Exception e )
    {
	
	if ( pC )
	    pC->abort();
	
	throw e;

    }
    
    delete pC;
    
    CegoCheckObject oe(tabSetId, checkName, tableName, pPredDesc );

    createCheckObject(oe);

    // create log entry
    
    CegoLogRecord lr;
    lr.setObjectInfo(oe.getName(), oe.getType());
    lr.setAction(CegoLogRecord::LOGREC_CREATE);
    
    char *buf;
    buf = (char*)malloc(oe.getEntrySize());

    oe.encode(buf);
    lr.setData(buf);
    lr.setDataLen(oe.getEntrySize());
    // lr.setTID(0);
    logIt(oe.getTabSetId(), lr);
    free(buf);    
}

void CegoTableManager::getPoolInfo(CegoTableObject& oe, ListT< ListT<CegoFieldValue> > &info)
{
	
    ListT<CegoField> schema;
    schema.Insert(CegoField(Chain("POOLINFO"), Chain("POOLINFO"), Chain("PARAMETER"),VARCHAR_TYPE, 20)); 
    schema.Insert(CegoField(Chain("POOLINFO"), Chain("POOLINFO"), Chain("VALUE"), VARCHAR_TYPE, 20)); 
    oe = CegoTableObject(0, CegoObject::SYSTEM, Chain("POOLINFO"), schema, Chain("POOLINFO"));
       
    int pageSize;
    long totalPages;
    long usedPages;
    long freePages;
    long dirtyPages;
    long fixedPages;
    long persistentPages;
    long noSyncPages;
    long numDiskRead;
    long numDiskWrite;
    long hitRate;
    long readDelay;
    long writeDelay;
    long curFixCount;
    long maxFixCount;
    int statStart;

    _pDBMng->poolInfo(pageSize, totalPages, usedPages, freePages, dirtyPages, fixedPages, persistentPages, noSyncPages, numDiskRead, numDiskWrite, hitRate, readDelay, writeDelay, curFixCount, maxFixCount, statStart);

    
    CegoFieldValue f1a(VARCHAR_TYPE, Chain("Page Size"));
    CegoFieldValue f1b(VARCHAR_TYPE,  Chain(pageSize));
    ListT<CegoFieldValue> fl1;
    fl1.Insert(f1a);
    fl1.Insert(f1b);
    info.Insert(fl1);
    
    CegoFieldValue f2a(VARCHAR_TYPE, Chain("Total Pages"));
    CegoFieldValue f2b(VARCHAR_TYPE,  Chain(totalPages));
    ListT<CegoFieldValue> fl2;
    fl2.Insert(f2a);
    fl2.Insert(f2b);
    info.Insert(fl2);
    
    CegoFieldValue f3a(VARCHAR_TYPE, Chain("Used Pages"));
    CegoFieldValue f3b(VARCHAR_TYPE, Chain(usedPages));
    ListT<CegoFieldValue> fl3;
    fl3.Insert(f3a);
    fl3.Insert(f3b);
    info.Insert(fl3);
    
    CegoFieldValue f4a(VARCHAR_TYPE, Chain("Free Pages"));
    CegoFieldValue f4b(VARCHAR_TYPE, Chain(freePages));
    ListT<CegoFieldValue> fl4;
    fl4.Insert(f4a);
    fl4.Insert(f4b);
    info.Insert(fl4);
    
    CegoFieldValue f5a(VARCHAR_TYPE, Chain("Dirty Pages"));
    CegoFieldValue f5b(VARCHAR_TYPE, Chain(dirtyPages));
    ListT<CegoFieldValue> fl5;
    fl5.Insert(f5a);
    fl5.Insert(f5b);
    info.Insert(fl5);
    
    CegoFieldValue f6a(VARCHAR_TYPE, Chain("Fixed Pages"));
    CegoFieldValue f6b(VARCHAR_TYPE, Chain(fixedPages));
    ListT<CegoFieldValue> fl6;
    fl6.Insert(f6a);
    fl6.Insert(f6b);
    info.Insert(fl6);
    
    CegoFieldValue f7a(VARCHAR_TYPE, Chain("Persistent Pages"));
    CegoFieldValue f7b(VARCHAR_TYPE, Chain(persistentPages));
    ListT<CegoFieldValue> fl7;
    fl7.Insert(f7a);
    fl7.Insert(f7b);
    info.Insert(fl7);
        
    CegoFieldValue f8a(VARCHAR_TYPE, Chain("No Sync Pages"));
    CegoFieldValue f8b(VARCHAR_TYPE, Chain(noSyncPages));
    ListT<CegoFieldValue> fl8;
    fl8.Insert(f8a);
    fl8.Insert(f8b);
    info.Insert(fl8);

    CegoFieldValue f9a(VARCHAR_TYPE, Chain("--------------------"));
    CegoFieldValue f9b(VARCHAR_TYPE, Chain("--------------------"));
    ListT<CegoFieldValue> fl9;
    fl9.Insert(f9a);
    fl9.Insert(f9b);
    info.Insert(fl9);
    
    Datetime dt(statStart);
    CegoFieldValue f10a(VARCHAR_TYPE, Chain("Stat Start"));
    CegoFieldValue f10b(VARCHAR_TYPE, dt.asChain("%d.%m.%Y %H:%M:%S"));
    ListT<CegoFieldValue> fl10;
    fl10.Insert(f10a);
    fl10.Insert(f10b);
    info.Insert(fl10);
    
    CegoFieldValue f11a(VARCHAR_TYPE, Chain("Cur Fix Count"));
    CegoFieldValue f11b(VARCHAR_TYPE, curFixCount);
    ListT<CegoFieldValue> fl11;
    fl11.Insert(f11a);
    fl11.Insert(f11b);
    info.Insert(fl11);
    
    CegoFieldValue f12a(VARCHAR_TYPE, Chain("Max Fix Count"));
    CegoFieldValue f12b(VARCHAR_TYPE, maxFixCount);
    ListT<CegoFieldValue> fl12;
    fl12.Insert(f12a);
    fl12.Insert(f12b);
    info.Insert(fl12);
    
    CegoFieldValue f13a(VARCHAR_TYPE, Chain("Disk Reads"));
    CegoFieldValue f13b(VARCHAR_TYPE, Chain(numDiskRead));
    ListT<CegoFieldValue> fl13;
    fl13.Insert(f13a);
    fl13.Insert(f13b);
    info.Insert(fl13);
    
    CegoFieldValue f14a(VARCHAR_TYPE, Chain("Disk Writes"));
    CegoFieldValue f14b(VARCHAR_TYPE, Chain(numDiskWrite));
    ListT<CegoFieldValue> fl14;
    fl14.Insert(f14a);
    fl14.Insert(f14b);
    info.Insert(fl14);
    
    int secDelay;
    int msecDelay;
    Chain delayStr;
    Chain fillStr;
    
    secDelay = readDelay / 100;
    msecDelay = readDelay % 100;
    fillStr = Chain("00") + Chain(msecDelay);
    delayStr = Chain(secDelay) + Chain(".") + fillStr.subChain(fillStr.length()-2, fillStr.length()) + Chain (" msec");
    
    CegoFieldValue f15a(VARCHAR_TYPE, Chain("Read Delay"));
    CegoFieldValue f15b(VARCHAR_TYPE, Chain(delayStr));
    ListT<CegoFieldValue> fl15;
    fl15.Insert(f15a);
    fl15.Insert(f15b);
    info.Insert(fl15);
    
    secDelay = writeDelay / 100;
    msecDelay = writeDelay % 100;
    fillStr = Chain("00") + Chain(msecDelay);
    delayStr = Chain(secDelay) + Chain(".") + fillStr.subChain(fillStr.length()-2, fillStr.length()) + Chain (" msec");
    
    CegoFieldValue f16a(VARCHAR_TYPE, Chain("Write Delay"));
    CegoFieldValue f16b(VARCHAR_TYPE, Chain(delayStr));
    ListT<CegoFieldValue> fl16;
    fl16.Insert(f16a);
    fl16.Insert(f16b);
    info.Insert(fl16);
        
}

void CegoTableManager::getSystemInfo(const Chain& tableSet, CegoTableObject& oe, ListT< ListT<CegoFieldValue> > &fa, Chain& format)
{   
     
    int tabSetId = _pDBMng->getTabSetId(tableSet);
    int tmpFid = _pDBMng->getTmpFid(tableSet);
    
    ListT<CegoField> schema;
    schema.Insert(CegoField(Chain("SYSINFO"), Chain("SYSINFO"), Chain("SPACE"), VARCHAR_TYPE, 10));
    schema.Insert(CegoField(Chain("SYSINFO"), Chain("SYSINFO"), Chain("NUMPAGES"), INT_TYPE, sizeof(int)));
    schema.Insert(CegoField(Chain("SYSINFO"), Chain("SYSINFO"), Chain("USEDPAGES"), INT_TYPE, sizeof(int)));

    oe = CegoTableObject(tabSetId, CegoObject::SYSTEM, Chain("SYSINFO"), schema, Chain("SYSINFO"));

    format = Chain("lrr");
    
    ListT<CegoFieldValue> fv1;

    fv1.Insert(CegoFieldValue(VARCHAR_TYPE, Chain("SYSTEM")));
    fv1.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumPages(tabSetId))));
    fv1.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumUsedPages(tabSetId, _pLockHandle))));

    fa.Insert(fv1);


    ListT<Chain> dfList;
    ListT<int> fidList;
    ListT<int> sizeList;

    
    _pDBMng->getDataFileInfo(tableSet, Chain(XML_SYSFILE_VALUE), dfList, fidList, sizeList);

    Chain *pDF = dfList.First();
    int *pFid = fidList.First();

    while ( pFid && pDF)
    {

	ListT<CegoFieldValue> fv;
		
	fv.Insert(CegoFieldValue(VARCHAR_TYPE, Chain("SYSTEM")));

	// fv.Insert(CegoFieldValue(VARCHAR_TYPE, *pDF));

	fv.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumPages(*pFid))));
	fv.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumUsedPages(*pFid, _pLockHandle))));
	
	fa.Insert(fv);
	
	pFid = fidList.Next();
	pDF = dfList.Next();
    }     
    
    ListT<CegoFieldValue> fv2;
    
    fv2.Insert(CegoFieldValue(VARCHAR_TYPE, Chain("TEMP")));
    fv2.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumPages(tmpFid))));
    fv2.Insert(CegoFieldValue(INT_TYPE,  Chain(_pDBMng->getNumUsedPages(tmpFid, _pLockHandle))));

    fa.Insert(fv2);

    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();

    _pDBMng->getDataFileInfo(tableSet, Chain(XML_TEMPFILE_VALUE), dfList, fidList, sizeList);

    pDF = dfList.First();
    pFid = fidList.First();

    while ( pFid && pDF)
    {

	ListT<CegoFieldValue> fv;
		
	fv.Insert(CegoFieldValue(VARCHAR_TYPE, Chain("TEMP")));

	// fv.Insert(CegoFieldValue(VARCHAR_TYPE, *pDF));

	fv.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumPages(*pFid))));
	fv.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumUsedPages(*pFid, _pLockHandle))));
	
	fa.Insert(fv);
	
	pFid = fidList.Next();
	pDF = dfList.Next();
    }     

    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();


    _pDBMng->getDataFileInfo(tableSet, Chain(XML_APPFILE_VALUE), dfList, fidList, sizeList);

    pDF = dfList.First();
    pFid = fidList.First();

    while ( pFid && pDF)
    {

	ListT<CegoFieldValue> fv;
		
	fv.Insert(CegoFieldValue(VARCHAR_TYPE, Chain("DATAFILE")));

	// fv.Insert(CegoFieldValue(VARCHAR_TYPE, *pDF));

	fv.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumPages(*pFid))));
	fv.Insert(CegoFieldValue(INT_TYPE, Chain(_pDBMng->getNumUsedPages(*pFid, _pLockHandle))));
	
	fa.Insert(fv);
	
	pFid = fidList.Next();
	pDF = dfList.Next();
    }     
}


void CegoTableManager::dumpObject(const Chain& tableSet, const Chain& objName, CegoObject::ObjectType type, Chain& chainDump)
{

    int tabSetId = _pDBMng->getTabSetId(tableSet);
    dumpObject(tabSetId, objName, type, chainDump);
}

void CegoTableManager::dumpObject(int tabSetId, const Chain& objName, CegoObject::ObjectType type, Chain& chainDump)
{
    
    chainDump = Chain("Name: <") + objName + Chain(">\n");
    chainDump += Chain("Type: ");

    switch (type)
    {
    case CegoObject::TABLE:
    case CegoObject::INDEX:
    case CegoObject::UINDEX:
    case CegoObject::PINDEX:
    {
	CegoTypeConverter tc;
	chainDump += tc.getObjectTypeString(type) + Chain("\n");

	CegoTableObject oe;
	getObject(tabSetId, objName, type, oe);

	chainDump += Chain("Schema:\n");
	CegoField* pF = oe.getSchema().First();
	while (pF)
	{
	    
	    chainDump += Chain("   TableName: <") + pF->getTableName() + Chain(">\n");
	    chainDump += Chain("   TableAlias: <") + pF->getTableAlias() + Chain(">\n");
	    chainDump += Chain("   AttrName: <") + pF->getAttrName() + Chain(">\n");
	    chainDump += Chain("   Id: <") + Chain(pF->getId()) + Chain(">\n");
	    
	    pF = oe.getSchema().Next();
	}

	CegoObjectCursor* pC = getObjectCursor(tabSetId, oe.getTabName(), objName, type);	

	CegoDataPointer dp;
	int len;
	char* pc = (char*)pC->getFirst(len, dp);

	long rowCount=0;
	while ( pc && len > 0 )
	{
	    rowCount++;

	    int tid;
	    int tastep; 
	    CegoTupleState ts;
	    memcpy(&tid, pc, sizeof(int));
	    
	    char* pctastep = pc + sizeof(int);
	    memcpy(&tastep, pctastep, sizeof(int));
	    
	    char* pcts = pc + sizeof(int) + sizeof(int);;
	    memcpy(&ts, pcts, sizeof(CegoTupleState));

	    chainDump += dp.toChain(); 
	    chainDump += Chain(" Row=") + Chain(rowCount) 
		+ Chain(",Tid=") + Chain(tid)
		+ Chain(",Step=") + Chain(tastep);
	    
	    if ( ts == COMMITTED )
		chainDump += Chain(",State=C");
	    else if ( ts == INSERTED )
		chainDump += Chain(",State=I");
	    else if ( ts == DELETED )
		chainDump += Chain(",State=D");
	    else if ( ts == OBSOLETE )
		chainDump += Chain(",State=O");

	    chainDump += Chain(",Data=");
	    
	    if ( type == CegoObject::TABLE )
	    {
		ListT<CegoField> fl = oe.getSchema();
		_qh.decodeFVL(fl, pc, len, tid, tastep, ts);
		CegoField *pF = fl.First();
		while ( pF )
		{
		    chainDump += Chain("<") + pF->getValue().toChain() + Chain(">");    
		    pF = fl.Next();
		}
		chainDump += Chain("\n");
		
	    }
	    else if ( type == CegoObject::INDEX || type == CegoObject::UINDEX || type == CegoObject::PINDEX )
	    {

		CegoAVLIndexEntry ie;
		ie.setPtr(pc,len);
		
		chainDump += "Entry " + dp.toChain() + ":  Parent=" + ie.getParent().toChain()  + " Left=" + ie.getLeftBranch().toChain() + " Right=" + ie.getRightBranch().toChain() + " Data=" + ie.getData().toChain();
		
		int h = (int)ie.getHeight(); 
		chainDump += " Height=" + Chain(h);

		if ( ie.getIdxPtr() != 0 )
		{
		    int l;
		    memcpy(&l, ie.getIdxPtr(), sizeof(int));
		    chainDump += " Len: " + Chain(l);
		    
		    int v;
		    memcpy(&v, (int*)((long)ie.getIdxPtr() + sizeof(int)), sizeof(int));
		    chainDump += " Val: " + Chain(v);

		    
		    // cout << " IdxPtr: " << ie.getIdxPtr();
		}	
		chainDump += "\n";

	    }
	    else
	    {
		cout << "Not implemented .." << endl;
	    }
	    
	    pc = (char*)pC->getNext(len, dp);	    
	    
	}
	
	delete pC;

	break;
    }
    case CegoObject::PROCEDURE:
    {
	chainDump = Chain("Not implemented");
	break;
    }
    case CegoObject::VIEW:
    {
	chainDump = Chain("Not implemented");
	break;
    }
    default:
	break;
    }
}

////////////////////////
/// private methods ///
///////////////////////

void CegoTableManager::regDataFiles(const Chain& tableSet, bool cleanIt)
{
    
    int tmpFileId = _pDBMng->getTmpFid(tableSet); 
    int tmpSize = _pDBMng->getTmpSize(tableSet); 
    int tabSetId = _pDBMng->getTabSetId(tableSet);
 
    Chain dbTempFileName = _pDBMng->getTmpFileName(tableSet); 
    Chain dbSysFileName = _pDBMng->getSysFileName(tableSet);

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Registering system datafile  ") + dbSysFileName + Chain(" ..."));
    _pDBMng->regDataFile(tabSetId, dbSysFileName, tabSetId, _pLockHandle);
    
    if ( cleanIt )
    {
	_pDBMng->log(_modId, Logger::NOTICE, Chain("Cleaning system datafile  ") + dbSysFileName + Chain(" ...")); 
	_pDBMng->cleanDataFile(tabSetId, _pLockHandle);
    }

    _pDBMng->log(_modId, Logger::NOTICE, Chain("Registering temp datafile  ") + dbTempFileName + Chain(" ..."));
    _pDBMng->regDataFile(tabSetId, dbTempFileName, tmpFileId, _pLockHandle);

    if ( cleanIt )
    {
	_pDBMng->log(_modId, Logger::NOTICE, Chain("Cleaning temp datafile  ") + dbTempFileName + Chain(" ..."));
	_pDBMng->cleanDataFile(tmpFileId, _pLockHandle);
    }

    ListT<Chain> dfList;
    ListT<int> fidList;
    ListT<int> sizeList;
    
    _pDBMng->getDataFileInfo(tableSet, Chain(XML_APPFILE_VALUE), dfList, fidList, sizeList);
	
    Chain *pFileName = dfList.First();
    int *pFid = fidList.First();
    int *pSize = sizeList.First();
    
    while ( pFileName && pFid && pSize ) 
    {
	
	_pDBMng->regDataFile(tabSetId, *pFileName, *pFid, _pLockHandle);

	if ( cleanIt )
	{	    
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Cleaning datafile  ") + *pFileName + Chain(" ..."));
	    _pDBMng->cleanDataFile(*pFid, _pLockHandle);
	}

	pFileName = dfList.Next();
	pFid = fidList.Next();
	pSize = sizeList.Next();
	
    }

    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();

    _pDBMng->getDataFileInfo(tableSet, Chain(XML_SYSFILE_VALUE), dfList, fidList, sizeList);
	
    pFileName = dfList.First();
    pFid = fidList.First();
    pSize = sizeList.First();
    
    while ( pFileName && pFid && pSize ) 
    {
	
	_pDBMng->regDataFile(tabSetId, *pFileName, *pFid, _pLockHandle);

	if ( cleanIt )
	{	    
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Cleaning datafile  ") + *pFileName + Chain(" ..."));
	    _pDBMng->cleanDataFile(*pFid, _pLockHandle);
	}

	pFileName = dfList.Next();
	pFid = fidList.Next();
	pSize = sizeList.Next();
	
    }

    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();

    _pDBMng->getDataFileInfo(tableSet, Chain(XML_TEMPFILE_VALUE), dfList, fidList, sizeList);
	
    pFileName = dfList.First();
    pFid = fidList.First();
    pSize = sizeList.First();
    
    while ( pFileName && pFid && pSize ) 
    {
	
	_pDBMng->regDataFile(tabSetId, *pFileName, *pFid, _pLockHandle);

	if ( cleanIt )
	{	    
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Cleaning datafile  ") + *pFileName + Chain(" ..."));
	    _pDBMng->cleanDataFile(*pFid, _pLockHandle);
	}

	pFileName = dfList.Next();
	pFid = fidList.Next();
	pSize = sizeList.Next();
	
    }

    dfList.Empty();
    fidList.Empty();
    sizeList.Empty();

    _pDBMng->getDataFileInfo(tableSet, Chain(XML_SYSFILE_VALUE), dfList, fidList, sizeList);
	
    pFileName = dfList.First();
    pFid = fidList.First();
    pSize = sizeList.First();
    
    while ( pFileName && pFid && pSize ) 
    {
	
	_pDBMng->regDataFile(tabSetId, *pFileName, *pFid, _pLockHandle);

	if ( cleanIt )
	{	    
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Cleaning datafile  ") + *pFileName + Chain(" ..."));
	    _pDBMng->cleanDataFile(*pFid, _pLockHandle);
	}

	pFileName = dfList.Next();
	pFid = fidList.Next();
	pSize = sizeList.Next();
	
    }
}
    
void CegoTableManager::initLock(int tabSetId)
{    
    tsLock[tabSetId].init(LCKMNG_LOCKWAITDELAY, __lockStatOn);
    _pTM->initLock(tabSetId);

    Chain tableSet = _pDBMng->getTabSetName(tabSetId);
    tsLock[tabSetId].setId(tableSet);
}

bool CegoTableManager::archiveComplete(const Chain& tableSet)
{
    
    ListT<Chain> lfList;
    ListT<int> sizeList;
    ListT<Chain> statusList;

    _pDBMng->getLogFileInfo(tableSet, lfList, sizeList, statusList);
    
    Chain *pStatus = statusList.First();
    
    while ( pStatus ) 
    {
	if ( *pStatus == Chain(XML_OCCUPIED_VALUE))
	{
	    return false;
	}
	else
	{
	    pStatus = statusList.Next();
	}	
    }
    return true;
}

void CegoTableManager::finishOpenTransaction(int tabSetId)
{
    _pTM->finishOpenTransaction(tabSetId);
}	

void CegoTableManager::extractIndexValue(const ListT<CegoField>& tableSchema, const ListT<CegoField>& indexSchema, char* p, int len, int& idxLen)
{

    // skipping tid

    CegoField* pIF = indexSchema.First();
           	
    idxLen = 0;

    while (pIF)
    {
       
	// cout << "Checking index " << pIF->getAttrName() << endl;
	CegoField* pTF = tableSchema.First();	

	while (pTF)
	{	    
	    if ( (Chain)pTF->getAttrName() == (Chain)pIF->getAttrName() )
	    {

		int flen = pTF->getValue().getLength();
		
		idxLen += flen + sizeof(int);
		if ( idxLen > len)
		{
		    throw Exception(EXLOC, Chain("Index value too long")); 
		}
   
		memcpy(p, &flen, sizeof(int));
		p += sizeof(int);
		if ( flen > 0 )
		{
		    memcpy(p, pTF->getValue().getValue(), flen);
		    p += flen;
		}
		
	    }
	    pTF = tableSchema.Next();
	}

	pIF = indexSchema.Next();	
	
    }   
}

void CegoTableManager::logIt(int tabSetId, CegoLogRecord& lr)
{

    tsLock[tabSetId].writeLock(TS_LOCKTIMEOUT);

    try {
    
	CegoLogManager::LogResult res = _pDBMng->logAction(tabSetId, lr);

	if ( res == CegoLogManager::LOG_FULL )
	{
	    
	    int gid;
	    Chain logFile;
	    int logSize = 0 ;
	    
	    Chain tableSet = _pDBMng->getTabSetName(tabSetId);
	    writeCheckPoint(tableSet, true, false);
	    
	    res =  _pDBMng->logAction(tabSetId, lr);
	    
	}
    
	if ( res == CegoLogManager::LOG_ERROR )
	{
	    Chain tableSet = _pDBMng->getTabSetName(tabSetId);
	    _pDBMng->setTableSetSyncState(tableSet, XML_LOG_LOSS_VALUE);

	    throw Exception(EXLOC, Chain("Cannot write to log"));
	}
	
    }
    catch ( Exception e )
    {

	tsLock[tabSetId].unlock();
	throw e;
    }

    tsLock[tabSetId].unlock();
}

void CegoTableManager::abort()
{
    _isAborted = true;
}

void CegoTableManager::proceed()
{
    _isAborted = false;
}

bool CegoTableManager::isAborted() const
{
    return _isAborted;
}

void CegoTableManager::poolP()
{
    if ( _pPool )
    {
	// cout << "pool P for " << _thrIdx << endl;
	_pPool->P(_thrIdx);
    }
}

void CegoTableManager::poolV()
{
    if ( _pPool )
    {
	// cout << "pool V for " << _thrIdx << endl;
	_pPool->V(_thrIdx);
    }
}

void CegoTableManager::addCompProcedure(int tabSetId, CegoProcedure *pProc)
{
    poolP();

    try
    {
	CegoProcedure** pCheckProc = _procList[tabSetId].First();
	while ( pCheckProc )
	{
	    if ( (Chain)(*pCheckProc)->getName() == (Chain)pProc->getName() )
	    {
		throw Exception(EXLOC, Chain("Procedure ") + pProc->getName() + Chain(" already exists"));  
	    }
	    pCheckProc = _procList[tabSetId].Next();
	}
	_procList[tabSetId].Insert(pProc);

	poolV();
    }
    catch ( Exception e )
    {
	poolV();
	throw e;
    }
    
}

void CegoTableManager::removeCompProcedure(int tabSetId, const Chain& procName)
{
    poolP();

    CegoProcedure** pProc = _procList[tabSetId].First();
    while ( pProc )
    {
	if ( (Chain)(*pProc)->getName() == (Chain)procName )
	{
	    CegoProcedure *pDelProc = *pProc;
	    delete pDelProc;

	    _procList[tabSetId].Remove(*pProc);

	    poolV();
	    return;
	}
	pProc = _procList[tabSetId].Next();
    }

    poolV();

    return;
}

bool CegoTableManager::checkCompProcedure(int tabSetId, const Chain& procName)
{
    poolP();

    CegoProcedure** pProc = _procList[tabSetId].First();
    while ( pProc )
    {
	if ( (Chain)(*pProc)->getName() == (Chain)procName )
	{
	    poolV();
	    return true;
	}
	pProc = _procList[tabSetId].Next();
    }
    poolV();
    return false;
}

CegoProcedure* CegoTableManager::getCompProcedure(int tabSetId, const Chain& procName)
{
    poolP();
    CegoProcedure** pProc = _procList[tabSetId].First();
    while ( pProc )
    {
	if ( (Chain)(*pProc)->getName() == (Chain)procName )
	{
	    poolV();
	    return *pProc;
	}
	pProc = _procList[tabSetId].Next();
    }

    poolV();
    Chain msg = Chain("Procedure ") + procName + Chain(" not compiled"); 
    throw Exception(EXLOC, msg);

}

void CegoTableManager::addCompView(int tabSetId, CegoView *pView)
{
    poolP();
    _viewList[tabSetId].Insert(pView);
    poolV();
}

void CegoTableManager::removeCompView(int tabSetId, const Chain& viewName)
{
    poolP();
    CegoView** pView = _viewList[tabSetId].First();
    while ( pView )
    {

	if ( (Chain)(*pView)->getViewName() == (Chain)viewName )
	{

	    CegoView *pDelView = *pView;
	    delete pDelView;
	    
	    _viewList[tabSetId].Remove(*pView);

	    poolV();

	    return;
	}
	pView = _viewList[tabSetId].Next();
    }
    poolV();
    return;
}

bool CegoTableManager::checkCompView(int tabSetId, const Chain& viewName)
{
    poolP();
    CegoView** pView = _viewList[tabSetId].First();
    while ( pView )
    {
	if ( (Chain)(*pView)->getViewName() == (Chain)viewName )
	{
	    poolV();
	    return true;
	}
	pView = _viewList[tabSetId].Next();
    }
    poolV();
    return false;
}

CegoView* CegoTableManager::getCompView(int tabSetId, const Chain& viewName)
{
    poolP();
    CegoView** pView = _viewList[tabSetId].First();
    while ( pView )
    {
	if ( (Chain)(*pView)->getViewName() == (Chain)viewName )
	{
	    poolV();
	    return *pView;
	}
	pView = _viewList[tabSetId].Next();
    }
    poolV();

    Chain msg = Chain("View ") + viewName + Chain(" not compiled"); 
    throw Exception(EXLOC, msg);

}

void CegoTableManager::removeAllComp(int tabSetId)
{
    poolP();
    CegoProcedure** pProc = _procList[tabSetId].First();
    while ( pProc )
    {
	_procList[tabSetId].Remove(*pProc);
	CegoProcedure *pDelProc = *pProc;
	delete pDelProc;
	pProc = _procList[tabSetId].First();
    }

    CegoView** pView = _viewList[tabSetId].First();
    while ( pView )
    {
	_viewList[tabSetId].Remove(*pView);
	CegoView *pDelView = *pView;
	delete pDelView;
	pView = _viewList[tabSetId].First();
    }
    poolV();
}

void CegoTableManager::getTSLockStat(int tabSetId, Chain& lockName, long& lockCount, long &numRdLock, long &numWrLock, long &sumRdDelay, long &sumWrDelay)
{
    lockName = tsLock[tabSetId].getId();
    lockCount = tsLock[tabSetId].numLockTry();

    numRdLock = tsLock[tabSetId].numReadLock();
    numWrLock = tsLock[tabSetId].numWriteLock();
    sumRdDelay = 0;
    sumWrDelay = 0;

    if ( tsLock[tabSetId].numReadLock() > 0 )
	sumRdDelay = tsLock[tabSetId].sumReadDelay() / LCKMNG_DELRES;
    if ( tsLock[tabSetId].numWriteLock() > 0 )
	sumWrDelay = tsLock[tabSetId].sumWriteDelay() / LCKMNG_DELRES;

}

void CegoTableManager::getLHLockStat(unsigned long lockId, Chain& lockName, long& lockCount, long &numRdLock, long &numWrLock, long &sumRdDelay, long &sumWrDelay)
{
    _pLockHandle->getLockStat(lockId, lockName, lockCount, numRdLock, numWrLock, sumRdDelay, sumWrDelay);
}

void CegoTableManager::getLHAggLockStat(const Chain& lockName, int& numLock, long& lockCount, long &numRdLock, long &numWrLock, long &sumRdDelay, long &sumWrDelay)
{
    _pLockHandle->getAggLockStat(lockName, numLock, lockCount, numRdLock, numWrLock, sumRdDelay, sumWrDelay);
}

CegoTransactionManager* CegoTableManager::getTransactionManager()
{
    return _pTM;
} 

void CegoTableManager::releaseBlob(int tabSetId, int fileId, int pageId)
{

    CegoBufferPage bp;
    _pDBMng->bufferFix(bp, tabSetId, fileId, pageId, CegoBufferPool::SYNC, _pLockHandle);
    while ( bp.isFixed() )
    {
	
	fileId = bp.getNextFileId();
	pageId = bp.getNextPageId();
	
	_pDBMng->bufferRelease(bp, _pLockHandle);
	
	if ( fileId && pageId )
	    _pDBMng->bufferFix(bp, tabSetId, fileId, pageId, CegoBufferPool::SYNC, _pLockHandle);

    }
}

unsigned char* CegoTableManager::getBlobData(int tabSetId, int fileId, int pageId, long& blobSize)
{
    CegoBufferPage bp;
    try
    {
	_pDBMng->bufferFix(bp, tabSetId, fileId, pageId, CegoBufferPool::SYNC, _pLockHandle);
	
	memcpy (&blobSize, bp.getChunkEntry(), sizeof(long));
	
	// allocate buffer of size blobsize

	unsigned char* rawBuf = (unsigned char*)malloc(blobSize);
	if (rawBuf == NULL)
	{
	    throw Exception(EXLOC, Chain("malloc system error"));
	}
		
	unsigned char* rawPtr = rawBuf;

	long writtenByte = 0;
	
	while ( bp.isFixed() )
	{	    
	    int chunkSize;
	    if (  bp.getChunkLen() < blobSize - writtenByte )
	    {		       
		chunkSize = bp.getChunkLen();
		if ( writtenByte == 0 )
		    chunkSize -= sizeof(long);
	    }
	    else
	    {
		chunkSize = blobSize - writtenByte;
	    }
	    char* src = 0;
	    if ( writtenByte == 0 )
	    {
		src = (char*)(bp.getChunkEntry() + sizeof(long));
	    }
	    else
	    {
		src = (char*)(bp.getChunkEntry());

	    }
	    memcpy(rawPtr, src, chunkSize);
	    
	    rawPtr += chunkSize;
	    writtenByte += chunkSize;

	    fileId = bp.getNextFileId();
	    pageId = bp.getNextPageId();
	    
	    _pDBMng->bufferUnfix(bp, false, _pLockHandle);
	    
	    if ( fileId || pageId )			
		_pDBMng->bufferFix(bp, tabSetId, fileId, pageId, CegoBufferPool::SYNC, _pLockHandle);
	    
	}

	return rawBuf;
     	
    }
    catch ( Exception e )
    {
	if ( bp.isFixed() )
	    _pDBMng->bufferUnfix(bp, true, _pLockHandle);
    }

    return 0;
       
}

void CegoTableManager::putBlobData(int tabSetId, unsigned char* data, long blobSize, int& fileId, int& pageId)
{
        
    CegoBufferPage bp;

    try
    {
	getNewFilePage(bp, tabSetId, CegoObject::TABLE);
	bp.initPage(CegoBufferPage::BLOB);
	
	fileId = bp.getFileId();
	pageId = bp.getPageId();
				
	int freeInPage = bp.getChunkLen();
	char* pagePtr = bp.getChunkEntry();
		
	memcpy(pagePtr, &blobSize, sizeof(long));
	pagePtr += sizeof(long);
	freeInPage -= sizeof(long);
	
	unsigned char *bufPtr = 0; 
	long availFromBuf = 0;
		
	long writtenByte = 0;
	while ( writtenByte < blobSize )
	{		    
	    if ( availFromBuf == 0 )		    
	    {		
		availFromBuf=blobSize;	       
		bufPtr = data;	     
	    }        
	    if ( freeInPage == 0 )
	    {
		CegoBufferPage nextPage;
		getNewFilePage(nextPage, tabSetId, CegoObject::TABLE);
		nextPage.initPage(CegoBufferPage::BLOB);
		
		bp.setNextFileId(nextPage.getFileId());
		bp.setNextPageId(nextPage.getPageId());
		
		_pDBMng->bufferUnfix(bp, true, _pLockHandle);
		
		bp = nextPage;
		
		freeInPage = bp.getChunkLen();
		pagePtr = bp.getChunkEntry();
	    }
		    
	    if ( freeInPage >= availFromBuf )
	    {
		memcpy(pagePtr, bufPtr, availFromBuf);
		writtenByte += availFromBuf;
		pagePtr += availFromBuf;
		freeInPage -= availFromBuf;
		availFromBuf = 0;
	    }
	    else if ( freeInPage < availFromBuf )
	    {
		memcpy(pagePtr, bufPtr, freeInPage);
		writtenByte += freeInPage;
		bufPtr += freeInPage;
		availFromBuf -= freeInPage;
		freeInPage = 0;
	    }
	    
	}
	_pDBMng->bufferUnfix(bp, true, _pLockHandle);
    }
    catch ( Exception e )
    {
	if ( bp.isFixed() )
	    _pDBMng->bufferUnfix(bp, true, _pLockHandle);
	if ( fileId || pageId )
	{
	    releaseBlob(tabSetId, fileId, pageId);
	}
    }
}

ListT<CegoBlob> CegoTableManager::getBlobs(int tabSetId, const ListT<CegoField>& fvl)
{
    ListT<CegoBlob> blobList;

    CegoField* pF = fvl.First();
    while (pF)
    {
	if ( pF->getValue().getType() == BLOB_TYPE  )
	{

	    int fileId;
	    int pageId;
	    memcpy(&fileId, pF->getValue().getValue(), sizeof(int));
	    memcpy(&pageId, (void*)((long)pF->getValue().getValue() + sizeof(int)), sizeof(int));
	    
	    long blobSize;
	    unsigned char* blobBuf = getBlobData(tabSetId, fileId, pageId, blobSize);

	    blobList.Insert(CegoBlob(fileId, pageId, blobBuf, blobSize));
	}
	pF = fvl.Next();
    }

    return blobList;
}


void CegoTableManager::addBUStat(int tabSetId, const Chain& butype, const Chain& msg)
{
    // insert entry in bustat table

    CegoTableObject boe;    
    getObject(tabSetId, Chain(SYSTAB_BUSTAT_ID), CegoObject::SYSTEM, boe);
    ListT<CegoField> fvl = boe.getSchema();
    CegoField *pF = fvl.First();
    while ( pF )
    {
	if (pF->getAttrName() == Chain(SYSTAB_TS_ATTR) )
	{
	    Datetime n;
	    int *pI = new (int);    
	    *pI = n.asInt();    
	    CegoFieldValue fv(DATETIME_TYPE, pI, sizeof(int), true);
	    pF->setValue(fv);
	}
	else if ( pF->getAttrName() == Chain(SYSTAB_BUINFO_ATTR) )
	{
	    pF->setValue(CegoFieldValue(VARCHAR_TYPE, butype));	
	}
	else if ( pF->getAttrName() == Chain(SYSTAB_BUMSG_ATTR) )
	{
	    pF->setValue(CegoFieldValue(VARCHAR_TYPE, msg));	
	}
	pF = fvl.Next();
    }
    
    CegoDataPointer bp;
    insertDataTable(boe, fvl, bp, true);
}
