///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoQuerryCursor.cc
// -------------------
// Cego query cursor class implementation
//      
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2026 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoQueryCursor
// 
// Description: Query cursor for high level data access
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// LFC INCLUDES
#include <lfcbase/Net.h>

// CEGO INCLUDES
#include "CegoQueryCursor.h"
#include "CegoQueryManager.h"
#include "CegoSelect.h"
#include "CegoTableObject.h"
#include "CegoJoinObject.h"
#include "CegoXMLdef.h"

CegoQueryCursor::CegoQueryCursor()
{
}

CegoQueryCursor::CegoQueryCursor(CegoQueryManager* pGTM, CegoContentObject *pCO, CegoProcBlock *pBlock)
{
    _moreTuple = false;
    
    _pSelect = 0;
    _pGTM = pGTM;

    _cursorObjectUsed = false;
    
    _tableName = pCO->getTabName();
    _tableAlias = pCO->getName();
    _tabSetId = pCO->getTabSetId();

    _pBlock = pBlock;
    
    _pDBMng = _pGTM->getDBMng(); 

    _useCache = false;
    _pCache = _pDBMng->getTableCache(_tabSetId);
    _isCached = false;
    _pCacheArray = 0;
    _pCacheList = 0;
    _pCO = pCO;

    _pTCTab = 0;
    _pTO = 0;
    _pTCLeft = 0;
    _pTCRight = 0;
    _pTC = 0;
    _pC = 0;

    _pFLA = 0;
    _pFLAmap = 0;

    _pPropPred = 0;
    _pJoinPred = 0;
    
    _idxMatch = CegoAttrCond::INAPP;

    checkType();
    
    _modId = _pDBMng->getModId("CegoQueryCursor");
}

CegoQueryCursor::~CegoQueryCursor()
{
    try
    {
	finishCaching();
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	_pDBMng->log(_modId, Logger::NOTICE, Chain("Cannot finish caching ( ignored ) : " + msg ));
    }
    
    if ( _pTC )
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Releasing global table cursor for ") + _tableName);
#endif

	delete _pTC;
    }
    if ( _pSelect )
    {
	delete _pSelect;			
    }
    if ( _pCO->getType() == CegoObject::JOIN )
    {
#ifdef CGDEBUG
	CegoJoinObject *pJCO = (CegoJoinObject*)_pCO;
	CegoContentObject *pCOLeft = pJCO->getLeftObject();
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Releasing global table cursor for ") + pCOLeft->getTabName());
#endif	
	delete _pTCLeft;
	
#ifdef CGDEBUG
	CegoContentObject *pCORight = pJCO->getRightObject();
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Releasing global table cursor for ") + pCORight->getTabName());
#endif	
	delete _pTCRight;		
    }

    if ( _pCO->getType() == CegoObject::ALIAS )
    {
	if ( _pTCTab )
	    delete _pTCTab;
	if ( _pTO )
	    delete _pTO;
	if ( _pFLAmap )
	    delete _pFLAmap;
    }
    if ( _pC )
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Releasing object cursor for ") + _tableName);
#endif
	delete _pC;
    }

    if ( _pJoinPred )
    {
	delete _pJoinPred;
    }
	    
    try
    {
	unuseCursorObject();
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	_pDBMng->log(_modId, Logger::NOTICE, Chain("Cannot unuse cursor object  ( ignored ) : " + msg ));
    }
    
    if ( _pCacheList )
    {
	delete _pCacheList;
    }
	
    if ( _pCacheArray && _pCache )
    {
	_pCache->releaseEntry(_tableName);
	_pCacheArray=0;
	_isCached = true;
    }
	
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Destructor for QueryCursor finished"));
#endif
}

void CegoQueryCursor::useCursorObject()
{
    if ( _cursorObjectUsed == false )
    {
	if ( _pCO->getType() == CegoObject::VIEW )
	{
	    _pDBMng->useObject(_tabSetId, _tableName, CegoObject::VIEW, CegoDatabaseManager::SHARED, _pGTM);
	}
	else if ( _pCO->getType() == CegoObject::TABLE
		  || _pCO->getType() == CegoObject::ALIAS )
	{
	    _pDBMng->useObject(_tabSetId, _tableName, CegoObject::TABLE, CegoDatabaseManager::SHARED, _pGTM);
	}
	else if ( _pCO->getType() == CegoObject::JOIN )
	{
	    CegoJoinObject *pJCO = (CegoJoinObject*)_pCO;
	    CegoContentObject *pCOLeft = pJCO->getLeftObject();
	    CegoContentObject *pCORight = pJCO->getRightObject();

	    if ( pCOLeft->getType() == CegoObject::VIEW || pCOLeft->getType() == CegoObject::TABLE )
		_pDBMng->useObject(pCOLeft->getTabSetId(), pCOLeft->getTabName(), pCOLeft->getType(), CegoDatabaseManager::SHARED, _pGTM);
	    if ( pCORight->getType() == CegoObject::VIEW || pCORight->getType() == CegoObject::TABLE )
		_pDBMng->useObject(pCORight->getTabSetId(), pCORight->getTabName(), pCORight->getType(), CegoDatabaseManager::SHARED, _pGTM);
	}

	_cursorObjectUsed = true;
    }
}

void CegoQueryCursor::unuseCursorObject()
{
    if ( _cursorObjectUsed == true )
    {
	if ( _pCO->getType() == CegoObject::VIEW )
	{
	    _pDBMng->unuseObject(_tabSetId, _tableName, CegoObject::VIEW);	
	}
	else if ( _pCO->getType() == CegoObject::TABLE
		  || _pCO->getType() == CegoObject::ALIAS )
	{
	    _pDBMng->unuseObject(_tabSetId, _tableName, CegoObject::TABLE);	
	}
	else if ( _pCO->getType() == CegoObject::JOIN )
	{	
	    CegoJoinObject *pJCO = (CegoJoinObject*)_pCO;
	    CegoContentObject *pCOLeft = pJCO->getLeftObject();
	    CegoContentObject *pCORight = pJCO->getRightObject();

	    if ( pCOLeft->getType() == CegoObject::VIEW || pCOLeft->getType() == CegoObject::TABLE )
		_pDBMng->unuseObject(pCOLeft->getTabSetId(), pCOLeft->getTabName(), pCOLeft->getType());	
	    if ( pCORight->getType() == CegoObject::VIEW || pCORight->getType() == CegoObject::TABLE )
		_pDBMng->unuseObject(pCORight->getTabSetId(), pCORight->getTabName(), pCORight->getType());	
	}
	_cursorObjectUsed = false;
    }
}

void CegoQueryCursor::checkType()
{
    useCursorObject();
    
    if ( _pCO->getType() == CegoObject::VIEW )
    {
	
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("View ") + _tableName + Chain(" detected as the cursor source"));
#endif

	CegoView *pView;

	try
	{
	    pView = _pGTM->getView(_tabSetId, _tableName);
	}
	catch ( Exception e )
	{
	    _pDBMng->unuseObject(_tabSetId, _tableName, CegoObject::VIEW);
	    throw Exception(EXLOC, Chain("Cannot get view object ") + _tableName, e);
	}

	// since a view might be used several times inside a single query, we have to use a clone	
	_pSelect = pView->getSelect()->clone(false);
	_pSelect->setProcBlock(_pBlock);
	_pSelect->cleanUp();
    }
    else if ( _pCO->getType() == CegoObject::ALIAS )
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Alias ") + _tableAlias + Chain(" detected as the cursor source"));
#endif	
	// Chain tableSet = _pDBMng->getTabSetName(_tabSetId);
	_pTO = new CegoTableObject();
	_pGTM->getObject(_tabSetId, _tableName, CegoObject::TABLE, *_pTO);

	_pTCTab = new CegoQueryCursor(_pGTM, _pTO, _pBlock);	
    }
    else if ( _pCO->getType() == CegoObject::TABLE )
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Table ") + _tableName + Chain(" detected as the cursor source"));
#endif
	//Chain tableSet = _pDBMng->getTabSetName(_tabSetId);
	
	CegoTableObject oe;
	_pGTM->getObject(_tabSetId, _tableName, CegoObject::TABLE, oe);
	
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Native Table ") + _tableName + Chain(" detected as the cursor source"));
#endif
	_pTC = new CegoTableCursor(_pGTM, _tabSetId, _tableName, false);	
	
	_cacheSchema = oe.getSchema();
    }
    else if ( _pCO->getType() == CegoObject::JOIN )
    {	
	CegoJoinObject *pJCO = (CegoJoinObject*)_pCO;
	CegoContentObject *pCOLeft = pJCO->getLeftObject();
	CegoContentObject *pCORight = pJCO->getRightObject();
	
	_pTCLeft = new CegoQueryCursor(_pGTM, pCOLeft, _pBlock);
	_pTCRight = new CegoQueryCursor(_pGTM, pCORight, _pBlock);
    }

    // we have to release object here to avoid dead locks
    unuseCursorObject();    
}

void CegoQueryCursor::querySetup(ListT<CegoField>** pFLA, CegoPredicate* pPred)
{
    
    _isFirst=true;
    _doEval = false;

    if ( _pJoinPred )
    {
	delete _pJoinPred;
	_pJoinPred = 0;
    }
    
    if (  pPred &&  ( _pCO->getType() == CegoObject::TABLE || _pCO->getType() == CegoObject::VIEW  || _pCO->getType() == CegoObject::ALIAS)  )
    {
	
	_pPropPred = pPred->getReduced(_tableAlias);
	
	_pJoinPred = _pPropPred;
    }
    else if ( pPred && _pCO->getType() == CegoObject::JOIN )
    {
		
	CegoJoinObject *pJO = (CegoJoinObject*)_pCO;

	// for inner joins, we propagate the predicate
	if ( pJO->getJoinType() == CegoJoinObject::INNER )
	{	    
	    _pPropPred = pPred;
	}

	Chain leftAlias = pJO->getLeftObject()->getTabAlias();
	
	ListT<Chain> aliasList;
	if ( pJO->getLeftObject()->getType() != CegoObject::JOIN )
	    aliasList.Insert(leftAlias);
	else
	{
	    ListT<Chain> subList = pJO->getSubAliasList();
	    aliasList += subList;
	}
		
	Chain rightAlias = pJO->getRightObject()->getTabAlias();
	if ( pJO->getRightObject()->getType() != CegoObject::JOIN )
	    aliasList.Insert(rightAlias);
	
	// cout << "1 Setting up cursor " << _tableAlias << " pred for " << leftAlias << " and " << rightAlias << endl;
	_pJoinPred = pPred->getReduced(aliasList);
	    
    }
    else
    {	
	_pPropPred = 0;
	_pJoinPred = 0;
    }


#ifdef CGDEBUG
    Chain predCond;
    if ( _pJoinPred )
	predCond = _pJoinPred->toChain(0);
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Setting empty condition and master condition <") + predCond + Chain("> for ") + _tableAlias);	
#endif


    // if could be reduced, setup propagated predicate to checked
    if ( pPred )
	if ( _pJoinPred )
	    pPred->setChecked(true);
    
    _idxMatch = CegoAttrCond::INAPP;
    _isAttrCondValid = false;
    _pFLA = pFLA;
    
    if ( _pCO->getType() == CegoObject::VIEW )
    {
	_pSelect->setTabSetId(_tabSetId);
	CegoAttrCond noCond;
	
	_pSelect->setViewCond(_pCO->getTabAlias(), noCond, _pPropPred, pFLA);
	_pSelect->prepare();
	_pSelect->checkValidRef();
    }
    else if ( _pCO->getType() == CegoObject::ALIAS )
    {
	_pTCTab->querySetup(_pFLA, _pPropPred);
    }
    else if ( _pCO->getType() == CegoObject::TABLE )
    {	
	CegoAttrCond attrCond; // empty cond
	_pTC->setup(attrCond);
	
	if ( _pCache )
	{
	    _useCache = true;
	    
	    finishCaching();

	    if ( _pCache->matchEntry(_tableName) )
	    {
		_pCacheArray = _pCache->claimEntry( _tableName, _cacheRows, _cacheCols);
		
		if ( _pCacheArray )
		{
		    _isCached = true;
		}
		else
		{
		    _isCached = false;
		    _cacheEntrySize = 0;
		    _pCacheList = new ListT< ListT< CegoFieldValue > >;
		}
	    }
	}	
    }
    else if ( _pCO->getType() == CegoObject::SYSTEM )
    {
	sysSetup();
    }
    else if ( _pCO->getType() == CegoObject::JOIN )
    {

	CegoAttrCond attrCond;
	joinSetup(attrCond);
    }
}

void CegoQueryCursor::querySetup( CegoAttrCond& attrCond, ListT<CegoField>** pFLA, CegoPredicate* pPred)
{    
    if ( attrCond.numComp() == 0 )
    {
	return querySetup(pFLA, pPred);	
    }
    
    if ( _pJoinPred )
    {
	delete _pJoinPred;
	_pJoinPred = 0;
    }

    _isAttrCondValid = false;
    _idxMatch = CegoAttrCond::INAPP;
    _isFirst=true;
    _doEval = false;
    
    if (  pPred &&  ( _pCO->getType() == CegoObject::TABLE || _pCO->getType() == CegoObject::VIEW  || _pCO->getType() == CegoObject::ALIAS)  )
    {

	_pPropPred = pPred->getReduced(_tableAlias);
	//if ( _pPropPred )
	//     cout << " => " << _pPropPred->toChain(0) << endl;
	_pJoinPred = _pPropPred;
    }
    else if ( pPred && _pCO->getType() == CegoObject::JOIN )
    {

	CegoJoinObject *pJO = (CegoJoinObject*)_pCO;
	
	// for inner joins, we propagate the predicate

	if ( pJO->getJoinType() == CegoJoinObject::INNER )
	{	    
	    _pPropPred = pPred;
	}

	Chain leftAlias = pJO->getLeftObject()->getTabAlias();
	
	ListT<Chain> aliasList;
	if ( pJO->getLeftObject()->getType() != CegoObject::JOIN )
	    aliasList.Insert(leftAlias);
	
	Chain rightAlias = pJO->getRightObject()->getTabAlias();
	if ( pJO->getRightObject()->getType() != CegoObject::JOIN )
		aliasList.Insert(rightAlias);
	
	// cout << "2 Setting up cursor " << _tableAlias << " pred for " << leftAlias << " and " << rightAlias << endl;
	_pJoinPred = pPred->getReduced(aliasList);
		
    }
    else
    {
	_pPropPred = 0;
	_pJoinPred = 0;
    }


    // if could be reduced, setup propagated predicate to checked
    if ( pPred )
	if ( _pJoinPred )
	    pPred->setChecked(true);

    // setup field list array
    _pFLA = pFLA;
    
    if ( _pCO->getType() == CegoObject::VIEW )
    {	
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Setting condition for view ")
		     + _tableName
		     + Chain(" [") + _tableAlias + Chain("] to ") + attrCond.toChain());
#endif
	
	// first setup tabSetId with anabling / disabling quey cache
	_pSelect->setTabSetId(_tabSetId);
	
	// we setup view condition with the given attrcond
	// if cannot be setup completely ( aggregation in expression ),
	// we force a dedicated evaluation

	/*
	if ( _pPred )
	    cout << "2. Setting view cond " << _pPred->toChain(0) << " for " << _tableAlias << endl;
	else
	    cout << "2. No condition for view " << _tableAlias << endl;
	*/
	
	// if ( _pJoinPred )
	//     cout << "JoinPred = " << _pJoinPred->toChain(0) << endl;
	
	if ( _pSelect->setViewCond(_tableAlias, attrCond, _pPropPred, pFLA) )
	    _doEval=false;
	else
	    _doEval=true;

#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Setting cursor condition eval =  ")
		     + Chain(_doEval));
#endif

	_pSelect->prepare();
	_pSelect->checkValidRef();
	_cursorCond = attrCond;
    }
    else if ( _pCO->getType() == CegoObject::ALIAS )
    {
	CegoAliasObject *pAO = (CegoAliasObject*)_pCO;
	CegoAttrCond mapAttrCond = CegoQueryHelper::mapAttrCond(attrCond, pAO);
	
	_pTCTab->querySetup(mapAttrCond, pFLA, _pPropPred);
    }
    else if ( _pCO->getType() == CegoObject::TABLE )
    {

#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Setting table condition ") + attrCond.toChain());	
#endif

	if ( _pCache )
	{
	    // check if a previous cursor trace has to be finished to complete cache 
	    finishCaching();
	}
	
	_useCache = false;
	
	_idxMatch = _pTC->setup(attrCond);
	
	if ( _idxMatch != CegoAttrCond::FULL )
	{
	    
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Setting val to true "));	
#endif
	    
	    _doEval=true;
	    
	    // if index match was inappropriate, all tuples are retrieved from table cursor
	    // In this case, we can use cache
	    if ( _pCache && _idxMatch == CegoAttrCond::INAPP )
	    {
		if ( _pCache->matchEntry(_tableName) )	   
		{
		    _useCache = true;
		    _pCacheArray = _pCache->claimEntry( _tableName, _cacheRows, _cacheCols);
		    
		    if ( _pCacheArray )
		    {
			_isCached = true;
		    }
		    else
		    {
			_isCached = false;
			_cacheEntrySize = 0;
			_pCacheList = new ListT< ListT< CegoFieldValue > >;
		    }
		}
	    }
	}
	_cursorCond = attrCond;       
    }
    else if ( _pCO->getType() == CegoObject::SYSTEM )
    {
	sysSetup();
	_cursorCond = attrCond;
	_doEval = true;	
    }
    else if ( _pCO->getType() == CegoObject::JOIN )
    {
	
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Setting join condition ") + attrCond.toChain());	
#endif
	_cursorCond = attrCond;

	// for inner joins, predicate is evaluated inside subjoin, so no need for evaluation	
	joinSetup(attrCond);
    }
}

bool CegoQueryCursor::nextTuple(ListT<CegoField>** pFLA, unsigned pos, unsigned size)
{
    if ( _pGTM->isAborted() )
    {
	throw Exception(EXLOC, "Query aborted");
    }
    
    useCursorObject();
    
    while ( getTuple(pFLA, pos, size) )
    {	
	/*
	cout << "### " << _tableAlias << " TUPLE = ";
	for ( int i=0; i<size; i++)
	{
	    
	    CegoField *pF = pFLA[pos+i]->First();
	    while ( pF )
	    {
		cout << pF->getTableAlias() << "." << pF->getAttrName() << "=" << pF->getValue() << " ";
		pF = pFLA[pos+i]->Next();
	    }		
	}
	cout << endl;
	*/

	bool evaluated=true;

	
	// first step : if required, evaluate join condition 
	if ( _doEval == true )
	{	    
	    evaluated =  evalCondition(pFLA, pos, size);
	}

	
	// cout << "Eval join pred ? " << endl; 
	// second step : if available, evaluate propagated master predicate
	if ( evaluated && _pJoinPred )
	{
	    evaluated =  _pJoinPred->eval(_pFLA,
					  0,
					  pFLA,
					  0,							       
					  _pBlock);
	}
	
	if ( evaluated )
	{
	    
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Cursor ") + _tableAlias + Chain(" returns tuple"));
#endif
	    return true;
	}    
    }
    return false;
}

bool CegoQueryCursor::evalCondition(ListT<CegoField>** pFLA, unsigned pos, unsigned size)
{
    if ( _cursorCond.numComp() == 0 )
	return true;
    
    bool isAnd = _cursorCond.isAnd();
    
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Starting eval condition for cursorcond ") + _cursorCond.toChain());
#endif	    

    bool validFound = false;

    CegoAttrComp *pAC = _cursorCond.getAttrCompSet().First();
    while ( pAC  )
    {

	validFound =  evalCompare(pAC, pFLA, pos, size);
    
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Eval ") + pAC->toChain() + Chain(" = ") + Chain(validFound));
#endif	    
	
	if ( validFound == false && isAnd == true )
	    return false;
	if ( validFound == true && isAnd == false)
	    return true;
	
	pAC = _cursorCond.getAttrCompSet().Next();
    }    
    
    return validFound;
}

bool CegoQueryCursor::evalCompare(CegoAttrComp *pAC, ListT<CegoField>** pFLA, unsigned pos, unsigned size)
{

    // cout << "Eval compare " << *pAC << endl;	
    // cout << "Condition " << pAC->getTableName() << " . " <<  pAC->getAttrName() << endl;
    
    CegoField *pF = 0;
    
    unsigned i=0;
    while ( pF == 0 && i < size)
    {
	if ( _pCO->getType() == CegoObject::VIEW || _pCO->getType() == CegoObject::TABLE )
	    pF = pFLA[pos+i]->Find( CegoField(_tableAlias, pAC->getAttrName()));
	else
	    pF = pFLA[pos+i]->Find( CegoField(pAC->getTableName(), pAC->getAttrName()) );
	i++;
    }
    
    if ( pF )
    {	    
	if ( pAC->getCompMode() == CegoAttrComp::BTWN )
	{
	    if ( ( (CegoFieldValue)pF->getValue() >= (CegoFieldValue)(pAC->getFieldValue()) 
		   && (CegoFieldValue)pF->getValue() <= (CegoFieldValue)(pAC->getFieldValue2()) ) == false )
		
		return false;	    
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISLIKE )
	{
	    if ( pAC->getMatcher()->match(pF->getValue().valAsChain()) == false)		
		return false;
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISNOTLIKE )
	{
	    if ( pAC->getMatcher()->match(pF->getValue().valAsChain()))
		return false;		
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISNCLIKE )
	{
	    if ( pAC->getMatcher()->match(pF->getValue().valAsChain().toLower()) == false)		
		return false;	    
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISNOTNCLIKE )
	{
	    if ( pAC->getMatcher()->match(pF->getValue().valAsChain().toLower()))
		return false;		
	}
	
	else
	{		
	    switch ( pAC->getComparison() )
	    {	    
	    case EQUAL:
	    {
		// cout << "Eval " << pF->getValue() << " = " << pAC->getFieldValue() << endl;
		if ( ( (CegoFieldValue)pF->getValue() == (CegoFieldValue)(pAC->getFieldValue()) ) == false )		    
		    return false;
		break;
	    }
	    case NOT_EQUAL:
	    {
		if ( ( (CegoFieldValue)pF->getValue() != (CegoFieldValue)(pAC->getFieldValue()) ) == false )		    
		    return false;	    
		break;
	    }
	    case LESS_THAN:
	    {
		if ( ( (CegoFieldValue)pF->getValue() < (CegoFieldValue)(pAC->getFieldValue()) ) == false )		     
		    return false;
		break;
	    }
	    case MORE_THAN: 
	    {
		if ( ( (CegoFieldValue)pF->getValue() > (CegoFieldValue)(pAC->getFieldValue()) ) == false )
		    return false;
		break;
	    }
	    case LESS_EQUAL_THAN:
	    {
		if ( ( (CegoFieldValue)pF->getValue() <= (CegoFieldValue)(pAC->getFieldValue()) ) == false )
		    return false;
		break;
	    }
	    case MORE_EQUAL_THAN:
	    {
		if ( ( (CegoFieldValue)pF->getValue() >= (CegoFieldValue)(pAC->getFieldValue()) ) == false )
		    return false;	    
		break;
	    }
	    }
	}
    }
    else
    {
	if ( pAC->isSetup() == false )
	{
	    Chain msg = Chain("Cannot get value for attribute ") + pAC->getAttrName();
	    throw Exception(EXLOC, msg);
	}
    }
    return true;
}
    

bool CegoQueryCursor::getTuple(ListT<CegoField>** pFLA, unsigned offset, unsigned size)
{
    if ( _pCO->getType() == CegoObject::VIEW )
    {
	try 
	{
	    ListT<CegoField> fvl;
	    _moreTuple = _pSelect->nextTuple(fvl);

	    /*
	    cout << "FVL Size = " << fvl.Size() << endl;

	    CegoField *pX = fvl.First();
	    while ( pX ) 
	    {
		cout << "Alias = " << pX->getTableAlias() << " Attr =" << pX->getAttrName() << " " << pX->getValue() << endl;
		
		pX = fvl.Next();
	    }
	    */
		    
	    if ( _moreTuple )
	    {
		CegoField *pF = pFLA[offset]->First();
		while ( pF ) 
		{
		    // cout << "Checking FLA .. Alias =" << pF->getTableAlias() << " Attr = " << pF->getAttrName() << endl;
		    
		    CegoField *pSF = fvl.First();
		    bool notFound=true;
		    while ( pSF && notFound)
		    {
			if ( pSF->getAttrName() == pF->getAttrName() )
			{
			    pF->setValue(pSF->getValue());
			    notFound=false;
			}
			pSF = fvl.Next();
		    }
		    pF = pFLA[offset]->Next();
		}
	    }
	    return _moreTuple;
	}
	catch ( Exception e )
	{
	    _pSelect->cleanUp();
	    throw Exception(EXLOC, Chain("Cannot get tuple from view"), e);
	}
    }
    else if ( _pCO->getType() == CegoObject::ALIAS )
    {
	CegoAliasObject *pAO = (CegoAliasObject*)_pCO;
	
	if ( _isFirst ) 
	{
	    _pFLAmap = new ListT<CegoField>();
	    CegoQueryHelper::mapFLA(_pFLAmap, pFLA, offset, size, pAO);
	    _isFirst = false;
	}
	
	if ( _pTCTab->nextTuple(&_pFLAmap, 0, 1) )
	{
	    CegoQueryHelper::propFLA(_pFLAmap, pFLA, offset, size, pAO);
	    return true;
	}
	return false;	
    }
    else if ( _pCO->getType() == CegoObject::TABLE )
    {
	if ( _useCache )
	{
	    if ( _isCached )
	    {
		CegoFieldValue** pCacheRow = 0;
		if ( _isFirst ) 
		{
		    _isFirst = false;
		    _cachePos = 0;
		    if ( _cacheRows > 0 )
		    {
			pCacheRow = _pCacheArray[_cachePos];
			_cachePos++;			
		    }			  
		}
		else
		{
		    if ( _cachePos < _cacheRows )
		    {
			pCacheRow = _pCacheArray[_cachePos];
			_cachePos++;
		    }
		}
		
		if ( pCacheRow  )
		{
		    CegoField *pF = pFLA[offset]->First();
		    
		    while ( pF )
		    {
			CegoField *pSF = _cacheSchema.First();
			
			unsigned pos = 0;
			while ( pF && pSF && ( *pF != *pSF ) )
			{				
			    pSF = _cacheSchema.Next();
			    pos++;
			}
			if ( *pF == *pSF )
			{
			    pF->setValue(*pCacheRow[pos]);
			}
			pF = pFLA[offset]->Next();
		    }
		    
		    _moreTuple = true;
		}
		else
		{			
		    if ( _pCacheArray && _pCache )
		    {
			_pCache->releaseEntry( _tableName);
			_pCacheArray = 0;
			_isCached = true;
		    }
		    _moreTuple = false;
		}
		
		return _moreTuple;
	    }
	    else // still not cached, so we have to retrieve and cache it
	    {		    
		CegoDataPointer dp;
		if ( _isFirst )
		{
		    _moreTuple = _pTC->getFirst(_cacheSchema, dp);			
		    _isFirst = false;
		}
		else
		{
		    _moreTuple = _pTC->getNext(_cacheSchema, dp);	    
		}
		
		if ( _moreTuple )
		{
		    // put tuple into cache
		    
		    if ( _pCacheList )
		    {
			ListT<CegoFieldValue> staticFieldList;
			CegoField* pF = _cacheSchema.First();
			while ( pF )
			{			
			    staticFieldList.Insert(pF->getValue().getLocalCopy());			       
			    _cacheEntrySize += pF->getValue().size();
			    pF = _cacheSchema.Next();
			}
			
			if ( _pCache->getMaxSize() > _cacheEntrySize )
			{
			    _pCacheList->Insert(staticFieldList);
			}
			else
			{
			    delete _pCacheList;
			    _pCacheList = 0;
			}
		    }
		    
		    // propagate  tuple to flArray
		    CegoField *pF = pFLA[offset]->First();			
		    while ( pF )
		    {
			CegoField *pSF = _cacheSchema.First();
			while ( pF && pSF && ( *pF != *pSF ) )
			{
			    pSF = _cacheSchema.Next();
			}
			// cout << "Comparing " << pF->getTableAlias() << "." << pF->getAttrName()  << " and " << pSF->getTableAlias() << "." << pSF->getAttrName() << endl;
			if ( *pF == *pSF )
			{
			    // cout << "MATCH" << endl;
			    pF->setValue(pSF->getValue());
			}
			pF = pFLA[offset]->Next();
		    }
		}
		else
		{
		    if ( _pCacheList )
		    {
			// put table cache entry into cache
			_pCache->addEntry( _tableName, _pCacheList );
			_isCached = true;
			delete _pCacheList;
			_pCacheList = 0;
		    }
		}
		return _moreTuple;
	    }
	}
	else // retrieve tuple via native table cursor
	{
	    CegoDataPointer dp;
	    if ( _isFirst )
	    {		
		_moreTuple = _pTC->getFirst(*pFLA[offset], dp);
		_isFirst = false;		  
	    }
	    else
	    {
		_moreTuple = _pTC->getNext(*pFLA[offset], dp);		    		   
	    }
	    return _moreTuple;
	}
    }
    else if ( _pCO->getType() == CegoObject::SYSTEM )
    {
	if ( _pC )
	{
	    CegoDataPointer dp;

	    unsigned len;
	    char *pc;
	    if ( _isFirst )
	    {
		pc = (char*)_pC->getFirst(len, dp);		
		_isFirst=false;
	    }
	    else
	    {
		pc = (char*)_pC->getNext(len, dp);
	    }

	    if ( pc && len > 0 )
	    {
		unsigned long long tid;
		unsigned long long tastep;
		CegoTupleState ts;

		unsigned toff = CegoQueryHelper::decodeTupleHeader(tid, tastep, ts, pc);

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

		CegoQueryHelper::decodeFVL(*pFLA[offset], tp, tlen);
		
		return true;   
	    }
	    else
	    {
		return false;
	    }
	}
	else
	{
	    Chain *pName = 0;
	    if ( _isFirst )
	    {
		_isFirst = false;
		pName = _sysObjList.First();
	    }	
	    else
	    {
		pName = _sysObjList.Next();	    
	    }
	    
	    if ( pName )
	    {
		CegoField *pF = pFLA[offset]->Find(CegoField(_tableAlias, Chain(SYSTAB_NAME_ATTR)));
		if ( pF )		    
		{
		    pF->setValue( CegoFieldValue(VARCHAR_TYPE, *pName));
		}
		
		if ( _tableName == Chain(SYSTAB_TABLE_ID) 
		     || _tableName == Chain(SYSTAB_INDEX_ID)
		     || _tableName == Chain(SYSTAB_BTREE_ID) )
		{	    
		    unsigned pageCount = 0;
		    
		    if ( _tableName == Chain(SYSTAB_TABLE_ID)  )
			pageCount = _pGTM->getPageCount(_tabSetId, *pName, CegoObject::TABLE);
		    else if ( _tableName == Chain(SYSTAB_INDEX_ID)  )
			pageCount = _pGTM->getPageCount(_tabSetId, *pName, CegoObject::AVLTREE);
		    else if ( _tableName == Chain(SYSTAB_BTREE_ID)  )
			pageCount = _pGTM->getPageCount(_tabSetId, *pName, CegoObject::BTREE);

		    CegoField *pF = pFLA[offset]->Find(CegoField(_tableAlias, Chain(SYSTAB_SIZE_ATTR)));
		    if ( pF )		    
		    {
			pF->setValue( CegoFieldValue(INT_TYPE, pageCount));
		    }
		    pF = pFLA[offset]->Find(CegoField(_tableAlias, Chain(SYSTAB_STATUS_ATTR)));
		    if ( pF )
		    {
			if ( pageCount > 0 )
			    pF->setValue( CegoFieldValue(VARCHAR_TYPE, Chain("valid")));
			else
			    pF->setValue( CegoFieldValue(VARCHAR_TYPE, Chain("invalid")));
		    }
		    
		}
		else if ( _tableName == Chain(SYSTAB_VIEW_ID) )
		{
		    Chain status("not compiled");
		    if ( _pGTM->checkCompView(_tabSetId, *pName) )
			status = Chain("compiled");
		    
		    CegoField *pF = pFLA[offset]->Find(CegoField(_tableAlias, Chain(SYSTAB_STATUS_ATTR)));
		    if ( pF )		    
		    {
			pF->setValue( CegoFieldValue(VARCHAR_TYPE, status));
		    }		
		}
		else if ( _tableName == Chain(SYSTAB_PROC_ID) )
		{
		    Chain status("not compiled");
		    if ( _pGTM->checkCompProcedure(_tabSetId, *pName) )
			status = Chain("compiled");
		    
		    CegoField *pF = pFLA[offset]->Find(CegoField(_tableAlias, Chain(SYSTAB_STATUS_ATTR)));
		    if ( pF )		    
		    {
			pF->setValue( CegoFieldValue(VARCHAR_TYPE, status));
		    }				
		}
		_moreTuple = true;	    
	    }
	    else
	    {
		_moreTuple = false;
	    }
	}
	return _moreTuple;
    }
    else if ( _pCO->getType() == CegoObject::JOIN )
    {	
	if ( _isFirst )
	{
	    _moreLeft = true;
	    _moreRight = true;
	}

	CegoJoinObject *pJO = (CegoJoinObject*)_pCO;
	
	if ( pJO->getJoinType() == CegoJoinObject::INNER )
	{   
	    bool isEvaluated = false;
	    
	    while ( isEvaluated == false && _moreLeft )
	    {
		if ( _isFirst )
		{
		    // cout << "OuterCond=" << _outerCond.toChain() << endl;
		    _pTCLeft->querySetup(_outerCond, _pFLA, _pPropPred);
		    		    
		    _moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
		    if ( _moreLeft )
		    {
			// cout << "InnerCond=" << _innerCond.toChain() << endl;
			_innerCond.setup(pFLA, offset);

			_pTCRight->reset();
			
			if ( _isAttrCondValid )
			{
			    _pTCRight->querySetup(_innerCond, _pFLA, _pPropPred);
			}
			else
			{
			    _pTCRight->querySetup(_pFLA, _pPropPred);
			}
			_moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
		    }
		    _isFirst = false;
		}
		else if ( _moreRight )
		{
		    _moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
		    if ( _moreRight == false )
		    {
			_moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
			if ( _moreLeft )
			{
			    _innerCond.setup(pFLA, offset);
			    _pTCRight->reset();

			    if ( _isAttrCondValid )
			    {
				_pTCRight->querySetup(_innerCond, _pFLA, _pPropPred);
			    }
			    else
			    {
				_pTCRight->querySetup(_pFLA, _pPropPred);
			    }
			    _moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
			}
		    }
		}
		else if ( _moreLeft )
		{
		    _moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
		    if ( _moreLeft )
		    {
			_innerCond.setup(pFLA, offset);
			_pTCRight->reset();

			if ( _isAttrCondValid )
			{
			    _pTCRight->querySetup(_innerCond, _pFLA, _pPropPred);
			}
			else
			{
			    _pTCRight->querySetup(_pFLA, _pPropPred);
			}
			_moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
		    }
		}

		
		if ( _moreLeft && _moreRight )
		{
		    if ( _evalPredicate )
		    {		       
			// attr cache is left for performace
			// pJO->getPredDesc()->clearAttrCache();
			
			isEvaluated = pJO->getPredDesc()->eval(0,
							       0,
							       pFLA,
							       offset,
							       _pBlock);
		    }
		    else
		    {			
			isEvaluated = true;
		    }

		  		   
		    if ( isEvaluated && _pJoinPred )
		    {

			isEvaluated = _pJoinPred->eval(_pFLA,
						       0,
						       pFLA,
						       0,							       
						       _pBlock);    			
		    }
		}
				
	    }
	    return _moreLeft && _moreRight;
	}
	else if ( pJO->getJoinType() == CegoJoinObject::LEFTOUTER )
	{
	    if ( _isFirst )
	    {	
		_pTCLeft->querySetup(_outerCond, _pFLA, _pPropPred);	    
		_moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
		
		if ( _moreLeft )
		{
		    _innerCond.setup(pFLA, offset);
		    _pTCRight->reset();
		    
		    if ( _isAttrCondValid )
		    {
			_pTCRight->querySetup(_innerCond, _pFLA, _pPropPred);
		    }
		    else
		    {
			_pTCRight->querySetup(_pFLA, _pPropPred);
		    }
		    
		    nextRight(pFLA, offset, size);
		}
		_isFirst = false;
	    }
	    else if ( _moreRight )
	    {
		nextRight(pFLA, offset, size);
		
		if ( _moreRight == false )
		{
		    _moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
		    if ( _moreLeft )
		    {
			_innerCond.setup(pFLA, offset);
			
			_pTCRight->reset();
			
			if ( _isAttrCondValid )
			{
			    _pTCRight->querySetup(_innerCond, _pFLA, _pPropPred);
			}
			else
			{
			    _pTCRight->querySetup(_pFLA, _pPropPred);
			}

			nextRight(pFLA, offset, size);

		    }
		}		
	    }
	    else if ( _moreLeft )
	    {
		_moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
		if ( _moreLeft )
		{
		    _innerCond.setup(pFLA, offset);
		    _pTCRight->reset();
		    
		    if ( _isAttrCondValid )
		    {
			_pTCRight->querySetup(_innerCond, _pFLA, _pPropPred);
		    }
		    else
		    {
			_pTCRight->querySetup(_pFLA, _pPropPred);
		    }

		    nextRight(pFLA, offset, size);
		}
	    }
	    
	    if ( _moreLeft )
	    {		
		if ( _moreRight == false  )
		{
		    // make right fields null
		    CegoField *pF = pFLA[offset+size-1]->First();
		    while ( pF )
		    {
			CegoFieldValue nullValue;
			pF->setValue(nullValue);
			pF = pFLA[offset+size-1]->Next();
		    }
		}
		return true;
	    }
	    return false;
	}	
	else if ( pJO->getJoinType() == CegoJoinObject::RIGHTOUTER )
	{    
	    if ( _isFirst )
	    {		
		_pTCRight->querySetup(_outerCond, _pFLA, _pPropPred);
		
		_moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
		
		if ( _moreRight )
		{
		    _innerCond.setup(pFLA, offset+size-1);	    
		    _pTCLeft->reset();
		    
		    if ( _isAttrCondValid )
			_pTCLeft->querySetup(_innerCond, _pFLA, _pPropPred);
		    else
			_pTCLeft->querySetup(_pFLA, _pPropPred);

		    nextLeft(pFLA, offset, size);
		}
		_isFirst = false;
	    }
	    else if ( _moreLeft )
	    {
		nextLeft(pFLA, offset, size);
		
		if ( _moreLeft == false )
		{
		    _moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
		    if ( _moreRight )
		    {
			_innerCond.setup(pFLA, offset+size-1);
			
			_pTCLeft->reset();
			
			if ( _isAttrCondValid )
			    _pTCLeft->querySetup(_innerCond, _pFLA, _pPropPred);
			else
			    _pTCLeft->querySetup(_pFLA, _pPropPred);

			nextLeft(pFLA, offset, size);
		    }
		}		
	    }
	    else if ( _moreRight )
	    {
		_moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
		if ( _moreRight )
		{
		    _innerCond.setup(pFLA, offset+size-1);
		    
		    _pTCLeft->reset();
		    
		    if ( _isAttrCondValid )
			_pTCLeft->querySetup(_innerCond, _pFLA, _pPropPred);
		    else
			_pTCLeft->querySetup(_pFLA, _pPropPred);

		    nextLeft(pFLA, offset, size);
		}	   	
	    }

	    if ( _moreRight )
	    {
		if ( _moreLeft == false )
		{
		    for ( unsigned i=0; i<size-1; i++)
		    {
			// make right fields null
			CegoField *pF = pFLA[offset+i]->First();
			while ( pF )
			{
			    CegoFieldValue nullValue;
			    pF->setValue(nullValue);
			    pF = pFLA[offset+i]->Next();
			}
		    }
		}
		return true;
	    }
	    return false;
	}	    
    }

    Chain msg = Chain("Invalid cursor object ") + _pCO->getTabAlias() + Chain(" (") + _pCO->getTabName() + Chain(")");
    throw Exception(EXLOC, msg);
}

void CegoQueryCursor::nextRight(ListT<CegoField>** pFLA, unsigned offset, unsigned size)
{    
    CegoJoinObject *pJO = (CegoJoinObject*)_pCO;
	
    _moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
    
    bool notFound = true;
    while ( ( _evalPredicate || _pJoinPred ) && _moreRight && notFound )
    {
	// attr cache is left for performace
	// pJO->getPredDesc()->clearAttrCache();

	
	bool isValid = true;

	if ( _evalPredicate )
	{
	    isValid =  pJO->getPredDesc()->eval(0,
						0,
						pFLA,
						offset,
						_pBlock);
	}
	
	if ( isValid && _pJoinPred )
	{
	    isValid = _pJoinPred->eval(_pFLA,
				       0,
				       pFLA,
				       0,							       
				       _pBlock);
	}
	
	
	if ( isValid )    
	{
	    notFound = false;
	}
	else
	{
	    _moreRight = _pTCRight->nextTuple(pFLA, offset+size-1, 1);
	}			      
    }
    
}

void CegoQueryCursor::nextLeft(ListT<CegoField>** pFLA, unsigned offset, unsigned size)
{
    CegoJoinObject *pJO = (CegoJoinObject*)_pCO;
    
    _moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
    
    bool notFound = true;
    while ( ( _evalPredicate || _pJoinPred )  && _moreLeft && notFound )
    {
	
	bool isValid =  true;

	if ( _evalPredicate )
	{
	    isValid = pJO->getPredDesc()->eval(0,
					       0,
					       pFLA,
					       offset,
					       _pBlock);
	}

       
	if ( isValid && _pJoinPred )
	{
	    isValid = _pJoinPred->eval(_pFLA,
				       0,
				       pFLA,
				       0,							       
				       _pBlock);	    			    
	}
	

	if ( isValid ) 
	{
	    notFound = false;
	}
	else
	{
	    _moreLeft = _pTCLeft->nextTuple(pFLA, offset, size-1);
	}			      
    }
}

void CegoQueryCursor::finishCaching()
{    
    if ( _useCache && _pCache && _pCacheList && _isCached == false && _pTC && _isFirst == false )
    {	
	CegoDataPointer dp;
	
	while ( _pTC->getNext(_cacheSchema, dp) && _pCacheList )
	{

	    ListT<CegoFieldValue> staticFieldList;
	    CegoField* pF = _cacheSchema.First();
	    while ( pF )
	    {			
		staticFieldList.Insert(pF->getValue().getLocalCopy());			       
		_cacheEntrySize += pF->getValue().size();
		pF = _cacheSchema.Next();
	    }
	    
	    if ( _pCache->getMaxSize() > _cacheEntrySize )
	    {
		_pCacheList->Insert(staticFieldList);
	    }
	    else
	    {
		delete _pCacheList;
		_pCacheList = 0;
	    }
	}
       	    
	if ( _pCacheList )
	{
	    // put table cache entry into cache
	    _pCache->addEntry( _tableName, _pCacheList );
	    _isCached = true;
	    delete _pCacheList;
	    _pCacheList = 0;
	}
    }
}

void CegoQueryCursor::reset()
{
    _isFirst=true;
    _moreTuple = false;

    if ( _pCO->getType() == CegoObject::VIEW )
    {
	// was true
	_pSelect->reset();
    }
    else if ( _pCO->getType() == CegoObject::TABLE )
    {
	if ( _pTC )
	    _pTC->abort();
	
	// we have to check, if cache was created
	if ( _isCached == false && _pCacheList )
	{
	    delete _pCacheList;
	    _pCacheList = 0;
	}
	
	if ( _pCacheArray && _pCache )
	{
	    _pCache->releaseEntry( _tableName);
	    _pCacheArray = 0;
	    _isCached = 0;
	}
    }
    else if ( _pCO->getType() == CegoObject::ALIAS )
    {
	if ( _pTCTab )
	    _pTCTab->reset();
    }
    else if ( _pCO->getType() == CegoObject::JOIN )
    {	
	if ( _pTCLeft )
	    _pTCLeft->reset();
	if ( _pTCRight )
	    _pTCRight->reset();
    }
    else if ( _pCO->getType() == CegoObject::SYSTEM )
    {
	if ( _pC )
	    _pC->reset();
    }

    unuseCursorObject();
}

void CegoQueryCursor::sysSetup()
{    
    // Chain tableSet = _pDBMng->getTabSetName(_tabSetId);	
    if ( _tableName == Chain(SYSTAB_TABLE_ID) )
    {
	_pGTM->getObjectList(_tabSetId, CegoObject::TABLE, _sysObjList);
    }
    else  if ( _tableName == Chain(SYSTAB_PROC_ID) )
    {
	_pGTM->getObjectList(_tabSetId, CegoObject::PROCEDURE, _sysObjList);
    }
    else  if ( _tableName == Chain(SYSTAB_VIEW_ID) )
    {
	_pGTM->getObjectList(_tabSetId, CegoObject::VIEW, _sysObjList);
    }
    else  if ( _tableName == Chain(SYSTAB_INDEX_ID) )
    {
	_pGTM->getObjectList(_tabSetId, CegoObject::AVLTREE, _sysObjList);
    }
    else  if ( _tableName == Chain(SYSTAB_BTREE_ID) )
    {
	_pGTM->getObjectList(_tabSetId, CegoObject::BTREE, _sysObjList);
    }
    else  if ( _tableName == Chain(SYSTAB_KEY_ID) )
    {
	_pGTM->getObjectList(_tabSetId, CegoObject::FKEY, _sysObjList);
    }
    else
    {
	_pC = _pGTM->getObjectCursor(_tabSetId, _tableName, _tableName, CegoObject::SYSTEM);
    }
}

void CegoQueryCursor::joinSetup(const CegoAttrCond& attrCond)
{
    CegoJoinObject *pJO = (CegoJoinObject*)_pCO;
        
    ListT<CegoField> outerSchema;
    ListT<CegoField> innerSchema;
       
    if ( pJO->getJoinType() == CegoJoinObject::INNER || pJO->getJoinType() == CegoJoinObject::LEFTOUTER ) 
    {
	outerSchema = pJO->getLeftObject()->getSchema();
	innerSchema = pJO->getRightObject()->getSchema();

	if ( pJO->getJoinType() == CegoJoinObject::INNER )
	{
	    _outerCond = attrCond.getFilterCond(outerSchema, false);

	    // cout << "AttrCond = " << attrCond.toChain() << endl;
	    _innerCond = attrCond.getFilterCond(innerSchema, false);

	    // cout << "InnerCond = " << _innerCond.toChain() << endl;
	    _doEval = false;
	}
	else
	{	    
	    _outerCond = attrCond.getFilterCond(outerSchema, false);
	    _innerCond = attrCond.getFilterCond(innerSchema, true);

	    // for left outer join, we have to evaluate the where condition outside the join 
	    _doEval = true;
	}    
    }
    else if (  pJO->getJoinType() == CegoJoinObject::RIGHTOUTER )
    {
	innerSchema = pJO->getLeftObject()->getSchema();
        outerSchema = pJO->getRightObject()->getSchema();
	
	_outerCond = attrCond.getFilterCond(outerSchema, false);
	_innerCond = attrCond.getFilterCond(innerSchema, true);

	// for right outer join, we have to evaluate the where condition outside the join 
	_doEval = true;	
    }

    // cout << "Setting up join with " << pJO->getPredDesc()->toChain(0) << endl;

    ListT<CegoPredicate*> conjunctionList;

    CegoQueryHelper::createConjunctionList(pJO->getPredDesc(), &conjunctionList);

    _evalPredicate = false;

    CegoPredicate **pPred = conjunctionList.First();
    while ( pPred )
    {
	CegoAttrCond ac;
	CegoQueryHelper::AttrCondMatch m  = CegoQueryHelper::checkAttrCond(ac, *pPred, innerSchema, &outerSchema, 1, 0);

	if ( m == CegoQueryHelper::COMPLETE )
	{
	    _innerCond = _innerCond + ac;
	}
	else if ( m == CegoQueryHelper::PARTIAL )
	{
	    // for Partial conditions, we have to evaluate in any case
	    _evalPredicate = true;
	    _innerCond = _innerCond + ac;
	}
	else
	{
	    // for inapp conditions, we have to evaluate the whole condition
	    _evalPredicate = true;
	}
	pPred = conjunctionList.Next();
    }

    // cout << "InnerCond = " << _innerCond.toChain() << " eval=" << _evalPredicate << endl;

    _isAttrCondValid = true;    
}

Element* CegoQueryCursor::getPlan()
{
    Element *pCursorPlan = new Element(XML_JOIN_ELEMENT);

    pCursorPlan->setAttribute(XML_TABLENAME_ATTR, _tableName);       
    pCursorPlan->setAttribute(XML_NAME_ATTR, _tableAlias);

    if ( _pCO->getType() == CegoObject::VIEW )
    {
	pCursorPlan->setAttribute(XML_TABLETYPE_ATTR, XML_VIEW_VALUE);

#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Getting view plan for ") + _tableAlias);
#endif

	
	pCursorPlan->addContent( _pSelect->getPlan() );


#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("View plan for ") + _tableAlias + Chain(" retrieved"));
#endif

    }
    else if ( _pCO->getType() == CegoObject::ALIAS )
    {
	if ( _pTCTab )
	{
	    pCursorPlan->setAttribute(XML_TABLETYPE_ATTR, XML_ALIAS_VALUE);
	    pCursorPlan->setAttribute(XML_NAME_ATTR, _pCO->getName());
	    // Chain tableName = _pTCTab->getPlan()->getAttributeValue(XML_TABLENAME_ATTR);
	    // Chain aliasName = _pTCTab->getPlan()->getAttributeValue(XML_NAME_ATTR);
	    // Chain tableType = _pTCTab->getPlan()->getAttributeValue(XML_TABLETYPE_ATTR);
	    // cout << "TableName=" << tableName << endl;
	    // cout << "AliasName=" << aliasName << endl;
	    // cout << "TableType=" << tableType << endl;
	    
	    pCursorPlan->addContent( _pTCTab->getPlan() );
	}
    }
    else if ( _pCO->getType() == CegoObject::TABLE )
    {
	pCursorPlan->setAttribute(XML_TABLETYPE_ATTR, XML_TABLE_VALUE);
	
	Chain joinStrat;
	if ( _idxMatch == CegoAttrCond::FULL)
	{
	    joinStrat = Chain("full index trace on ") + _cursorCond.toChain(); 
	}
	else if( _idxMatch == CegoAttrCond::PART )
	{
	    joinStrat =  Chain("index support on ") + _cursorCond.toChain() + Chain(" using index ") + _pTC->getIndexName(); 
	}
	else
	{	    	    
	    if ( _cursorCond.numComp() > 0 )
	    {
		joinStrat = Chain("full table scan using condition ") + _cursorCond.toChain();		
	    }
	    else
	    {
		joinStrat = Chain("full table scan with no condition");
	    }
	}
		
	if ( _pJoinPred )
	    joinStrat += Chain(" on ") + _tableAlias + Chain(" with master predicate ") + _pJoinPred->toChain(0);
	
	pCursorPlan->setAttribute(XML_JOINSTRAT_ATTR, joinStrat);
	
    }
    else if ( _pCO->getType() == CegoObject::JOIN )
    {
	CegoJoinObject *pJCO = (CegoJoinObject*)_pCO;
	
	if ( pJCO->getJoinType() == CegoJoinObject::INNER )	    
	    pCursorPlan->setAttribute(XML_TABLETYPE_ATTR, XML_INNERJOIN_VALUE);
	else if ( pJCO->getJoinType() == CegoJoinObject::LEFTOUTER)	    
	    pCursorPlan->setAttribute(XML_TABLETYPE_ATTR, XML_LEFTOUTERJOIN_VALUE);
	else if ( pJCO->getJoinType() == CegoJoinObject::RIGHTOUTER)	    
	    pCursorPlan->setAttribute(XML_TABLETYPE_ATTR, XML_RIGHTOUTERJOIN_VALUE);

	Chain joinStrat;
	
	if ( _evalPredicate )
	{
	    CegoJoinObject *pJO = (CegoJoinObject*)_pCO;
	    Chain joinPred = pJO->getPredDesc()->toChain(0);	    
	    joinStrat = Chain("join condition ") + joinPred;
	}
	else
	    joinStrat = Chain("no join condition");
	
	if ( _pJoinPred )
	{
	    joinStrat += Chain(" and master predicate ") + _pJoinPred->toChain(0);
	}
	

	pCursorPlan->setAttribute(XML_JOINSTRAT_ATTR, joinStrat);

	if ( _pTCLeft )
	{
	    _pTCLeft->querySetup(_outerCond, _pFLA, _pPropPred);
	}
	if ( _pTCRight )
	{	    
	    if ( _isAttrCondValid )
	    {
		_pTCRight->querySetup(_innerCond, _pFLA, _pPropPred);
	    }
	    else
	    {
		_pTCRight->querySetup(_pFLA, _pPropPred);
	    }	    
	}
	if ( _pTCLeft )
	    pCursorPlan->addContent( _pTCLeft->getPlan() );
	if ( _pTCRight )
	    pCursorPlan->addContent( _pTCRight->getPlan() );
    }
    else
    {
	delete pCursorPlan;
	throw Exception(EXLOC, Chain("Invalid content type"));		
    }
    

    return pCursorPlan;
}
