///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoTransactionManager.cc
// -------------------------
// Cego transaction manager implementation
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoTransactionManager
// 
// Description: This module implements the transaction management including virgin index updates
//
// 11.05.2019, Rework done, since not crash safe, the getObjectWithFix called have been removed.
//             Also fix in commitUpdate by adding BtreeManager commit method 
//
///////////////////////////////////////////////////////////////////////////////

// base includes
#include <lfcbase/Exception.h>
#include <lfcbase/Sleeper.h>
#include <lfcbase/ThreadLock.h>
#include <lfcbase/Tokenizer.h>

// cego includes
#include "CegoTransactionManager.h"

// system includes
#include <string.h>
#include <stdlib.h>

#include "CegoDataType.h"
#include "CegoTupleState.h"
#include "CegoTableManager.h"
#include "CegoAVLIndexManager.h"
#include "CegoBTreeManager.h"

#define RBSEP "#"
#define SYS_RB "rbcatlog"
#define SYS_RBCOMMIT "rbcommit"
#define SYS_RBROLLBACK "rbrollback"

#define SYS_UPDTAB "updtab"

#define SYS_RB_PAGEID "rb_pageid"
#define SYS_RB_OFFSET "rb_offset"
#define SYS_RB_TABLE "rb_table"

#define SYS_UPDTAB_PAGEID "upd_pageid"
#define SYS_UPDTAB_OFFSET "upd_offset"

extern bool __lockStatOn;

/////////////
// TAEntry //
/////////////

CegoTransactionManager::TAEntry::TAEntry()
{
}

CegoTransactionManager::TAEntry::TAEntry(const unsigned long long tid)
{
    _tid = tid;
}

CegoTransactionManager::TAEntry::TAEntry(const unsigned long long tid,
					 // const CegoBufferPage& bp,
					 const CegoTableObject& oe)
{
    _tid = tid;
    _oe = oe;
}

CegoTransactionManager::TAEntry::~TAEntry()
{
}

const unsigned long long CegoTransactionManager::TAEntry::getTid() const
{
    return _tid;
}

CegoTableObject& CegoTransactionManager::TAEntry::getTableObject()
{
    return _oe;
}

CegoTransactionManager::TAEntry& CegoTransactionManager::TAEntry::operator = ( const CegoTransactionManager::TAEntry& t)
{
    _tid = t._tid;
    _oe = t._oe;
    return (*this);
}

bool CegoTransactionManager::TAEntry::operator == ( const CegoTransactionManager::TAEntry& t)
{
    if ( _tid == t._tid )
	return true;
    return false;
}

CegoTransactionManager::CegoTransactionManager(CegoTableManager *pTM)
{
    _pTabMng = pTM;

    CegoFieldValue defVal;
    _rbcatSchema.Insert(CegoField(SYS_RB, SYS_RB, SYS_RB_PAGEID, PAGEID_TYPE, sizeof(PageIdType), 0, defVal, true, 1));
    _rbcatSchema.Insert(CegoField(SYS_RB, SYS_RB, SYS_RB_OFFSET, INT_TYPE, sizeof(int), 0, defVal, true, 2));
    _rbcatSchema.Insert(CegoField(SYS_RB, SYS_RB, SYS_RB_TABLE, VARCHAR_TYPE, MAX_OBJNAME_LEN, 0, defVal, true, 3));
    
    _updSchema.Insert(CegoField(SYS_UPDTAB, SYS_UPDTAB, SYS_UPDTAB_PAGEID, PAGEID_TYPE, sizeof(PageIdType), 0, defVal, true, 1));
    _updSchema.Insert(CegoField(SYS_UPDTAB, SYS_UPDTAB, SYS_UPDTAB_OFFSET, INT_TYPE, sizeof(int), 0, defVal, true, 2));

    _pDBMng = pTM->getDBMng();

    _modId = _pDBMng->getModId("CegoTransactionManager");
}

CegoTransactionManager::~CegoTransactionManager()
{
}

void CegoTransactionManager::release(unsigned tabSetId)
{
    // nothing to do anymore
}

void CegoTransactionManager::reorgSystemSpace(unsigned tabSetId)
{
    // nothing to do anymore
}

void CegoTransactionManager::newRBEntry(unsigned tabSetId, unsigned long long tid, PageIdType pageId, unsigned offset, const Chain& tableName)
{
    TAEntry *pTAE = _taList.Find( TAEntry(tid));

    CegoBufferPage bp;
    
    // if this is a new transaction, we have to create a rollback table for it
    if ( pTAE == 0 )
    {
	Chain taTable = Chain(SYS_RB) + Chain(RBSEP) + Chain(tid);
	
	CegoTableObject oe;

	// in case of crash recovery, the rb table might already exist
	if ( _pTabMng->objectExists(tabSetId, taTable, CegoObject::RBSEG) == false )
	{
	   oe = _pTabMng->createDataTable(tabSetId, taTable, CegoObject::RBSEG, _rbcatSchema);
	}
	else
	{
	    _pTabMng->getObject(tabSetId, taTable, CegoObject::RBSEG, oe);
	}
	
	TAEntry tae(tid, oe);

	_taList.Insert(tae);
	pTAE = _taList.Find( TAEntry(tid));
    }
    
    CegoFieldValue fv1(PAGEID_TYPE, &pageId, sizeof(PageIdType));
    CegoField f1(CegoField(SYS_RB, SYS_RB, SYS_RB_PAGEID, PAGEID_TYPE, sizeof(PageIdType), 0, fv1, true, 1));	
    
    CegoFieldValue fv2(INT_TYPE, &offset, sizeof(int));
    CegoField f2(CegoField(SYS_RB, SYS_RB, SYS_RB_OFFSET, INT_TYPE, sizeof(int), 0, fv2, true, 2));
    
    CegoFieldValue fv3(VARCHAR_TYPE, (char*)tableName, tableName.length());
    CegoField f3(CegoField(SYS_RB, SYS_RB, SYS_RB_TABLE, VARCHAR_TYPE, MAX_OBJNAME_LEN, 0, fv3, false, 3));	
    
    ListT<CegoField> fl;
    fl.Insert(f1);
    fl.Insert(f2);
    fl.Insert(f3);
        
    unsigned long long rbtid = 0;
    unsigned long long rbtastep = 0;
    CegoTupleState ts = COMMITTED;
    
    char *pBufBase = 0;
    unsigned buflen = 0;
    
    unsigned encLen = _qh.encodeFVL(rbtid, rbtastep, ts, fl, pBufBase, buflen);
    
    try
    {	
	CegoDataPointer dp = _pTabMng->insertData(pTAE->getTableObject(), pBufBase, encLen, true);
    }
    catch ( Exception e )
    {
	free (pBufBase);
	throw Exception(EXLOC, Chain("Cannot create new rollback entry"), e);
    }
    free (pBufBase);   
}

/* 
   For crash recovery, we have to find out about all affected tables
*/

void CegoTransactionManager::getCrashAffectedTables(unsigned tabSetId, SetT<Chain>& tableList)
{
    CegoObjectCursor *pOC = 0;
    
    try
    {	
	ListT<Chain> rboList;    

	_pTabMng->getObjectList(tabSetId, CegoObject::RBSEG, rboList);

	Chain *pRBO = rboList.First();
	while ( pRBO )
	{    
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Checking rollback segment ") + *pRBO + Chain(" ..."));

	    CegoTableObject rbo;
	    _pTabMng->getObject(tabSetId, *pRBO, CegoObject::RBSEG, rbo);

	    ListT<CegoField> schema = rbo.getSchema();
	    
	    pOC = _pTabMng->getObjectCursor(tabSetId, *pRBO, *pRBO, CegoObject::RBSEG);
	    	  
	    CegoDataPointer dp;
	    bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, dp);
	    	    
	    while (moreTuple)
	    {	
		Chain tableName;
		CegoField *pF = schema.Find(CegoField(Chain(), SYS_RB_TABLE));
		if (pF)
		{
		    tableName = Chain((char*)pF->getValue().getValue());			
		    tableList.Insert(tableName);			      
		}
		
		moreTuple = _pTabMng->getNextTuple(pOC, schema, dp);	
	    }

	    pOC->abort();
	    delete pOC;
	    pOC = 0;

	    pRBO = rboList.Next();
	}
    }
    catch ( Exception e )
    {
	if ( pOC )
	{
	    pOC->abort();
	    delete pOC;
	}
	throw Exception(EXLOC, Chain("Cannot get crash affected tables"), e);
    }
}

/* 
   For the commit operation, we provide a method to give information about all affected table
   for this transaction. Setting a lock to all these tables before starting the commit avoids
   a possible deadlock
*/

void CegoTransactionManager::getTransactionAffectedTables(unsigned tabSetId, unsigned long long tid, SetT<Chain>& tableList)
{
    CegoObjectCursor *pOC = 0;

    try
    {	
	TAEntry *pTAE = _taList.Find( TAEntry(tid) );

	if ( pTAE )
	{
	    Chain taTable = pTAE->getTableObject().getName();
	    pOC = _pTabMng->getObjectCursor(tabSetId, taTable, taTable, CegoObject::RBSEG);
	    
	    ListT<CegoField> schema = _rbcatSchema;
	    	    
	    CegoDataPointer dp;
	    bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, dp);
	    
	    while (moreTuple)
	    {	
		Chain tableName;
		CegoField *pF = schema.Find(CegoField(taTable, SYS_RB_TABLE));
		if (pF)
		{
		    tableName = Chain((char*)pF->getValue().getValue());			
		    tableList.Insert(tableName);			      
		}
		
		moreTuple = _pTabMng->getNextTuple(pOC, schema, dp);	
	    }

	    pOC->abort();
	    delete pOC;
	    pOC = 0;
	}
    }
    catch ( Exception e )
    {
	if ( pOC )
	{
	    pOC->abort();
	    delete pOC;
	}

	throw Exception(EXLOC, Chain("Cannot get transaction affected tables"), e);
    }
}

bool CegoTransactionManager::hasOpenTransaction(unsigned tabSetId, const Chain& tableName) const
{
    ListT<Chain> rboList;    

    _pTabMng->getObjectList(tabSetId, CegoObject::RBSEG, rboList);
    
    Chain *pRBO = rboList.First();

    bool tabFound = false;
    
    while ( pRBO && tabFound == false)
    {
	CegoObjectCursor *pOC = _pTabMng->getObjectCursor(tabSetId, *pRBO, *pRBO, CegoObject::RBSEG);
	
	ListT<CegoField> schema = _rbcatSchema;
	
	CegoDataPointer dp;

	bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, dp);
       
	while (moreTuple && tabFound == false)
	{	    
	    Chain taTabName;
	    
	    CegoField *pF = schema.Find(CegoField(SYS_RB, SYS_RB_TABLE));
	    if (pF)
		taTabName = Chain((char*)pF->getValue().getValue());

	    if ( taTabName == tableName )
		tabFound = true;
	    else
		moreTuple = _pTabMng->getNextTuple(pOC, schema, dp);
	}
	pOC->abort();
	delete pOC;

	pRBO = rboList.Next();
    }

    return tabFound;
}
	
void CegoTransactionManager::finishOpenTransaction(unsigned tabSetId)
{
    Chain tableSet = _pDBMng->getTabSetName(tabSetId);
    _pDBMng->log(_modId, Logger::NOTICE, Chain("Finishing open transaction for tableset ") + tableSet);
    ListT<Chain> rboList;    

    _pTabMng->getObjectList(tabSetId, CegoObject::RBSEG, rboList);
    
    Chain *pRBO = rboList.First();
    
    while ( pRBO )
    {	
	Tokenizer t(*pRBO, Chain(RBSEP));
	Chain taType;
	Chain taTid;

	t.nextToken(taType);
	t.nextToken(taTid);

	_pDBMng->log(_modId, Logger::NOTICE, Chain("Treating ") + *pRBO + Chain(" Tid = ") + taTid + Chain(" Type = ") + taType);
		     
	unsigned tid = taTid.asUnsigned();
	
	if ( taType == Chain(SYS_RB) )
	{	    
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Rollback transaction ") + Chain(tid));
	    // transaction still was not in commit or rollback phase
	    // so we roll back
	    rollbackTransaction(tabSetId, tid);
	}
	else if ( taType == Chain(SYS_RBROLLBACK) )
	{
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Finishing rollback for transaction ") + Chain(tid));
	    // transaction already was in rollback phase
	    // we have to finish rollback

	    doRollback(tabSetId, *pRBO);
	    _pTabMng->removeObject(tabSetId, *pRBO, CegoObject::RBSEG);
	}
	else if ( taType == Chain(SYS_RBCOMMIT) )
	{
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Finishing commit for transaction ") + Chain(tid));
	    // transaction already was in commit phase
	    // we have to finish commit
	    doCommit(tabSetId, *pRBO);
	    _pTabMng->removeObject(tabSetId, *pRBO, CegoObject::RBSEG);
	}

	pRBO = rboList.Next();
    }    
}

unsigned long long CegoTransactionManager::commitTransaction(unsigned tabSetId, unsigned long long tid)
{   
    unsigned long long opCount = 0;

    TAEntry *pTAE = _taList.Find( TAEntry(tid) );

    /* in case of a crash recovery, this method is called by finishOpenTransaction.
       In this case, no entry in _taList is available, but we still have to rollback from existing segment
    */

    Chain rbo = Chain(SYS_RB) + Chain(RBSEP) + Chain(tid);

    if ( _pTabMng->objectExists(tabSetId, rbo, CegoObject::RBSEG) )
    {
	// We rename the rollback object to indicate the commit phase
	// In case of a crash recovery, the renamed objects have to be recovered
	
	Chain commitTa = Chain(SYS_RBCOMMIT) + Chain(RBSEP) + Chain(tid);
	
	_pTabMng->renameObject(tabSetId, rbo, CegoObject::RBSEG, commitTa);
		
	if ( pTAE )
	    pTAE->getTableObject().setName(commitTa);


	unsigned long long cpCount = _pDBMng->getCPCount();
	
	opCount = doCommit(tabSetId, commitTa);

	/* if a checkpoint has occured during commit, we have to sync again
	   to have consistent data on disk */

	if ( _pDBMng->getCPCount() > cpCount )
	{
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Forced checkpoint by commitTransaction"));
	    _pDBMng->writeCheckPoint(tabSetId, true, Chain(), 0,  _pTabMng->getLockHandler());
	}
	
	// Step 2 : Cleaning up rollback segment
	
	if ( pTAE )
	{
	    _taList.Remove(TAEntry(tid));
	}
	
	_pTabMng->removeObject(tabSetId, commitTa, CegoObject::RBSEG);
	
    }
    return opCount;
}

unsigned long long CegoTransactionManager::rollbackTransaction(unsigned tabSetId, unsigned long long tid)
{    
    unsigned long long opCount = 0;

    TAEntry *pTAE = _taList.Find( TAEntry(tid) );

    /* in case of a crash recovery, this method is called by finishOpenTransaction.
       In this case, no entry in _taList is available, but we still have to rollback from existing segment
    */
     
    Chain rbo = Chain(SYS_RB) + Chain(RBSEP) + Chain(tid);

    if ( _pTabMng->objectExists(tabSetId, rbo, CegoObject::RBSEG) )
    {
	// rename the transaction object 
	
	Chain rollbackTa = Chain(SYS_RBROLLBACK) + Chain(RBSEP) + Chain(tid);
	_pTabMng->renameObject(tabSetId, rbo, CegoObject::RBSEG, rollbackTa);
	
	if ( pTAE )
	    pTAE->getTableObject().setName(rollbackTa);

	unsigned long long cpCount = _pDBMng->getCPCount();
	
	opCount = doRollback(tabSetId, rollbackTa);

	/* if a checkpoint has occured during rollback, we have to sync again
	   to have consistent data on disk */
	
	if ( _pDBMng->getCPCount() > cpCount )
	{
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Forced checkpoint by rollbackTransaction"));
	    _pDBMng->writeCheckPoint(tabSetId, true, Chain(), 0,  _pTabMng->getLockHandler());
	}

	if ( pTAE )
	{
	    _taList.Remove(TAEntry(tid));
	}
	
	_pTabMng->removeObject(tabSetId, rollbackTa, CegoObject::RBSEG);
    }

    return opCount;    
}

unsigned long long CegoTransactionManager::doCommit(unsigned tabSetId, const Chain& rbo)
{
    unsigned long long opCount = 0;

    CegoObjectCursor *pOC = _pTabMng->getObjectCursor(tabSetId, rbo, rbo, CegoObject::RBSEG);
    
    ListT<CegoField> schema = _rbcatSchema;
    
    // Step 1 : Committing all attached tuples
    
    try  
    {	
	CegoDataPointer rbdp;
	bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, rbdp);
	
	Chain cachedTable;
	ListT<CegoField> cachedFvl;
	ListT<CegoTableObject> cachedIdxList;
	ListT<CegoBTreeObject> cachedBTreeList;
	ListT<CegoKeyObject> cachedKeyList;
	ListT<CegoCheckObject> cachedCheckList;
	ListT<CegoTriggerObject> cachedTriggerList;
	ListT<CegoAliasObject> cachedAliasList;
	
	unsigned numInvalid;

	while (moreTuple)
	{
	    PageIdType pageId;
	    unsigned offset;

	    CegoField *pF = schema.Find(CegoField(SYS_RB, SYS_RB_PAGEID));
	    if (pF)
	    {      
		memcpy(&pageId, pF->getValue().getValue(), sizeof(PageIdType));
	    }
	    else
	    {
		Chain msg = Chain("Cannot find attribute ") + Chain(SYS_RB_PAGEID);
		throw Exception(EXLOC, msg);
	    }
	    
	    pF = schema.Find(CegoField(SYS_RB, SYS_RB_OFFSET));
	    if (pF)
	    {
		memcpy(&offset, pF->getValue().getValue(), sizeof(unsigned));
	    }
	    else
	    {
		Chain msg = Chain("Cannot find attribute ") + Chain(SYS_RB_OFFSET);
		throw Exception(EXLOC, msg);
	    }

	    CegoDataPointer dp(pageId, offset);
	    
	    unsigned long long tid;
	    unsigned long long tastep;
	    CegoTupleState ts;

	    _pTabMng->getTupleInfo(tabSetId, dp, tid, tastep, ts);			

	    opCount++;
	    if ( ts == CegoTupleState::DELETED || ts == CegoTupleState::OBSOLETE )
	    {		
		Chain tableName;
		
		pF = schema.Find(CegoField(SYS_RB, SYS_RB_TABLE));
		if (pF)
		    tableName = Chain((char*)pF->getValue().getValue());
				
		if ( cachedTable != tableName )
		{
		    CegoTableObject oe;
		    _pTabMng->getObject(tabSetId, tableName, CegoObject::TABLE, oe);
		    cachedFvl = oe.getSchema();
		    cachedTable = tableName;
		    cachedIdxList.Empty();
		    cachedBTreeList.Empty();
		    cachedKeyList.Empty();
		    cachedCheckList.Empty();
		    cachedTriggerList.Empty();
		    cachedAliasList.Empty();
		    
		    _pTabMng->getObjectListByTable(tabSetId, cachedTable, cachedIdxList, cachedBTreeList, cachedKeyList, cachedCheckList, cachedTriggerList, cachedAliasList, numInvalid);		    
		}
		
		char* p;
		unsigned len;

		CegoBufferPage bp;
		
		try
		{
		    _pTabMng->claimDataPtrUnlocked(tabSetId, CegoBufferPool::SYNC, dp, p, len, bp);

		    unsigned toff = _qh.skipTupleHeader();

		    char* tp = p + toff;
		    unsigned tlen = len - toff;
		   
		    _qh.decodeFVL(cachedFvl, tp, tlen);

		    // cout << "Deleting data tuple " << dp << "..." << endl;
		    bool isDeleted = _pTabMng->deleteDataTableEntry(tabSetId, cachedTable, CegoObject::TABLE, dp, cachedFvl, cachedIdxList, cachedBTreeList, cachedKeyList, false);
		    if ( isDeleted == false )
		    {
			Chain msg = Chain("Delete data table entry returned false");
			throw Exception(EXLOC, msg);
		    }		    
		}
		catch ( Exception e)
		{   
		    _pTabMng->releaseDataPtrUnlocked(bp);
		    throw Exception(EXLOC, Chain("Cannot delete data table entry"), e);
		}
		_pTabMng->releaseDataPtrUnlocked(bp);		
	    }
	    else
	    {
		_pTabMng->setTupleInfo(tabSetId, dp, 0, 0, COMMITTED);
	    }
	    // set the rb datapointer to the current tid
	    // in case of a crash recovery, this tuple is ignored by the object cursor
	    // so we avoid a double commit

	    _pTabMng->setTupleInfo(tabSetId, rbdp, tid, 0, COMMITTED);
	    
	    moreTuple = _pTabMng->getNextTuple(pOC, schema, rbdp);
	}
    }
    catch ( Exception e )
    {
	pOC->abort();
	delete pOC;

	Chain msg;
	e.pop(msg);
	
	throw Exception(EXLOC, Chain("Cannot commit transaction : ") + msg);
    }
    
    pOC->abort();
    delete pOC;

    return opCount;
}

unsigned long long CegoTransactionManager::doRollback(unsigned tabSetId, const Chain& rbo)
{
    unsigned long long opCount = 0;
    unsigned long long recLock = 0;
    CegoObjectCursor *pOC = 0;

    try
    {
	pOC = _pTabMng->getObjectCursor(tabSetId, rbo, rbo, CegoObject::RBSEG);
		
	ListT<CegoField> schema = _rbcatSchema;
	
	// step 1 ( tid setting )
	    
	CegoDataPointer rbdp;
	bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, rbdp);
	
	Chain cachedTable;
	ListT<CegoField> cachedFvl;
	ListT<CegoTableObject> cachedIdxList;
	ListT<CegoBTreeObject> cachedBTreeList;
	ListT<CegoKeyObject> cachedKeyList;
	ListT<CegoCheckObject> cachedCheckList;
	ListT<CegoTriggerObject> cachedTriggerList;
	ListT<CegoAliasObject> cachedAliasList;
	
	unsigned numInvalid;

	while (moreTuple)
	{
	    PageIdType pageId;
	    unsigned offset;
	    Chain tableName;
	    
	    CegoField *pF = schema.Find(CegoField(SYS_RB, SYS_RB_PAGEID));
	    if (pF)
	    {
		memcpy(&pageId, pF->getValue().getValue(), sizeof(PageIdType));
	    }
	    else
	    {
		Chain msg = Chain("Cannot find attribute ") + Chain(SYS_RB_PAGEID);
		throw Exception(EXLOC, msg);
	    }

	    pF = schema.Find(CegoField(SYS_RB, SYS_RB_OFFSET));
	    if (pF)
	    {
		memcpy(&offset, pF->getValue().getValue(), sizeof(unsigned));
	    }
	    else
	    {
		Chain msg = Chain("Cannot find attribute ") + Chain(SYS_RB_OFFSET);
		throw Exception(EXLOC, msg);
	    }
	    
	    pF = schema.Find(CegoField(SYS_RB, SYS_RB_TABLE));
	    if (pF)
	    {
		tableName = Chain((char*)pF->getValue().getValue());
	    }
	    else
	    {
		Chain msg = Chain("Cannot find attribute ") + Chain(SYS_RB_TABLE);
		throw Exception(EXLOC, msg);
	    }
	    
	    CegoDataPointer dp(pageId, offset);
	    
	    unsigned long long tid;
	    unsigned long long tastep;
	    CegoTupleState ts;

	    recLock = _pTabMng->getLockHandler()->lockRecord(dp, CegoLockHandler::WRITE);

	    _pTabMng->getTupleInfo(tabSetId, dp, tid, tastep, ts);			
	    _pTabMng->setTupleInfo(tabSetId, dp, 0, 0, COMMITTED);

	    _pTabMng->getLockHandler()->unlockRecord(recLock);
	    recLock = 0;
	    
	    opCount++;
	    
	    if ( ts == CegoTupleState::INSERTED || ts == CegoTupleState::OBSOLETE ) 
	    {		
		if ( cachedTable != tableName )
		{
		    CegoTableObject oe;
		    _pTabMng->getObject(tabSetId, tableName, CegoObject::TABLE, oe);
		    cachedFvl = oe.getSchema();
		    cachedTable = tableName;
		    cachedIdxList.Empty();
		    cachedBTreeList.Empty();
		    cachedKeyList.Empty();
		    cachedCheckList.Empty();
		    cachedTriggerList.Empty();
		    cachedAliasList.Empty();
		    
		    _pTabMng->getObjectListByTable(tabSetId, cachedTable, cachedIdxList, cachedBTreeList, cachedKeyList, cachedCheckList, cachedTriggerList, cachedAliasList, numInvalid);		    
		}
		
		char* p;
		unsigned len;

		CegoBufferPage bp;
		
		try
		{
		    _pTabMng->claimDataPtrUnlocked(tabSetId, CegoBufferPool::SYNC, dp, p, len, bp);
		    
		    unsigned toff = _qh.skipTupleHeader();

		    char* tp = p + toff;
		    unsigned tlen = len - toff;
		    
		    _qh.decodeFVL(cachedFvl, tp, tlen);

		    _pTabMng->deleteDataTableEntry(tabSetId, tableName, CegoObject::TABLE, dp, cachedFvl, cachedIdxList, cachedBTreeList, cachedKeyList, false, true);		    
		}
		catch ( Exception e)
		{   
		    _pTabMng->releaseDataPtrUnlocked(bp);

		    throw Exception(EXLOC, "Cannot delete data table entry", e);
		}
		_pTabMng->releaseDataPtrUnlocked(bp);		
	    }

	    // set the rb datapointer to the current tid
	    // in case of a crash recovery, this tuple is ignored by the object cursor
	    // so we avoid a double rollback

	    _pTabMng->setTupleInfo(tabSetId, rbdp, tid, 0, COMMITTED);

	    moreTuple = _pTabMng->getNextTuple(pOC, schema, rbdp);
	}
    }
    catch ( Exception e )
    {
	if ( recLock )
	    _pTabMng->getLockHandler()->unlockRecord(recLock);
	
	if ( pOC )
	{
	    pOC->abort();
	    delete pOC;
	}
	throw Exception(EXLOC, Chain("Cannot rollback transaction"), e);
    }
    
    pOC->abort();
    delete pOC;
	        
    return opCount;
}

void CegoTransactionManager::getTransactionInfo(unsigned tabSetId, const Chain& rbo, unsigned& numop)
{    
    CegoObjectCursor *pOC;

    numop=0;

    if ( _pTabMng->objectExists(tabSetId, rbo, CegoObject::RBSEG) )
    {
	pOC = _pTabMng->getObjectCursor(tabSetId, rbo, rbo, CegoObject::RBSEG);

	try
	{	    
	    ListT<CegoField> schema = _rbcatSchema;
	    
	    CegoDataPointer dp;
	    bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, dp);
	    
	    while (moreTuple)
	    {	       		
		numop++;
		moreTuple = _pTabMng->getNextTuple(pOC, schema, dp);
	    }
	}
	catch ( Exception e )
	{
	    pOC->abort();
	    delete pOC;

	    throw Exception(EXLOC, Chain("Cannot get transaction info"), e);
	}
	
	pOC->abort();
	delete pOC;
    }
}

void CegoTransactionManager::recordUpdate(unsigned tabSetId, unsigned long long tid, const CegoDataPointer& dp )
{
    TAEntry *pUDE = _udList.Find( TAEntry(tid));
    
    if ( pUDE == 0 )
    {
	Chain udTable = Chain(SYS_UPDTAB) + Chain(tid);

	CegoBufferPage bp;
	CegoTableObject oe;
	if ( _pTabMng->objectExists(tabSetId, udTable, CegoObject::RBSEG) == false )
	{
	    oe = _pTabMng->createDataTable(tabSetId, udTable, CegoObject::RBSEG, _updSchema);
	}

	TAEntry ude(tid, oe);

	_udList.Insert(ude);
	pUDE = _udList.Find( TAEntry(tid));
    }

    PageIdType pageId = dp.getPageId();

    // we use type int here to satisfy CegoFieldValue INT_TYPE
    int offset = (int)dp.getOffset();

    CegoFieldValue fv1(PAGEID_TYPE, &pageId, sizeof(PageIdType));
    CegoField f1(CegoField(SYS_UPDTAB, SYS_UPDTAB, SYS_UPDTAB_PAGEID, PAGEID_TYPE, sizeof(PageIdType), 0, fv1, true, 1));	

    CegoFieldValue fv2(INT_TYPE, &offset, sizeof(int));
    CegoField f2(CegoField(SYS_UPDTAB, SYS_UPDTAB, SYS_UPDTAB_OFFSET, INT_TYPE, sizeof(int), 0, fv2, true, 2));

    ListT<CegoField> fl;
    fl.Insert(f1);
    fl.Insert(f2);

    unsigned long long rbtid = 0;
    unsigned long long rbtastep = 0;
    CegoTupleState ts = COMMITTED;

    char *pBufBase = 0;
    unsigned buflen = 0;

    try
    {
	unsigned encLen = _qh.encodeFVL(rbtid, rbtastep, ts, fl, pBufBase, buflen);    	
	CegoDataPointer dp = _pTabMng->insertData(pUDE->getTableObject(), pBufBase, encLen, true);
    }
    catch ( Exception e )
    {
	if ( pBufBase )
	    free (pBufBase);	
	throw Exception(EXLOC, Chain("Cannot record update"), e);
    }
    
    free (pBufBase);   
}

/* 
   the commitUpdate method completes a transaction oriented update operation, where a index 
   was used to find matching tuples.
   In this case, the ( virgin ) index still has to expand for the updated tuples
*/

void CegoTransactionManager::commitUpdate(unsigned tabSetId, const Chain& idxName, const CegoObject::ObjectType idxType, unsigned long long tid, bool doAppend)
{
    CegoObjectCursor *pOC = 0;
    CegoBufferPage bp;
    CegoBufferPage cbp;
    bool isFixed = false;

    try
    {
	TAEntry *pUDE = _udList.Find( TAEntry(tid) );
       
	if ( pUDE == 0 )
	{	    
	    // no update entry found. It seems, no 
	    // modifying operations have been take place, so we can return immediately
	    return;
	}

	Chain udTable = pUDE->getTableObject().getName();

	if ( idxType  == CegoObject::AVLTREE 
	     || idxType == CegoObject::UAVLTREE 
	     || idxType == CegoObject::PAVLTREE )
	{	    
	    CegoTableObject ioe;
	    _pTabMng->getObjectWithFix(tabSetId, idxName, idxType, ioe, bp);			
	    isFixed = true;
	    
	    pOC = _pTabMng->getObjectCursor(tabSetId, udTable, udTable, CegoObject::RBSEG);
	       
	    ListT<CegoField> schema = _updSchema;
	    
	    CegoDataPointer updp;
	    bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, updp);
	    
	    while ( moreTuple)
	    {
		PageIdType pageId;

		// we leave offset as int to satisfy CegoFieldValue INT type
		int offset;
		
		CegoField *pF = schema.Find(CegoField(SYS_UPDTAB, SYS_UPDTAB_PAGEID));
		if (pF)
		{
		    pageId = *(PageIdType*)pF->getValue().getValue();
		}
		else
		{
		    Chain msg = Chain("Cannot find attribute ") + Chain(SYS_UPDTAB_PAGEID);
		    throw Exception(EXLOC, msg);
		}
	    		
		pF = schema.Find(CegoField(SYS_UPDTAB, SYS_UPDTAB_OFFSET));
		if (pF)
		{
		    offset = *(int*)pF->getValue().getValue();
		}
		else
		{
		    Chain msg = Chain("Cannot find attribute ") + Chain(SYS_UPDTAB_OFFSET);
		    throw Exception(EXLOC, msg);
		}

		CegoDataPointer dp(pageId, (unsigned)offset);
		
		ListT<CegoField> fvl = ioe.getSchema();
		
		char* p;
		unsigned len;
		
		CegoDataPointer sysEntry(bp.getPageId(), bp.getEntryPos());
				
		_pTabMng->claimDataPtrUnlocked(tabSetId, CegoBufferPool::SYNC, dp, p, len, cbp);
	      		
		unsigned toff = _qh.skipTupleHeader();

		char* tp = p + toff;
		unsigned tlen = len - toff;

		_qh.decodeFVL(fvl, tp, tlen);
		
		char ip[TABMNG_MAXINDEXVALUE];
		unsigned ilen = TABMNG_MAXINDEXVALUE;
		unsigned idxLen;
		CegoDataPointer ritp;
		
		_pTabMng->extractIndexValue(fvl, ioe.getSchema(), ip, ilen, idxLen);
		
		CegoAVLIndexManager idxMng(_pTabMng);
		
		idxMng.insertNativeIndexTable(ioe, sysEntry, dp, ip, idxLen, tid, doAppend, ritp);

		_pTabMng->releaseDataPtrUnlocked(cbp);
		cbp = CegoBufferPage();
				
		moreTuple = _pTabMng->getNextTuple(pOC, schema, updp);
	    }

	    delete pOC;
	    pOC = 0;
	    
	    // now we can release the index root page
	    _pTabMng->getDBMng()->bufferUnfix(bp, true, _pTabMng->getLockHandler());
	    isFixed = false;

	    _pTabMng->removeObject(tabSetId, udTable, CegoObject::RBSEG);
	    
	    // release the update commit object
	    _udList.Remove(TAEntry(tid));	    
	}
	else if ( idxType == CegoObject::BTREE 
		  || idxType == CegoObject::UBTREE 
		  || idxType == CegoObject::PBTREE )
	{
	    // we have to keep the index object in buffer pool 
	    CegoBTreeObject boe;
	    _pTabMng->getObjectWithFix(tabSetId, idxName, idxType, boe, bp);
	    isFixed = true;
	    
	    CegoDataPointer sysEntry(bp.getPageId(), bp.getEntryPos());
	    
	    CegoBTreeManager btreeMng(_pTabMng, &boe);
	    
	    pOC = _pTabMng->getObjectCursor(tabSetId, udTable, udTable, CegoObject::RBSEG);
	    	    
	    ListT<CegoField> schema = _updSchema;
	    
	    CegoDataPointer updp;
	    bool moreTuple = _pTabMng->getFirstTuple(pOC, schema, updp);
	    
	    while ( moreTuple)
	    {
		PageIdType pageId;
		// we leave offset as int to satisfy CegoFieldValue INT type
		int offset;
		
		CegoField *pF = schema.Find(CegoField(SYS_UPDTAB, SYS_UPDTAB_PAGEID));
		if (pF)
		{
		    pageId = *(PageIdType*)pF->getValue().getValue();
		}
		else
		{
		    Chain msg = Chain("Cannot find attribute ") + Chain(SYS_UPDTAB_PAGEID);
		    throw Exception(EXLOC, msg);
		}

		pF = schema.Find(CegoField(SYS_UPDTAB, SYS_UPDTAB_OFFSET));
		if (pF)
		{
		    offset = *(int*)pF->getValue().getValue();
		}
		else
		{
		    Chain msg = Chain("Cannot find attribute ") + Chain(SYS_UPDTAB_OFFSET);
		    throw Exception(EXLOC, msg);
		}

		CegoDataPointer dp(pageId, (unsigned)offset);
		
		ListT<CegoField> fvl = boe.getSchema();
		
		char* p;
		unsigned len;	       
		
		_pTabMng->claimDataPtrUnlocked(tabSetId, CegoBufferPool::SYNC, dp, p, len, cbp);

		unsigned toff = _qh.skipTupleHeader();

		char* tp = p + toff;
		unsigned tlen = len - toff;

		_qh.decodeFVL(fvl, tp, tlen);

		CegoBTreeValue iv;
		iv.valueFromSchema(fvl, boe.getSchema());
	       		
		btreeMng.insertBTree(dp,
				     iv,
				     tid);
			
		_pTabMng->releaseDataPtrUnlocked(cbp, true);
		cbp = CegoBufferPage();
			       
		moreTuple = _pTabMng->getNextTuple(pOC, schema, updp);
	    }

	    delete pOC;
	    pOC = 0;

	    // write btree sysentry 
	    btreeMng.commit(sysEntry);
	    
	    // now we can release the index root page
	    _pTabMng->getDBMng()->bufferUnfix(bp, true, _pTabMng->getLockHandler());
	    isFixed = false;

	    _pTabMng->removeObject(tabSetId, udTable, CegoObject::RBSEG);
	    
	    // release the update commit object
	    _udList.Remove(TAEntry(tid));	        
	}
    }
    catch ( Exception e )
    {
	_pTabMng->releaseDataPtrUnlocked(cbp);
	       
	if ( isFixed )
	{
	    _pTabMng->getDBMng()->bufferUnfix(bp, true, _pTabMng->getLockHandler());
	}	
	
	if ( pOC )
	{
	    pOC->abort();
	    delete pOC;
	}
	throw Exception(EXLOC, Chain("Cannot commit update"), e);
    }

    if ( pOC )
    {
	pOC->abort();
	delete pOC;
    }
}

void CegoTransactionManager::abortUpdate(unsigned tabSetId, unsigned long long tid)
{    
    TAEntry *pUDE = _udList.Find( TAEntry(tid) );
    
    if ( pUDE == 0 )
    {	    
	return;
    }

    Chain udTable = pUDE->getTableObject().getName();
    _pTabMng->removeObject(tabSetId, udTable, CegoObject::RBSEG);		    
    _udList.Remove(TAEntry(tid));	        
}
