///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoSelect.cc
// -------------
// Cego query implementation         
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoSelect
// 
// Description: This class implements the SQL select query algorithm for the Cego database system 
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// LFC INCLUDES
#include <lfcbase/Datetime.h>
#include <lfcbase/SetT.h>
#include <lfcbase/StackT.h>
#include <lfcbase/BigDecimal.h>

// CEGO INCLUDES
#include "CegoSelect.h"
#include "CegoQueryHelper.h"
#include "CegoCaseCond.h"
#include "CegoJoinObject.h"
#include "CegoXMLdef.h"
#include "CegoDatabaseFormater.h"

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

CegoSelect::CegoSelect(char* buf)
{
    _pPred = 0;
    _pNativeOrderList = 0;
    _pOrderList = 0;
    _pGroupList = 0;
    _pHavingPred = 0;
    _pNativeOrderOptList = 0;
    _pOrderOptList = 0;
    _pOrderSpace = 0;
    _pGroupSpace = 0;
    _pGTM = 0;
    _pDBMng = 0;
    _modId = 0;

    unsigned i=0;
    while ( i < TABMNG_MAXJOINLEVEL )
    {
	_joinBuf[i] = 0;
	_pTC[i]=0;
	_attrCondFlag[i] = false;
	_firstTuple[i]=true;
	_attrPred[i]=0;
	_cursorPred[i]=0;
	i++;
    }

    _cacheEnabled = false;
    _pCache = 0;
    _pCacheArray = 0;
    _pCacheList = 0;

    _pBlock = 0;
    _pUnionSelect = 0;

    _checkUnion = false;
    _selectMode = PLAIN;
    _nextAid=0;
    _aggregationCount=0;

    _pOrderCursor=0;
    _pGroupCursor=0;

    _joinLevel = 0;
    _isDistinct = false;
    _pParentJoinBuf = 0;
    _isPrepared = false;
    _rowLimit = 0;
    _isCached = false;
    _extRefCount = 0;


    decode(buf);
}

CegoSelect::CegoSelect(ListT<CegoContentObject*>& coList,
		       ListT<CegoExpr*>& exprList, 
		       CegoPredicate* pPred,
		       ListT<CegoAttrDesc*> *pGroupList,
		       CegoPredicate *pHavingPred,
		       ListT<CegoExpr*> *pOrderList,
		       ListT<CegoOrderNode::Ordering>* pOrderOptList,
		       bool isDistinct,
		       unsigned rowLimit,
		       CegoQueryManager* pGTM)
{
    _coList = coList;
    _exprList = exprList;
    _pPred = pPred;    
    _pGroupList = pGroupList;
    _pHavingPred = pHavingPred;
    _pNativeOrderList = pOrderList;
    _pOrderList = 0;
    _pNativeOrderOptList = pOrderOptList;
    _pOrderOptList = 0;
    _pOrderSpace = 0;
    _pGroupSpace = 0;
    _pGTM = pGTM;
    _cacheEnabled = false;

    _pCache = 0;
    _pCacheArray = 0;
    _pCacheList = 0;

    if ( _pGTM )
    {
	_pDBMng = _pGTM->getDBMng();
	_modId = _pGTM->getDBMng()->getModId("CegoSelect");
    }
    
    unsigned i=0;
    while ( i < TABMNG_MAXJOINLEVEL )
    {
	_joinBuf[i] = 0;
	_pTC[i]=0;
	_attrCondFlag[i] = false;
	_firstTuple[i]=true;
	_attrPred[i]=0;
	_cursorPred[i]=0;	
	i++;
    }
    
    _pBlock = 0;
    _pUnionSelect = 0;

    _checkUnion = false;

    if ( _coList.isEmpty() )
       _selectMode = SIMPLE;
    else
       _selectMode = PLAIN;
    
    _nextAid=0;
    _aggregationCount=0;

    _pOrderCursor=0;
    _pGroupCursor=0;

    _joinLevel = 0;
    _isDistinct = isDistinct;
    _pParentJoinBuf = 0;
    _tabSetId = 0;
    _isPrepared = false;
    _rowLimit = rowLimit;
    _isCached = false;

    _orderingDone=false;
    _groupingDone=false;
    _simpleDone=false;
    
    _extRefCount = 0;
}

CegoSelect::~CegoSelect()
{    
    cleanUp();
    
    if ( _pPred )
	delete _pPred;

    CegoPredicate **pP = _viewConjunctionList.First();
    while ( pP )
    {
	delete (*pP);
	pP = _viewConjunctionList.Next();
    }
    
    unsigned i=0;
    while ( i < TABMNG_MAXJOINLEVEL )
    {
	if ( _pTC[i] )
	    delete _pTC[i];
	if ( _joinBuf[i] )
	    delete _joinBuf[i];
	i++;
    }    

    CegoContentObject **pCO = _coList.First();
    while ( pCO )
    {
	delete (*pCO);
	pCO = _coList.Next();
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr )
    {
	delete (*pExpr);
	pExpr = _exprList.Next();
    }

    if ( _pOrderList )
    {
	if ( _pOrderCursor )
	    delete _pOrderCursor;
	if ( _pOrderSpace )
	{
	    delete _pOrderSpace;	
	    _pOrderSpace = 0;
	}

	CegoExpr **pExpr = _pOrderList->First();
	while ( pExpr )
	{
	    delete (*pExpr);
	    pExpr = _pOrderList->Next();
	}

	delete _pOrderList;
	_pOrderList = 0;	
    }

    if ( _pNativeOrderList )
    {
	CegoExpr **pExpr = _pNativeOrderList->First();
	while ( pExpr )
	{
	    delete (*pExpr);
	    pExpr = _pNativeOrderList->Next();
	}

	delete _pNativeOrderList;
	_pNativeOrderList = 0;	
    }

    if ( _pNativeOrderOptList )
    {
	delete _pNativeOrderOptList;
	_pNativeOrderOptList = 0;	
    }
    
    if ( _pGroupList )
    {
	if ( _pGroupCursor )
	    delete _pGroupCursor;
	if ( _pGroupSpace )
	{
	    delete _pGroupSpace;
	    _pGroupSpace=0;
	}

	CegoAttrDesc **pAttrDesc = _pGroupList->First();
	while ( pAttrDesc )
	{
	    delete (*pAttrDesc);
	    pAttrDesc = _pGroupList->Next();
	}
	
	delete _pGroupList;
	_pGroupList = 0;
    }

    if ( _pHavingPred )
    {     	
	delete _pHavingPred;
    }
    
    if ( _pGTM )
	_pGTM->setAllocatedSortArea(0);

    if ( _pUnionSelect )
    {
	delete _pUnionSelect;
    }

    if ( _pCacheList )
	delete _pCacheList;
}

void CegoSelect::setTableManager(CegoQueryManager* pGTM)
{
    _pGTM = pGTM;
    if ( _pGTM )
    {
	_pDBMng = _pGTM->getDBMng();
	_modId = _pGTM->getDBMng()->getModId("CegoSelect");
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr )
    {
        (*pExpr)->setQueryManager(pGTM);
        pExpr = _exprList.Next();
    }
    
    if ( _pPred )
    {
	_pPred->setQueryManager(pGTM);
    }

    if ( _pUnionSelect )
    {
	_pUnionSelect->setTableManager(pGTM);
    }    
}

void CegoSelect::setTabSetId(unsigned tabSetId)
{
    _tabSetId = tabSetId;

    if ( _pGTM )
    {
	_pCache = _pGTM->getDBMng()->getQueryCache(_tabSetId);
	if ( _pCache )
	{
	    _cacheEnabled = true;
	}
	else
	{
	    _cacheEnabled = false;
	}
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr )
    {
        (*pExpr)->setTabSetId(tabSetId);
        pExpr = _exprList.Next();
    }
    
    if ( _pPred )
    {
	_pPred->setTabSetId(tabSetId);
    }

    if ( _pUnionSelect )
    {
	_pUnionSelect->setTabSetId(tabSetId);
    }    
}

// the cleanup method makes a complete reinitialization of the select statement to the state
// after the instance has been created

void CegoSelect::cleanUp()
{
    if ( _pCacheArray )
    {
	// since cleanup modifies getQueryId ( e.g. _conjunctionList )
	// we have check this first
	_pCache->releaseEntry(getQueryId());
	_pCacheArray=0;
    }
    
    if ( _pCacheList )
    {
        delete _pCacheList;
        _pCacheList=0;
    }

    CegoPredicate **pP = _consolidationList.First();
    while ( pP )
    {
	(*pP)->getCondition()->setLeft(0);
	(*pP)->getCondition()->setRight(0);
	delete *pP;
	pP = _consolidationList.Next();
    }
    
    _consolidationList.Empty();

    _conjunctionList.Empty();
    _joinList.Empty();
    _joinPredList.Empty();

    _joinFields.Empty();
    _aggregationCount=0;
    _nextAid=0;

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr )
    {
        (*pExpr)->cleanUp();
        pExpr = _exprList.Next();
    }
    
    unsigned i = 0;
    while ( i < TABMNG_MAXJOINLEVEL )
    {
	if ( _pTC[i] )	
	    delete _pTC[i];
	_pTC[i]=0;

	if ( _joinBuf[i] )
	    delete _joinBuf[i];
	_joinBuf[i]=0;
	
	_attrCondFlag[i] = false;
	_firstTuple[i]=true;
	_attrPred[i]=0;
	_cursorPred[i]=0;
	i++;
    }
    
    // cleanup
    if ( _pPred )
    {
	_pPred->setCheckedRec(false);
	_pPred->cleanUp();
	
	// attr cache is left for performace
	// _pPred->clearAttrCache();
    }

    _isPrepared = false;

    _checkUnion = false;

    if ( _pUnionSelect )
    {
	_pUnionSelect->cleanUp();
    }

    if ( _pOrderSpace )
    {
	_pOrderSpace->resetOrderSpace();
    }

    _orderingDone=false;

    if ( _pGroupList )
    {
	_groupingDone = false;
		
	if ( _pGroupSpace )
	    _pGroupSpace->resetGroupSpace();	
    }

    _pParentJoinBuf = 0;
    _isCached = false;
}

// the prepare method organizes the select query parameters in a kind,
// that the join can be performed in the best way. This requires evaluating 
// of all attribute references and evaluation of the join condition to build
// an appropriate join table order list.
// the method detects, if the join was already prepared and if so, it just adjuts 
// changed join parameters ( e.g. contained procedure or parent query variables ) to the appropriate value

void CegoSelect::prepare(bool graceful) 
{    
    if ( _pGTM == 0 )
	return;
    
    if ( _isPrepared )
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Building join conditions ..."));
#endif

	buildJoinConditions();
	_rowCount=0;
	return;
    }

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

    evalReferences();


#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Analyzing join ..."));	
#endif
    
    analyzeJoin();


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

    prepareOrder();

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

    _rowCount=0;
    
    _isPrepared = true;

    if ( _pUnionSelect )
    {
	_pUnionSelect->prepare();

	if ( _pUnionSelect->_exprList.Size() != _exprList.Size() )
	{
	    throw Exception(EXLOC, Chain("Mismatched expression count in select list for union")); 
	}

	CegoExpr **pExpr1 = _pUnionSelect->_exprList.First();
	CegoExpr **pExpr2 = _exprList.First();

	while ( pExpr1 && pExpr2 )
	{
	    if ( (*pExpr1)->getAlias() != (*pExpr2)->getAlias() )
	    {
		throw Exception(EXLOC, Chain("Mismatched alias definition in select list for union")); 
	    }
	    pExpr1 = _pUnionSelect->_exprList.Next();
	    pExpr2 = _exprList.Next();
	}	
    }


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

    // provide schema
    evalSchema(graceful);

}

// the reset method performas a reset on the query without making any join evaluation
// actions. This method can be used, if there was no change on any included join variables
// If the query result was cached, cache is still valid after reset

void CegoSelect::reset()
{
    if ( _selectMode == AGGREGATION )
	_aggDone=false;

    if ( _selectMode == GROUPING )
    {
    	_groupingDone = false;

	if ( _pGroupSpace )
	    _pGroupSpace->resetGroupSpace();
    }
	
    unsigned i=0;

    while ( i <= _joinLevel )
    {
	_firstTuple[i]=true;
	i++;
    }

    i=0;

    while ( i <= _joinLevel )
    {
	if ( _pTC[i] )
	{
	    _pTC[i]->reset();	    
	}
	i++;
    }

    _joinLevel=0;
    _joinSize=0;
    
    if ( _pUnionSelect )
    {
	_checkUnion = false;
	_pUnionSelect->reset();
    }
    
    if ( _pOrderSpace )
    {
	_pOrderSpace->resetOrderSpace();
    }

    _orderingDone=false;
    _simpleDone=false;

    // we have to check, if cache was claimed
    if ( _pCacheArray )
    {
	_pCache->releaseEntry(getQueryId());
	_pCacheArray=0;
    }
    
    if ( _pCacheList )
    {
	delete _pCacheList;
	_pCacheList=0;
    }
        
    _rowCount=0;
}

void CegoSelect::setUnionSelect(CegoSelect* pUnion)
{
    _pUnionSelect = pUnion;
}

bool CegoSelect::isPrepared() const
{
    return _isPrepared;
}

// the analyzeJoin method builds up a join strategy in terms of  
// creating a join order and filtering all required join attributes
// Depending on the analyzed join where condition, a table access plan 
// is created ( either index optimized or  full table scan )
// This also involves join optimization for underlying subqueries
void CegoSelect::analyzeJoin()
{
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Preparing join ..."));	
#endif    

    if (_pPred)
    {
	_pPred->liftCondition();
	
	if (_pPred->getCondition())
	{
	    CegoQueryHelper::makeCNF(_pPred->getCondition());   	    
	}
	createConjunctionList(_pPred);
    }

    _conjunctionList += _viewConjunctionList;
    
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Consolidating conjunction list ..."));
#endif    

    consolidateConjunctionList();
        
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Building join tables ..."));
#endif

    buildJoinTables();

#ifdef CGDEBUG    
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Building join refs ..."));
#endif

    buildJoinRefs();

#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Building join cursor conditions ..."));
#endif

    buildJoinConditions();

    _joinLevel = 0;
    _joinSize = 0;    

    // prepare sub queries
    if ( _pPred ) 
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Analyzing subselect ..."));
#endif
	ListT<CegoSelect*> queryList;
	_pPred->getSelectQueryList(queryList);
	
	CegoSelect **pSelect = queryList.First();
	while ( pSelect )
	{
	    (*pSelect)->setParentJoinBuf(_joinBuf);
	    pSelect = queryList.Next();
	}
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Preparing subselects ..."));
#endif
	_pPred->analyzeSelect();
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Subselects prepared"));
#endif
    }
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Select prepared"));
#endif
}

Element* CegoSelect::getPlan()
{
    if ( _pGTM == 0 )
	throw Exception(EXLOC, Chain("No valid table manager"));
    
    prepare();

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

    
    checkValidRef();
    setParentJoinBuf();

    Element *pPlan = new Element(XML_PLAN_ELEMENT);

    while ( _joinLevel < _joinList.Size() )
    {
#ifdef CGDEBUG	
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Plan : Creating cursor for ") + _joinList[_joinLevel]->getTabName() + Chain(" on join level ") + Chain(_joinLevel));
#endif	
    
	_pTC[_joinLevel] = 
	    new CegoQueryCursor(_pGTM, _joinList[_joinLevel], _pBlock);
	
	if ( _attrCondFlag[_joinLevel] == true )
	{
#ifdef CGDEBUG	    
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Plan : Setting attrcond for ") + _joinList[_joinLevel]->getName() + Chain(" to ") + _attrCond[_joinLevel].toChain() + Chain(" on level ") + Chain(_joinLevel));
#endif

	    if ( _attrCond[_joinLevel].setup(_joinBuf, 0 ) == false )
	    {
		// attribute condition can not be resolved on this join level
		// for this reason, we disable for the future
		// since this condition is disabled, we also have to enable predicate check again
		// for the final evaluation
#ifdef CGDEBUG	    
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Plan : Disabling condition flag for ") + _joinList[_joinLevel]->getName());
#endif
		
		_attrCondFlag[_joinLevel]=false;
		if ( _attrPred[_joinLevel] )
		{
		    _attrPred[_joinLevel]->setChecked(false);
		}
	    }
	    if ( _pParentJoinBuf == 0 )
		_pTC[_joinLevel]->querySetup(_attrCond[_joinLevel], _joinBuf, _cursorPred[_joinLevel]);
	    else
	    {
		mergeBuffer();
		_pTC[_joinLevel]->querySetup(_attrCond[_joinLevel], _pMergedBuf, _cursorPred[_joinLevel]);
	    }		
	}
	else
	{
#ifdef CGDEBUG	    
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Plan : Setting no attrcond for ") + _joinList[_joinLevel]->getName() + Chain(" on level ") + Chain(_joinLevel));
#endif


	    // since cursor predicate may contain parent references, we just up, if parent join buf is empty

	    if ( _pParentJoinBuf == 0 )
		_pTC[_joinLevel]->querySetup(_joinBuf, _cursorPred[_joinLevel]);
	    else
	    {
		mergeBuffer();
		_pTC[_joinLevel]->querySetup(_pMergedBuf, _cursorPred[_joinLevel]);
	    }

	}
	
	Element* pCursorPlan = _pTC[_joinLevel]->getPlan();
	
	pPlan->addContent(pCursorPlan);
	
	_joinLevel++;
    }

    CegoPredicate **pPred = _conjunctionList.First();

    while ( pPred )
    {			
	if ( ! (*pPred)->isChecked() )
	{	  
	    Element *pPredElement = new Element(XML_PRED_ELEMENT);
	    pPredElement->setAttribute(XML_PRED_ATTR, (*pPred)->toChain(0));
	    pPlan->addContent(pPredElement);
	}
	pPred = _conjunctionList.Next();
    }

    ListT<Element*> planList;
    
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr )
    {
	ListT<CegoSelect*> queryList;
	(*pExpr)->getSelectQueryList(queryList);

	CegoSelect** pSelect = queryList.First();
	while ( pSelect )
	{
	    (*pSelect)->setParentJoinBuf(_joinBuf);
	    pSelect = queryList.Next();
	}

	(*pExpr)->getPlanList(planList);
	pExpr = _exprList.Next();
    }
    
    if ( _pPred ) 
    {	
	_pPred->getPlanList(planList);
    }

    Element** pSubPlan = planList.First();
    while ( pSubPlan )
    {
	pPlan->addContent(*pSubPlan);	
	pSubPlan = planList.Next();
    }

    if ( _pUnionSelect )
    {
	pPlan->addContent(_pUnionSelect->getPlan());
    }    

    return pPlan;
}

void CegoSelect::setParentJoinBuf(ListT<CegoField>** pParentJoinBuf)
{
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Setting parent join buffer ..."));
#endif
    
    _pParentJoinBuf = pParentJoinBuf;
    
    if ( _pUnionSelect )
    {
	_pUnionSelect->setParentJoinBuf(pParentJoinBuf);
    }
}

void CegoSelect::checkValidRef()
{
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Validating references ..."));
#endif

    ListT<CegoAttrDesc*> attrRefList = getAttrRefList();
    CegoAttrDesc **pAD = attrRefList.First();
    while ( pAD )
    {	
	if ( (*pAD)->isValid() == false )
	{
	    throw Exception(EXLOC, Chain("Unknown attribute ") + (*pAD)->toChain()); //  + Chain(" for query ") + toChain(0)); 
	}

#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Validated attribute reference ") + (*pAD)->toChain());
#endif

	pAD = attrRefList.Next();
    }
}

void CegoSelect::setProcBlock(CegoProcBlock *pBlock)
{
    _pBlock = pBlock;

    if ( _pUnionSelect )
    {
	_pUnionSelect->setProcBlock(pBlock);
    }
}

bool CegoSelect::nextTuple(ListT<CegoField>& jfl)
{   
    if ( _rowLimit > 0 && _rowCount >= _rowLimit )
    {
	if ( _cacheEnabled && _pCacheList )
	{
	    finishCaching();
	}

	if ( _pCache && _pCacheArray )
	{
	    _pCache->releaseEntry(getQueryId());
	    _pCacheArray=0;
	}
	return false;
    }

    if ( _cacheEnabled && _rowCount == 0 )
    {
	_pCacheArray = _pCache->claimEntry( getQueryId(), _cacheSchema, _cacheRows );
   
	if ( _pCacheArray )
	{
	    _isCached = true;
	}
	else
	{
	    startCaching();	    
	}
    }

    if ( _cacheEnabled && _pCacheArray && _isCached )
    {
	CegoFieldValue** pCacheRow = 0;
	if ( _rowCount == 0 ) 
	{
	    if ( _cacheRows > 0 )
	    {
		pCacheRow = _pCacheArray[_rowCount];
	    }			  
	}
	else
	{
	    if ( _rowCount < _cacheRows )
	    {			    
		pCacheRow = _pCacheArray[_rowCount];
	    }
	}
	_rowCount++;
		    
	if ( pCacheRow )
	{	    
	    jfl = _cacheSchema;
	    CegoField *pF = jfl.First();
	    unsigned pos=0;
	    while ( pF )
	    {
		pF->setValue(*pCacheRow[pos]);
		pF = jfl.Next();
		pos++;
	    }
	    return true;
	}

	_pCache->releaseEntry(getQueryId());
	_pCacheArray=0;
	
	_rowCount = 0;
	return false;
    }
    else
    {
	if ( _checkUnion == false )
	{
	    if ( nextAggTuple(jfl) )
	    {
		if ( _cacheEnabled && _pCacheList )
		{
		    ListT<CegoFieldValue> staticFieldList;
		    CegoField* pF = jfl.First();
		    while ( pF )
		    {
			staticFieldList.Insert(pF->getValue().getLocalCopy());
			_cacheEntrySize += pF->getValue().size();
			pF = jfl.Next();
		    }

		    if ( _pCache->getMaxSize() > (unsigned)_cacheEntrySize )
		    {
			_pCacheList->Insert(staticFieldList);
		    }
		    else
		    {
			delete _pCacheList;
			_pCacheList = 0;
		    }

		    // if the cache schema is still not set up, we have to do this
		    // this just has to be done on time 
		    if ( _cacheSchema.Size() == 0 )
			_cacheSchema = jfl;
		}
		_rowCount++;
		return true;
	    }
	}

	if ( _pUnionSelect )
	{
	    _checkUnion = true;

	    jfl.Empty();
	    if ( _pUnionSelect->nextTuple(jfl) )
	    {
		if ( _cacheEnabled && _pCacheList )
		{
		    ListT<CegoFieldValue> staticFieldList;
		    CegoField* pF = jfl.First();
		    while ( pF )
		    {
			staticFieldList.Insert(pF->getValue().getLocalCopy());
			_cacheEntrySize += pF->getValue().size();
			pF = jfl.Next();
		    }

		    if ( _pCache->getMaxSize() > (unsigned)_cacheEntrySize )
		    {
			_pCacheList->Insert(staticFieldList);
		    }
		    else
		    {
			delete _pCacheList;
			_pCacheList = 0;
		    }
		    
		    // if the cache schema is still not set up, we have to do this
		    // this just has to be done on time 
		    if ( _cacheSchema.Size() == 0 )
			_cacheSchema = jfl;
		}
		_rowCount++;
		return true;
	    }
	}

	// no more tuples found, finalize query caching

	if ( _cacheEnabled && _pCacheList )
	{
	    finishCaching();
	}
	return false;
    }
}

bool CegoSelect::isCached() const
{
    return _isCached;
}

bool CegoSelect::isCacheEnabled() const
{
    return _cacheEnabled;
}

void CegoSelect::startCaching()
{
    _isCached = false;
    _cacheEntrySize = 0;
    
    // check for system objects
    SetT<CegoObject> tableList;
    getObjectList(tableList);
    bool sysTableFound=false;
    CegoObject *pO = tableList.First();
    while ( pO && sysTableFound == false)
    {
	if ( pO->getType() == CegoObject::SYSTEM )
	{
	    sysTableFound=true;
	}
	pO = tableList.Next();
    }
    if ( sysTableFound == false )
    {
	_execTime.reset();
	_execTime.start();
	_pCacheList = new ListT< ListT< CegoFieldValue > >;
    }
}

void CegoSelect::finishCaching()
{
    _isCached = true;   
    SetT<CegoObject> tableList;
  
    getObjectList(tableList);
    
    // check object list for foreign objects
    bool hasForeign=false;
    CegoObject *pTable = tableList.First();
    while ( pTable && hasForeign == false)
    {
	if ( pTable->getTabSetId() != _tabSetId )
	{		    
	    hasForeign = true;
	}
	pTable = tableList.Next();
    }
    
    // we just add cache entries, if the tablelist does not contain any tables from  foreign tablesets
    Chain queryId = getQueryId();
    
    _execTime.stop();
    // get queryTime in msec 
    unsigned queryTime = (unsigned) ( _execTime.getSum()  / 1000000 );
    
    if ( hasForeign == false && queryId != Chain(MOD_QUERY_ID) && _pCache->getThreshold() <= queryTime )
    {
	_pCache->addEntry( queryId , tableList, _pCacheList, _cacheSchema );
    }
    
    delete _pCacheList;
    
    _pCacheList = 0;
    _rowCount=0;
    
}
Chain CegoSelect::getQueryId() const
{
    try
    { 
	Chain id;

	// since values might be reference to parent join buffer, we have to add the current values to the id
	
	if ( _pParentJoinBuf && _extRefCount > 0 )
	{
	    unsigned i=0;
	    while ( _pParentJoinBuf[i] )
	    {
		CegoField *pF = _pParentJoinBuf[i]->First();
		while ( pF )
		{
		    id += pF->getValue().valAsChain() + Chain("#");
		    pF = _pParentJoinBuf[i]->Next();	
		}
		i++;
	    }
	}
	
	if ( _isDistinct && _selectMode != AGGREGATION )
	    id += Chain("#d#");
	
	if ( _exprList.Size() == 0 )
	{
	    id += Chain("*");
	}
	else
	{
	    CegoExpr** pExpr = _exprList.First();
	    while (pExpr)
	    {
		id += (*pExpr)->getId(_pBlock);
		id += Chain("#");
		if ( (*pExpr)->getAlias() != Chain() )
		{
		    id += (*pExpr)->getAlias();
		    id += Chain("#");
		}
		pExpr = _exprList.Next();
	    }
	}
	
	CegoContentObject** pCO = _coList.First();
	while (pCO)
	{
	    id += (*pCO)->getId(_pBlock);
	    id += Chain("#");
	    pCO = _coList.Next();
	}
	
	CegoPredicate** pPred = _conjunctionList.First();
	while ( pPred )
	{
	    id += (*pPred)->getId(_pBlock);
	    id += Chain("#");
	    pPred = _conjunctionList.Next();
	}
	
	id += _viewCondId;
	
	if (_pGroupList)
	{
	    CegoAttrDesc **pAttrDesc = _pGroupList->First();
	    while ( pAttrDesc )
	    {
		id += (*pAttrDesc)->getId();
		id += Chain("#");
		pAttrDesc = _pGroupList->Next();
	    }	
	    if ( _pHavingPred )
	    {
		id += Chain("#h#");
		id += _pHavingPred->getId(_pBlock);
	    }
	}
	
	if ( _pNativeOrderList && _pNativeOrderOptList )
	{
	    CegoExpr **pExpr = _pNativeOrderList->First();
	    CegoOrderNode::Ordering *pOrdering = _pNativeOrderOptList->First();
	    while ( pExpr && pOrdering )
	    {
		id += (*pExpr)->getId(_pBlock);
		id += Chain("#");
		if ( *pOrdering == CegoOrderNode::ASC )
		    id += Chain("A");
		else
		    id += Chain("D");
		id += Chain("#");
		pExpr = _pNativeOrderList->Next();
	    }
	}

	if ( _rowLimit > 0 )
	{
	    id += Chain(_rowLimit);
	}
	
	if ( _pUnionSelect )
	{
	    id += Chain("#");
	    Chain queryId = _pUnionSelect->getQueryId();
	    if ( queryId == Chain(MOD_QUERY_ID))
		return Chain(MOD_QUERY_ID);
	    id += queryId;
	}
	
	return id;   
    }
    // we catch exception for any modyfying function calls ( nextcount / setcount )
    catch ( Exception e )
    {
	return Chain(MOD_QUERY_ID);
    } 
}

void CegoSelect::getObjectList(SetT<CegoObject>& objList) const
{
    // get tables for select expressions
    CegoExpr** pExpr = _exprList.First();
    while (pExpr)
    {
	ListT<CegoSelect*> queryList;
	(*pExpr)->getSelectQueryList( queryList);
	CegoSelect** pSelect = queryList.First();
	while ( pSelect )
	{
	    (*pSelect)->getObjectList(objList);
	    pSelect = queryList.Next();
	}

	(*pExpr)->getFunctionList(objList);

	pExpr = _exprList.Next();
    }

    // get tables for join
    CegoContentObject** pCO = _coList.First();
    while (pCO)
    {
	addObject4CO(*pCO, objList);	
	pCO = _coList.Next();
    }

    if (_pPred)
    {
	ListT<CegoSelect*> queryList;
	_pPred->getSelectQueryList( queryList);

	CegoSelect** pSelect = queryList.First();
	while ( pSelect )
	{
	    (*pSelect)->getObjectList(objList);
	    pSelect = queryList.Next();
	}

	if ( _pPred->getExpr1() )
	    _pPred->getExpr1()->getFunctionList(objList);
	if ( _pPred->getExpr2() )
	    _pPred->getExpr2()->getFunctionList(objList);
	if ( _pPred->getExpr3() )
	    _pPred->getExpr3()->getFunctionList(objList);
    }    
}

void CegoSelect::addObject4CO(CegoContentObject *pCO, SetT<CegoObject>& objList) const
{
    if ( _pGTM == 0 )
	throw Exception(EXLOC, "No tablemanager set");
			
    if ( pCO->getType() == CegoObject::JOIN )
    {
	ListT<CegoPredicate*> predList;
	
	((CegoJoinObject*)pCO)->getPredList(predList);
	
	CegoPredicate **pPred = predList.First();
	while ( pPred )
	{
	    ListT<CegoSelect*> queryList;
	    (*pPred)->getSelectQueryList( queryList);

	    CegoSelect** pSelect = queryList.First();
	    while ( pSelect )
	    {
		(*pSelect)->getObjectList(objList);
		pSelect = queryList.Next();
	    }

	    pPred = predList.Next();
	}
	
	ListT<CegoContentObject*> subList = pCO->getSubCOList();
	CegoContentObject** pSubCO = subList.First();
	while ( pSubCO )
	{
	    addObject4CO(*pSubCO, objList);
	    pSubCO = subList.Next();
	}
    }
    else if ( pCO->getType() == CegoObject::VIEW )
    {
	_pDBMng->useObject(pCO->getTabSetId(), pCO->getTabName(), CegoObject::VIEW, CegoDatabaseManager::SHARED, _pGTM);
	
	CegoView *pView;
	
	try
	{
	    pView = _pGTM->getView(pCO->getTabSetId(), pCO->getTabName());
	}
	catch ( Exception e )
	{
	    _pDBMng->unuseObject(pCO->getTabSetId(), pCO->getTabName(), CegoObject::VIEW);
	    throw Exception(EXLOC, Chain("Cannot get view ") + pCO->getTabName(), e);
	}
	CegoSelect* pSelect = pView->getSelect();

	pSelect->getObjectList(objList);

	objList.Insert( CegoObject(CegoObject::VIEW, pCO->getTabName(), pCO->getTabSetId()));	
	
	_pDBMng->unuseObject(pCO->getTabSetId(), pCO->getTabName(), CegoObject::VIEW);	
    }
    else if ( pCO->getType() == CegoObject::TABLE )
    {
	objList.Insert( CegoObject(CegoObject::TABLE, pCO->getTabName(), pCO->getTabSetId()));	
    }   
    else if ( pCO->getType() == CegoObject::SYSTEM )
    {
	objList.Insert( CegoObject(CegoObject::SYSTEM, pCO->getTabName()));
    }
}

bool CegoSelect::nextAggTuple(ListT<CegoField>& jfl)
{
    if ( _selectMode == AGGREGATION )
    {
	if ( _aggDone )
	    return false;
	bool moreTuple=true;

	initAggregation();
	
	while ( moreTuple )
	{
	    ListT<CegoField> aggTuple;
	    moreTuple = nextOrderedTuple(aggTuple);

	    if ( moreTuple )
	    {
		aggregateTuple(aggTuple);
		_aggregationCount++;
	    }	   
	}

	evalAggregation(jfl);
	
	_aggDone=true;
	return true;
    }
    else
    {
	return nextOrderedTuple(jfl);
    }
}

bool CegoSelect::nextOrderedTuple(ListT<CegoField>& jfl)
{   
    if ( _pGTM == 0 )
	throw Exception(EXLOC, "No tablemanager set");

    if ( _pOrderList )
    {
	if ( _orderingDone )
	{
	    jfl = _orderedSchema;
	    
	    if ( _isDistinct )
	    {
		while ( _pOrderCursor->getNext(jfl) )
		{
		    CegoField* pF1=_dfl.First();
		    CegoField* pF2=jfl.First();
		    unsigned i=0;
		    while ( pF1 && pF2 && i < _dcount )
		    {
			if ( pF1->getValue() != pF2->getValue() )
			{
			    _dfl = jfl;
			    return true;
			}
			i++;
			pF1=_dfl.Next();
			pF2=jfl.Next();
		    }
		}
		return false;
	    }
	    else
	    {
		return _pOrderCursor->getNext(jfl);
	    }
	}
	else
	{
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Collecting tuples in orderspace ..."));
#endif
	    ListT<CegoField> dataTuple;
	    ListT<CegoField> orderTuple;
	    bool isInit=false;

	    while ( nextResultTuple(dataTuple, orderTuple) )
	    {
		ListT<CegoField> orderKey;

		CegoExpr **pExpr = _pOrderList->First();
		while ( pExpr )
		{	
		    ListT<CegoAttrDesc*> attrRefList = (*pExpr)->getAttrRefList();

		    CegoAttrDesc** pAttrRef = attrRefList.First();

		    while ( pAttrRef )
		    {
			if ( CegoField* pF = orderTuple.Find(CegoField((*pAttrRef)->getTableName(), (*pAttrRef)->getAttrName())) )
			{
			    orderKey.Insert(*pF);
			}
			pAttrRef = attrRefList.Next();
		    }

		    // _pDBMng->log(_modId, Logger::DEBUG, Chain("Checking aggregation..."));
		    // check for aggregation id
		    ListT<CegoAggregation*> aggList = (*pExpr)->getAggregationList();
		    CegoAggregation **pAgg = aggList.First();
		    
		    while ( pAgg )
		    {
			bool notFound = true;
			CegoField* pF = orderTuple.First();
			while ( pF && notFound )
			{
			    if ( pF->getId() == (*pAgg)->getAggregationId() )
			    {
				orderKey.Insert(*pF);
				notFound = false;
			    }
			    else
			    {
				pF = orderTuple.Next();
			    }
			}
			
			if ( notFound )
			{
			    Chain msg = Chain("Unknown order expression ") +  (*pAgg)->toChain(0);
			    throw Exception(EXLOC, msg);
			}
			pAgg = aggList.Next();				
		    }			    
		    pExpr = _pOrderList->Next();
		}

		if ( isInit == false )
		{
		    _pOrderSpace->initOrderSpace(_pOrderList, _pOrderOptList,  _pGTM->getDBMng()->getTSSortAreaSize(_tabSetId));
		}

		_pOrderSpace->insertTuple(orderKey, dataTuple);
		if ( _pGroupSpace )
		    _pGTM->setAllocatedSortArea(_pOrderSpace->numAllocated() + _pGroupSpace->numAllocated());
		else
		    _pGTM->setAllocatedSortArea(_pOrderSpace->numAllocated());

		if ( isInit == false )
		{    
		    _orderedSchema = dataTuple;
		    isInit=true;
		}
	    }

	    // no rows were selected
	    if ( isInit == false )
	    {
		return false;
	    }

	    _orderingDone = true;
	    
	    if ( _pOrderCursor )
		delete _pOrderCursor;
	    
	    _pOrderCursor = _pOrderSpace->getCursor();
	    
	    jfl = _orderedSchema;

	    bool moreTuple = _pOrderCursor->getFirst(jfl); 

	    if ( _isDistinct )
		_dfl = jfl;
	    return moreTuple;
	}
    }
    else
    {
	ListT<CegoField> ofl;
	return nextResultTuple(jfl, ofl);	   
    }
}

// the nextResultTuple method is used as a cursor to retrieve the corresponding result set of 
// the defined select query. the method returns true until no further tuple results are
// returned from the query. The current tuple result is provided in the reference parameter jfl

bool CegoSelect::nextResultTuple(ListT<CegoField>& jfl, ListT<CegoField>& ofl)
{
    switch ( _selectMode )
    {
    case SIMPLE:
    {
	if ( _simpleDone == true )
	    return false;
	
	jfl.Empty();
	CegoExpr **pExpr = _exprList.First();
	while ( pExpr )
	{
	    CegoField f;
	    f.setValue((*pExpr)->evalFieldValue(_pParentJoinBuf, _pBlock) );
	    f.setAttrName((*pExpr)->getAlias());
	    jfl.Insert(f);
	    pExpr = _exprList.Next();
	}
	_simpleDone = true;
	return true;
    }
    case PLAIN:
    case AGGREGATION:
    {	
	return nextJoinTuple(jfl, ofl);
    }
    case GROUPING:
    {
	ListT<CegoField> gfl;

	bool moreTuple = nextGroupedTuple(gfl);

	ofl=gfl;

	if ( moreTuple == false )
	    return false;

	jfl.Empty();
	CegoExpr **pExpr = _exprList.First();
	while ( pExpr )
	{
	    ListT<CegoAggregation*> aggList;	    
	    aggList = (*pExpr)->getAggregationList();

	    if ( aggList.Size() > 0 ) 
	    {
		CegoField f;
		
		CegoAggregation **pAgg = aggList.First();
		while ( pAgg )
		{
		    bool notFound=true;
		    CegoField *pF = gfl.First();
		    while ( pF && notFound )
		    {
			if ( pF->getId() == (*pAgg)->getAggregationId() )
			{
			    (*pAgg)->setFieldValue(pF->getValue());
			    f.setId((*pAgg)->getAggregationId());
			    notFound=false;
			}			    
			pF = gfl.Next();
		    }		
		    pAgg = aggList.Next();
		}		

		ListT<CegoField>* fl[2];
		fl[0] = &gfl;
		fl[1] = 0;

		f.setValue ( (*pExpr)->evalFieldValue(fl, _pBlock) );
		f.setAttrName((*pExpr)->getAlias());
		jfl.Insert(f);
	    }
	    else
	    {
		CegoField f;
		f.setId(0);
		
		ListT<CegoField>* fl[2];
		fl[0] = &gfl;
		fl[1] = 0;

		f.setValue((*pExpr)->evalFieldValue(fl, _pBlock) );
		f.setAttrName((*pExpr)->getAlias());
		jfl.Insert(f);
	    }
	    pExpr = _exprList.Next();
	}
	return moreTuple;
    }
    default:
       throw Exception(EXLOC, Chain("Unknown select mode"));
    }
}

bool CegoSelect::nextGroupedTuple(ListT<CegoField>& jfl)
{    
    if ( _groupingDone )
    {
	if ( ! _pGroupCursor )
	    return false;
	    
	jfl = _pGroupSpace->getSchema();

	if ( _pHavingPred )
	{
	    while ( _pGroupCursor->getNext(jfl) )
	    {	       
		ListT<CegoField>* fla[2];
		
		fla[0]=&jfl;
		fla[1]=0;

		_pHavingPred->setAggregationValue(jfl);

		if ( _pHavingPred->eval(0, 0, fla, 0, _pBlock) )
		{
		    return true;
		}
	    }
	    return false;
	}
	else
	{	    
	    return  _pGroupCursor->getNext(jfl);
	}
    }
    else
    {	
	ListT<CegoAggregation*> aggList;

	CegoExpr** pExpr = _exprList.First();
	while ( pExpr )
	{	    	    
	    aggList += (*pExpr)->getAggregationList();
	    pExpr = _exprList.Next();
	}

	if ( _pHavingPred )
	{
	    aggList += _pHavingPred->getAggregationList();
	}

	if ( _pOrderList )
	{
	    CegoExpr **pExpr = _pOrderList->First();
	    while ( pExpr )
	    {
		aggList += (*pExpr)->getAggregationList();
		pExpr = _pOrderList->Next();
	    }
	}
	
	ListT<CegoField> dataTuple;
	bool isInit=false;

	ListT<CegoField> ofl;
	while ( nextJoinTuple(dataTuple, ofl) )
	{	    
	    if ( isInit == false )
	    {
		ListT<CegoField> groupKey;
		CegoAttrDesc** pAttrRef = _pGroupList->First();
		
		while ( pAttrRef )
		{
		    CegoField* pF;
		    if ( (pF = dataTuple.Find(CegoField((*pAttrRef)->getTableName(), (*pAttrRef)->getAttrName()))) != 0 )
		    {
			groupKey.Insert(*pF);
		    }
		    else
		    {
			Chain msg = Chain("Unknown group attribute ") +  (*pAttrRef)->getTableName() + Chain(".") + (*pAttrRef)->getAttrName();
			throw Exception(EXLOC, msg);
		    }
		    pAttrRef = _pGroupList->Next();
		}

		_pGroupSpace->initGroupSpace(groupKey, aggList, _pGTM->getDBMng()->getTSSortAreaSize(_tabSetId));
		isInit = true;
	    }

	    _pGroupSpace->insertTuple(dataTuple);
	    _pGTM->setAllocatedSortArea(_pGroupSpace->numAllocated());
	}
	
	_groupingDone = true;

	if ( isInit == false )
	    return false;

	if ( _pGroupCursor )
	    delete _pGroupCursor;
	
	_pGroupCursor = _pGroupSpace->getCursor();
	
	jfl = _pGroupSpace->getSchema();

	bool moreTuple = _pGroupCursor->getFirst(jfl); 

	if ( _pHavingPred )
	{
	    while ( moreTuple )
	    {
		ListT<CegoField>* fla[2];
				
		fla[0]=&jfl;
		fla[1]=0;

		_pHavingPred->setAggregationValue(jfl);
		
		if ( _pHavingPred->eval(0, 0, fla, 0, _pBlock) )
		{
		    return true;
		}
		else
		{
		    moreTuple = _pGroupCursor->getNext(jfl);
		}
	    }
	}
	return moreTuple;
    }
}

bool CegoSelect::nextJoinTuple(ListT<CegoField>& jfl, ListT<CegoField>& ofl)
{    
    if ( _pGTM == 0 )
	throw Exception(EXLOC, "No tablemanager set");
    
    if ( _pGTM->isAborted() )
    {
	throw Exception(EXLOC, "Query aborted");
    }
    
    ofl.Empty();
    
    while ( true )
    {	
	bool joinComplete=false;
	
	while ( _joinLevel < _joinList.Size()  && ! joinComplete)
	{
	    bool moreTuple=false;
    
	    if ( _firstTuple[_joinLevel] ) 
	    {
		if ( _pTC[_joinLevel] == 0 )
		{
#ifdef CGDEBUG
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Creating cursor for ") 
		    		  + _joinList[_joinLevel]->getTabName() + Chain(" on join level ") + Chain(_joinLevel));
#endif

		    
		    _pTC[_joinLevel] = 
			new CegoQueryCursor(_pGTM, _joinList[_joinLevel], _pBlock);
		}
		else
		{
		    _pTC[_joinLevel]->reset();		   
		}
	
		if ( _attrCondFlag[_joinLevel] == true )
		{
#ifdef CGDEBUG	    
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Setting attrcond ") + _attrCond[_joinLevel].toChain() + Chain(" on level ") + Chain(_joinLevel));
#endif
		    if ( _attrCond[_joinLevel].setup(_pParentJoinBuf, 0, _joinBuf, 0 ) == true )
		    {
#ifdef CGDEBUG	    
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Cursor set up for attrcond ") + _attrCond[_joinLevel].toChain());
#endif

			if ( _pParentJoinBuf == 0 )
			    _pTC[_joinLevel]->querySetup(_attrCond[_joinLevel], _joinBuf, _cursorPred[_joinLevel]);
			else
			{
			    mergeBuffer();
			    _pTC[_joinLevel]->querySetup(_attrCond[_joinLevel], _pMergedBuf, _cursorPred[_joinLevel]);
			}
		    }	 
		    else
		    {
#ifdef CGDEBUG	    
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Could not set up cursor for attrcond =  ") + _attrCond[_joinLevel].toChain());
#endif
			_attrCondFlag[_joinLevel]=false;
			if ( _attrPred[_joinLevel] )
			{
			    _attrPred[_joinLevel]->setChecked(false);
			}

			// since cursor predicate may contain parent references, we just up, if parent join buf is empty
			if ( _pParentJoinBuf == 0 )
			    _pTC[_joinLevel]->querySetup(_joinBuf, _cursorPred[_joinLevel]);
			else
			{
			    mergeBuffer();
			    _pTC[_joinLevel]->querySetup(_pMergedBuf, _cursorPred[_joinLevel]);
			}

		    }		    
		}
		else
		{
		    if ( _pParentJoinBuf == 0 )
		    {
			_pTC[_joinLevel]->querySetup(_joinBuf,  _cursorPred[_joinLevel]);
		    }
		    else
		    {
			mergeBuffer();
			_pTC[_joinLevel]->querySetup(_pMergedBuf, _cursorPred[_joinLevel]);
		    }
		}

		/*		
		cout << "NextTuple : Size=" << _joinSize << " NumCO= " <<  _joinList[_joinLevel]->getSubCOList().Size() << endl;
		for ( int i=_joinSize; i<_joinList[_joinLevel]->getSubCOList().Size(); i++)
		{
		    cout << "SchemaSize=" << _joinBuf[i].Size() << endl;
		}
		*/
#ifdef CGDEBUG	    
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Retrieving first tuple from cursor ..."));
#endif
		
		moreTuple = _pTC[_joinLevel]->nextTuple(_joinBuf, _joinSize, _joinList[_joinLevel]->getSubCOList().Size());

#ifdef CGDEBUG
		if ( moreTuple )
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Tuple retrieved"));
		else
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("No more tuple available"));
#endif
		if ( moreTuple )
		    _firstTuple[_joinLevel]=false;		
	    }
	    else
	    {				
		/*
		cout << "NextTuple : Size=" << _joinSize << " NumCO= " <<  _joinList[_joinLevel]->getSubCOList().Size() << endl;
		for ( int i=_joinSize; i<_joinList[_joinLevel]->getSubCOList().Size(); i++)
		{
		    cout << "SchemaSize=" << _joinBuf[i].Size() << endl;
		}
		*/
#ifdef CGDEBUG	    
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Retrieving next tuple from cursor ..."));
#endif
		moreTuple = _pTC[_joinLevel]->nextTuple(_joinBuf, _joinSize, _joinList[_joinLevel]->getSubCOList().Size());
#ifdef CGDEBUG
		if ( moreTuple )
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Tuple retrieved"));
		else
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("No more tuple available"));
#endif
	    }

	    if ( moreTuple)
	    {
		if ( _joinLevel < _joinList.Size() - 1 )
		{
		    _joinSize += _joinList[_joinLevel]->getSubCOList().Size();
		    _joinLevel++;
		}
		else
		    joinComplete=true;
	    }
	    else
	    {		
		_firstTuple[_joinLevel]=true;
		
		if ( _joinLevel > 0 )
		{
		    _joinSize -= _joinList[_joinLevel]->getSubCOList().Size();
		    _joinLevel--;
		}
		else
		{
		    unsigned i=0;
		    while ( i < TABMNG_MAXJOINLEVEL )
		    {			
			if ( _pTC[i] )
			    _pTC[i]->reset();
			i++;
		    }
		    
		    return false;
		}
	    }
	}
	
	bool checkPred = true;
	CegoPredicate **pPred = _conjunctionList.First();

	while ( pPred && checkPred )
	{
	    if ( ! (*pPred)->isChecked() )
	    {		
		// cout << " For " << toChain(0) << " eval predicate "  <<  (*pPred)->toChain(0) << endl;
#ifdef CGDEBUG
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Evaluating predicate ") + (*pPred)->toChain(0));
#endif
		checkPred = (*pPred)->eval(_pParentJoinBuf,
					   0,
					   _joinBuf,
					   0,
					   _pBlock);

#ifdef CGDEBUG
		if ( checkPred )
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Predicate matches"));
		else
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Predicate matches not"));
#endif		
	    }

	    pPred = _conjunctionList.Next();
	}
	
	if ( checkPred )
	{	    
	    jfl.Empty();

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

	    evalSelection(_exprList, _joinBuf, jfl);
		
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Evaluating selection done"));
#endif

	    if ( _pOrderList )
	    {
		CegoExpr **pExpr = _pOrderList->First();
		while ( pExpr )
		{
		    ListT<CegoAttrDesc*> attrRefList = (*pExpr)->getAttrRefList();		    
		    CegoAttrDesc** pAttrRef = attrRefList.First();
		    
		    while ( pAttrRef )
		    {
			unsigned i=0;
			bool notFound=true;
			while ( i< _joinSize + _joinList[_joinLevel]->getSubCOList().Size()
				&& notFound )
			{
			    if ( CegoField* pF = _joinBuf[i]->Find(CegoField((*pAttrRef)->getTableName(), (*pAttrRef)->getAttrName())))
			    {
				ofl.Insert(*pF);
				notFound=false;
			    }
			    i++;
			}
			/* there might be attributes in nested selects or parent queries, which cannot be found
			   so this check is not useful */

			/*
			if ( notFound )
			{
			    Chain msg = Chain("Unknown order attribute ") + (*pAttrRef)->getTableName() + Chain(".") + (*pAttrRef)->getAttrName();
			    throw Exception(EXLOC, msg);
			}
			*/
			pAttrRef = attrRefList.Next();
		    }
		    
		    pExpr = _pOrderList->Next();
		}
	    }
	    return true;
	}
    }
}

CegoPredicate* CegoSelect::getPredicate()
{
    return _pPred;
}

bool CegoSelect::hasAliasReference() const
{
    CegoExpr** pExpr = _exprList.First();
    while ( pExpr )
    {
	if ( (*pExpr)->getAlias() == Chain() )
	    return false;
	pExpr = _exprList.Next();
    }
    return true;
}

void CegoSelect::getSchema(ListT<CegoField>& schema)
{
    schema = _evalSchema;
}

void CegoSelect::evalSchema(bool graceful)
{
    if ( _exprList.isEmpty() )
    {
	_evalSchema = _joinFields;
    }
    else
    {
	CegoExpr** pExpr = _exprList.First();
	while ( pExpr )
	{
	    CegoAttrDesc* pAttrDesc = (*pExpr)->checkAttr();
	    if ( pAttrDesc )
	    {
		if ( pAttrDesc->getAttrName() == Chain(SELECTION_WILDCARD))
		{
		    bool refFound=false;
		    CegoField *pF = _joinFields.First();
		    while ( pF )
		    {
			if ( (Chain)pF->getTableName() ==  (Chain)pAttrDesc->getTableName()
			     || (Chain)pF->getTableAlias() ==  (Chain)pAttrDesc->getTableName() )
			{
			    refFound=true;
			    _evalSchema.Insert(*pF);
			}
			pF = _joinFields.Next();
		    }
		    if ( refFound == false )
		    {
			Chain msg = Chain("Unknown table reference ") + pAttrDesc->getTableName();
			throw Exception(EXLOC, msg);		    
		    }
		}
		else
		{
		    CegoField f = (*pExpr)->evalField(_joinFields, graceful);
		    _evalSchema.Insert(f);
		}
	    }
	    else
	    {
		CegoField f = (*pExpr)->evalField(_joinFields, graceful);
		_evalSchema.Insert(f);
	    }
	    pExpr = _exprList.Next();
	}
    }
    if ( _pUnionSelect )
    {
	ListT<CegoField> subSchema;
	_pUnionSelect->getSchema(subSchema);
		
	CegoField *pF = _evalSchema.First();
	CegoField *pSF = subSchema.First();

	while ( pF && pSF )
	{
	    if ( pF->getType() == NULL_TYPE )
            {
                if ( pSF->getType() != NULL_TYPE )
                {
                    pF->setType(pSF->getType());
                    pF->setLength(pSF->getLength());
                }
            }
            else if ( pSF->getType() == NULL_TYPE )
            {
                if ( pF->getType() != NULL_TYPE )
                {
                    pSF->setType(pF->getType());
                    pSF->setLength(pF->getLength());
                }
            }
            else if ( pF->getType() != pSF->getType() )
            {
                throw Exception(EXLOC, "Type mismatch in union select");
            }

	    pF = _evalSchema.Next();
	    pSF = subSchema.Next();
	}
    }

    CegoField *pF = _evalSchema.First();
    unsigned id=1;
    while ( pF )
    {
        pF->setId(id);
        id++;
        pF = _evalSchema.Next();
    }
    return;
}

void CegoSelect::createConjunctionList(CegoPredicate* pPred)
{
    if (pPred)
    {
	CegoCondition* pC = pPred->getCondition();	
	if (pC)
	{
	    if (pC->getCondType() == CegoCondition::AND)
	    {
		createConjunctionList(pC->Left());
		createConjunctionList(pC->Right());
	    }
	    else
	    {
		_conjunctionList.Insert(pPred);
	    }
	}
	else
	{
	    _conjunctionList.Insert(pPred);
	}
    }
}

void CegoSelect::consolidateConjunctionList()
{
    if ( _pGTM == 0 )
	throw Exception(EXLOC, "No tablemanager set");

#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Consolidate conjunction list  ( Size =  ") + Chain(_conjunctionList.Size()) + Chain(")"));
#endif

    // first step :
    // ============
    // we have to check for predicates, where the referenced tables 
    // are the same. These predicates are merged then.
    // This is important to analyse the corresponding table join order

    bool finished = false;
    
    while ( finished == false )
    {
	SetT<Chain> tableRefSetA;
	SetT<Chain> tableRefSetB;
	
	bool predFound = false;
	CegoPredicate **pPredA = _conjunctionList.First();
	while ( pPredA && predFound == false)
	{
	    if ( (*pPredA)->isChecked() == false && (*pPredA)->hasOrCond() == false )
		predFound = true;
	    else
		pPredA = _conjunctionList.Next();
	}
	
	if ( pPredA )
	{      
	    tableRefSetA = (*pPredA)->getTableRefSet();
	    (*pPredA)->setChecked(true);
	}
	else
	{
	    finished = true;
	}
	
	if ( finished == false )
	{
	    bool isMerged=false;
	    CegoPredicate **pPredB = _conjunctionList.Next();
	    while ( pPredB && isMerged == false )
	    {
		if ( (*pPredB)->hasOrCond() )
		{
		    pPredB = _conjunctionList.Next();
		}
		else
		{
		    tableRefSetB = (*pPredB)->getTableRefSet();
		    
		    if ( tableRefSetA == tableRefSetB )
		    {
			CegoPredicate *pA = *pPredA;
			CegoPredicate *pB = *pPredB;
			
#ifdef CGDEBUG
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Removing predicates ") + (*pPredA)->toChain(0) + Chain(" and ") + (*pPredB)->toChain(0));
#endif
			_conjunctionList.Remove(*pPredA);
			_conjunctionList.Remove(*pPredB);
			
			CegoCondition* pCond = new CegoCondition(CegoCondition::AND);
			pCond->setLeft(pA);
			pCond->setRight(pB);
			CegoPredicate *pCondPred = new CegoPredicate(pCond);
#ifdef CGDEBUG
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Adding predicate ") + pCondPred->toChain(0));
#endif
			_conjunctionList.Insert(pCondPred);
			_consolidationList.Insert(pCondPred);
			
			isMerged = true;
		    }
		    if ( isMerged == false )
			pPredB = _conjunctionList.Next();
		}
	    }
	}
    }

    CegoPredicate **pPred = _conjunctionList.First();
    while ( pPred )
    {
	(*pPred)->setCheckedRec(false);
	pPred = _conjunctionList.Next();
    }

    // second step :
    // ============
    // the conjunction list is ordered regarding the 
    // amount of referenced tables.
    // predicates with more referenced tables are put to the beginning 
    // of the list.
    // The ordered conjunction list results in an appropriate table join order 
    // ( method buildJoinTables ), so predicates with index conditions for two joined
    // tables ( a = b ) are treated first before constant comparisions ( a = 'alpha' )
    // 
    // Additional logic added at 12.07.2015 :
    // Inappropriate predicates ( e.g. a != b, exists ( .. ), a in ( ..), etc.
    // are first filtered. Otherwise, this can lead to inefficient join ordering
    // Also predicates are filtered out, which don't have any table references.
    // Although this might be stupid, it is allowed. Don't filtering out these predicates,
    // would result in an infinite growsize-loop 
    
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Conjunction list size =  ") + Chain(_conjunctionList.Size()));
#endif
	        
    StackT<CegoPredicate*> orderedStack;

    // first filter out inappropriate predicates and push to stack first
    pPred = _conjunctionList.First();
    while ( pPred )
    {
	bool isAppropriate = (*pPred)->getMode() == CegoPredicate::EXPRCOMP || (*pPred)->getMode() == CegoPredicate::CONDITION || (*pPred)->getMode() == CegoPredicate::IN ;
	if ( isAppropriate == false || (*pPred)->getTableRefSet().Size() == 0 )
	{
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Pushing inapp predicate ") + (*pPred)->toChain(0));
#endif
	    orderedStack.Push(*pPred);
	    _conjunctionList.Remove(*pPred);
	    pPred = _conjunctionList.First();
	}
	else
	{
	    pPred = _conjunctionList.Next();
	}
    }

    // second filter out predicates with no index available
    pPred = _conjunctionList.First();
    while ( pPred )
    {
	bool indexAvailable = CegoQueryHelper::checkIndexForPredicate(_tabSetId, _pGTM, *pPred, _coList);
	
	if ( indexAvailable == false ) 
	{
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Pushing no-index predicate ") + (*pPred)->toChain(0));
#endif
	    orderedStack.Push(*pPred);
	    _conjunctionList.Remove(*pPred);
	    pPred = _conjunctionList.First();
	}
	else
	{
	    pPred = _conjunctionList.Next();
	}
    }
 
    unsigned growSize = 1;
    unsigned numAdded = 0;
    while ( numAdded < _conjunctionList.Size() )
    {
	pPred = _conjunctionList.First();
	while ( pPred )
	{
	    if ( (*pPred)->getTableRefSet().Size() == growSize )
	    {
#ifdef CGDEBUG
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Pushing app predicate ") + (*pPred)->toChain(0));
#endif
		numAdded++;
		orderedStack.Push(*pPred);
	    }
	    pPred = _conjunctionList.Next();
	}
	growSize++;
    }
    
    _conjunctionList.Empty();
    CegoPredicate *pP;
    while ( orderedStack.Pop(pP))
	_conjunctionList.Insert(pP);
}

//
// the buildJoinTables method provides an appropriate order of the required join tables
// for the query execution. In this sense, first the table are joined, where predicates have
// been defined. In this sense, inner and outer joins are treated first 
//

void CegoSelect::buildJoinTables()
{
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Building join table list"));
#endif

    // growStep is the number of tables to join with the next join step
    // we start with growStep 1 
    unsigned growStep=1;
    
    while ( _joinList.Size() != _coList.Size() && growStep <= _coList.Size() )
    {	
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("JoinList Size = ") + Chain(_joinList.Size()) + Chain(", coList Size = ") + Chain(_coList.Size()) + Chain(", growStep = ") + Chain(growStep));
#endif
	bool isGrowing = true;
	while ( isGrowing  )
	{	    
#ifdef CGDEBUG
	    _pDBMng->log(_modId, Logger::DEBUG, Chain("Still growing ..."));
#endif
	    isGrowing = false;

	    CegoPredicate **pPred = _conjunctionList.First();
	    while ( pPred )
	    {
		ListT<CegoContentObject*> newObjectList;

		// we just treet predicated, which are adequate for join order optimization
		if ( (*pPred)->prioOnJoin() )
		    getPredObjectList(*pPred, newObjectList);
#ifdef CGDEBUG
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Analysing predicate ") + (*pPred)->toChain(0));

		Chain tlist;
		CegoContentObject **pDO = newObjectList.First();
		while ( pDO )
		{
		    tlist = tlist + Chain(" ") + (*pDO)->getTabName(); 
		    pDO = newObjectList.Next();
		}

		_pDBMng->log(_modId, Logger::DEBUG, Chain("Found ") + Chain(newObjectList.Size()) + Chain(" tables [") + tlist + Chain("]"));
#endif
		CegoContentObject** pOE = _joinList.First();
		while ( pOE )
		{    		    
		    CegoContentObject **pNCO = newObjectList.First();
		    while ( pNCO )
		    {
			if ( (**pNCO) == (**pOE) )
			{
			    newObjectList.Remove(*pNCO);
			    pNCO = newObjectList.First();
			}
			else
			{
			    pNCO = newObjectList.Next();
			}
		    }
		    pOE = _joinList.Next();
		}

		if ( newObjectList.Size() == growStep )
		{
#ifdef CGDEBUG
		    Chain tlist;
		    CegoContentObject **pDO = newObjectList.First();
		    while ( pDO )
		    {
			tlist = tlist + Chain(" ") + (*pDO)->getTabName(); 
			pDO = newObjectList.Next();
		    }
		    
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("On growstep = " ) + Chain(growStep) + Chain(" adding ") + Chain(newObjectList.Size()) + Chain(" tables to join list  [") + tlist + Chain("]"));
#endif
	
		    _joinList += newObjectList;

		    /* we reset growStep to 1, since newObjectList was modified */ 
		    growStep = 1;
		    isGrowing = true;

		    pPred = _conjunctionList.First();
		}
		else
		{
		    pPred = _conjunctionList.Next();
		}
	    }	    
	}
	growStep++;
    }

    if ( _joinList.Size() != _coList.Size() )
    {
	CegoContentObject** pCO = _coList.First();
	
	while (pCO)
	{		    	    
	    bool notFound=true;
	    CegoContentObject **pJCO = _joinList.First();
	    while ( pJCO )
	    {
		if ( (Chain)(*pJCO)->getName() == (Chain)(*pCO)->getName())
		    notFound=false;
		pJCO = _joinList.Next();
	    }
	    
	    if ( notFound )
	    {
		_joinList.Insert(*pCO);
	    }
	
	    pCO = _coList.Next();
	}
    }
        
    CegoContentObject** pJCO = _joinList.First();
    while ( pJCO )
    {
	if ( (*pJCO)->getType() != CegoObject::JOIN )
	{
	    CegoField *pF =  (*pJCO)->getSchema().First();
	    while (pF)
	    {
		pF->setTableName((*pJCO)->getTabName());
		pF->setTableAlias((*pJCO)->getName());
		pF = (*pJCO)->getSchema().Next();
	    }
	}
	pJCO = _joinList.Next();
    }
}

void CegoSelect::buildJoinConditions()
{    
    if ( _pPred )
	_pPred->setCheckedRec(false);

    CegoPredicate **pP = _conjunctionList.First();
    while ( pP )
    {
	(*pP)->setCheckedRec(false);
	pP = _conjunctionList.Next();
    }

    unsigned i = 0;
    while ( i < TABMNG_MAXJOINLEVEL )
    {
	_attrCondFlag[i]=false;
	i++;
    }

    i = 0;
    ListT<CegoField> availJoinFields;
    
    if ( _pParentJoinBuf )
    {
	unsigned i=0;
	while ( _pParentJoinBuf[i] )
	{
	    availJoinFields += *_pParentJoinBuf[i];
	    i++;
	}
    }	
    
    if ( i >= TABMNG_MAXJOINLEVEL )
	throw Exception(EXLOC, "Join levels exceeded");

    if ( ! _conjunctionList.isEmpty() )
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Checking conjunctionlist ..."));
#endif	
	while ( i < _joinList.Size() )
	{	    
	    _attrCondFlag[i] = false;
	    
	    CegoPredicate **pPred = _conjunctionList.First();

	    bool notFound = true;
	    while ( pPred && notFound )
	    {		
		if ( (*pPred)->isChecked() == false )
		{		    
		    CegoAttrCond ac;
#ifdef CGDEBUG
		    _pDBMng->log(_modId, Logger::DEBUG, Chain("Getting attribute cond for ") + (*pPred)->toChain(0));
#endif
		    CegoQueryHelper::AttrCondMatch m = CegoQueryHelper::checkAttrCond(ac, *pPred, _joinList[i]->getSchema(), &availJoinFields, 1, _pBlock);
		    		    
		    if ( m != CegoQueryHelper::INAPP )
		    {
			bool predChecked = false;
			
			if ( m == CegoQueryHelper::COMPLETE )
			    predChecked = true;
#ifdef CGDEBUG
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Detecting valid attr cond [ ") + ac.toChain() + Chain(" ] on level ") + Chain(i));
#endif 			
			if ( _attrCondFlag[i] == false )
			{
			    _attrCondFlag[i] = true;
			    _attrCond[i] = ac;
			}
			else
			{
			    if ( ac.isAnd() && _attrCond[i].isAnd() )
			    {
				_attrCond[i] = _attrCond[i] + ac;
			    }
			    else
			    {
				predChecked=false;
			    }
			}		       
			
			// we just set predicated to checked which are not external
			// 06.07.2025, no more need since pPred is fully handled
			if ( predChecked == true ) // &&  _viewConjunctionList.Find(*pPred) == 0 )
			{
			    (*pPred)->setChecked(predChecked);
			}
			
			// if _attrCond.setup fails, the predicate has to be checked on corresponding join level
			_attrPred[i] = *pPred;

			if ( m != CegoQueryHelper::COMPLETE )
			{
			    // cout << "Attr pred is not complete" << endl;
			    if ( (*pPred)->isCursorPred() )
			    {				
				_cursorPred[i] = *pPred;
			    }
			}
			
			pPred = _conjunctionList.Next();		    			
		    }
		    else
		    {
#ifdef CGDEBUG
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Inappropriate predicate on level ") + Chain(i));
#endif

			if ( (*pPred)->isCursorPred() )
			{
			    _cursorPred[i] = *pPred;
			}

			// cout << "Cursor Pred = " << (*pPred)->toChain(0) << endl;
			
			pPred = _conjunctionList.Next();
		    }		    
		}
		else
		{
		    pPred = _conjunctionList.Next();
		}		
	    }

	    availJoinFields += _joinList[i]->getSchema();
	    i++;

	    if ( i >= TABMNG_MAXJOINLEVEL )
		throw Exception(EXLOC, "Join levels exceeded");
	}
    }
}

void CegoSelect::buildJoinRefs()
{
    // provide all selected fields in _joinFields

    unsigned i=0;
    CegoContentObject** pCO = _joinList.First();
    
    while ( pCO )
    {	
	// check for all subCO
	CegoContentObject **pSubCO = (*pCO)->getSubCOList().First();
	while ( pSubCO )
	{	    
	    if ( (*pSubCO)->getType() != CegoObject::JOIN )
	    {
		CegoField *pF =  (*pSubCO)->getSchema().First();
		while (pF)
		{
		    pF->setTableName((*pSubCO)->getTabName());
		    // cout << "Setting table alias " << (*pSubCO)->getTabAlias() << endl;
		    
		    pF->setTableAlias((*pSubCO)->getTabAlias());
		    // pF->setTableAlias((*pSubCO)->getName());
		    pF = (*pSubCO)->getSchema().Next();
		}
	    }

	    ListT<CegoField> outSchema;

	    // in case of select * get all available fields
	    if ( _exprList.isEmpty() )
	    {
		filterRefs( (*pSubCO)->getSchema(), outSchema );
		outSchema = (*pSubCO)->getSchema();
	    }
	    else
	    {				
		filterRefs( (*pSubCO)->getSchema(), outSchema );
		(*pSubCO)->setSchema(outSchema);
	    }

	    // sort outSchema

	    if ( _joinBuf[i] )
		_joinBuf[i]->Empty();
	    else
		_joinBuf[i] = new ListT<CegoField>;
	    
	    CegoField *pF = outSchema.First();
	    unsigned maxId=0;
	    while ( pF )
	    {
		if ( pF->getId() > maxId )
		    maxId = pF->getId();
		pF = outSchema.Next();
	    }

	    for ( unsigned id=0; id<=maxId; id++ )
	    {
		CegoField *pF = outSchema.First();
		while ( pF )
		{
		    if ( pF->getId() == id )
		    {
			_joinBuf[i]->Insert(*pF);
			pF = 0;
		    }
		    else
		    {
			pF = outSchema.Next();
		    }
		}
	    }
	    
	    i++;

	    _joinFields += (*pSubCO)->getSchema();
	    
	    pSubCO = (*pCO)->getSubCOList().Next();
	}
	pCO = _joinList.Next();
    }
}

void CegoSelect::filterRefs(const ListT<CegoField>& inSchema, ListT<CegoField>& outSchema)
{
    if ( _exprList.isEmpty() )
    {
	outSchema = inSchema;
    }
    else
    {
	CegoExpr** pExpr = _exprList.First();
	while (pExpr)
	{
	    filterExprRef(*pExpr, inSchema, outSchema);
	    pExpr = _exprList.Next();
	}
        
	if ( _pGroupList == 0 && _selectMode == AGGREGATION )
	{
	    pExpr = _exprList.First();
	    while (pExpr)
	    {
		// expressions are just allowed containing an aggregation or constant values
		if (  (*pExpr)->getAggregationList().Size() == 0 && (*pExpr)->getAttrRefList().Size() > 0 )
		{
		    Chain msg = Chain("Invalid expression ") + (*pExpr)->toChain(0) + Chain(" for aggregation");
		    throw Exception(EXLOC, msg);
		}
		pExpr = _exprList.Next();
	    }
	}
    }
    
    ListT<CegoField> tmpSchema[TABMNG_MAXTMPSCHEMA];

    unsigned tmpidx=0;

    if (_pPred)
    {
	filterPredRef(_pPred, inSchema, tmpSchema[tmpidx]);
	tmpidx++;
    }

    CegoPredicate** pJoinPred = _joinPredList.First();
    while ( pJoinPred )
    {
	filterPredRef(*pJoinPred, inSchema, tmpSchema[tmpidx]);
	tmpidx++;
	pJoinPred = _joinPredList.Next();
    }
        
    if (_pGroupList)
    {
	CegoAttrDesc** pAD = _pGroupList->First();
	while ( pAD )
	{
	    filterAttrRef(*pAD, inSchema, tmpSchema[tmpidx]);
	    pAD = _pGroupList->Next();
	}
	if ( _pHavingPred )
	{

	    filterPredRef(_pHavingPred, inSchema, tmpSchema[tmpidx]);
	    tmpidx++;	    
	}

    }

    if (_pNativeOrderList)
    {
	CegoExpr** pExpr = _pNativeOrderList->First();
	while ( pExpr )
	{	   
	    ListT<CegoAttrDesc*> attrRefList = (*pExpr)->getAttrRefList();
		    
	    CegoAttrDesc** pAttrRef = attrRefList.First();
	    
	    while ( pAttrRef )
	    {
		filterAttrRef(*pAttrRef, inSchema, tmpSchema[tmpidx]);
		pAttrRef = attrRefList.Next();
	    }
	    pExpr = _pNativeOrderList->Next();
	}
	tmpidx++;
    }

    // merge tmpSchemas to outSchema

    for ( unsigned i=0; i < tmpidx; i++)
    {
	CegoField *pF = tmpSchema[i].First();
	while( pF )
	{
	    if ( outSchema.Find(*pF) == 0 )
		outSchema.Insert(*pF);
	    pF = tmpSchema[i].Next();
	}
    }    
}

void CegoSelect::filterFacRef(CegoFactor *pFac, const ListT<CegoField>& inSchema, ListT<CegoField>& outSchema)
{
    if (pFac->getType() == CegoFactor::EXPR)
    {
	filterExprRef(pFac->getExpr(), inSchema, outSchema);
    }
    else if ( pFac->getType() == CegoFactor::ATTR)
    {
	filterAttrRef(pFac->getAttr(), inSchema, outSchema);
    }
    else if ( pFac->getType() == CegoFactor::FUNCTION )
    {
	CegoFunction* pFunc = pFac->getFunction();

	CegoExpr **pFuncArg = pFunc->getExprList().First();
	while ( pFuncArg )
	{
	    filterExprRef(*pFuncArg, inSchema, outSchema);
	    pFuncArg = pFunc->getExprList().Next();
	}
    }
    else if (  pFac->getType() == CegoFactor::AGGREGATION )
    {
	_selectMode = AGGREGATION;

	if ( pFac->getAggregation()->getExpr() )
	{
	    filterExprRef(pFac->getAggregation()->getExpr(), inSchema, outSchema);
	}
	pFac->getAggregation()->setAggregationId(_nextAid);
	_nextAid++;
    }
    else if (  pFac->getType() == CegoFactor::QUERY )
    {
	pFac->getSelect()->prepare();
	ListT<CegoAttrDesc*> attrRefList = pFac->getAttrRefList();
	CegoAttrDesc** pAttr = attrRefList.First();
	while ( pAttr )
	{
	    filterAttrRef( *pAttr, inSchema, outSchema );
	    pAttr = attrRefList.Next();
	}
    }
    else if (  pFac->getType() == CegoFactor::CASECOND )
    {
	CegoCaseCond *pCaseCond = pFac->getCaseCond();
 
	ListT<CegoField> outSchema2;
	CegoPredicate **pPred = pCaseCond->getPredList().First();
	while ( pPred )
	{
	    filterPredRef(*pPred, inSchema, outSchema2);
	    pPred = pCaseCond->getPredList().Next();
	}
	ListT<CegoField> outSchema3;
	CegoExpr **pExpr = pCaseCond->getExprList().First();
	while ( pExpr )
	{
	    filterExprRef(*pExpr, inSchema, outSchema3);
	    pExpr = pCaseCond->getExprList().Next();
	}
	ListT<CegoField> outSchema4;
	if ( pCaseCond->getElseExpr() )
	    filterExprRef(pCaseCond->getElseExpr(), inSchema, outSchema4);

	// merge outSchemas
	CegoField *pF = outSchema2.First();
	while( pF )
	{
	    if ( outSchema.Find(*pF) == 0 )
		outSchema.Insert(*pF);
	    pF = outSchema2.Next();
	}
	pF = outSchema3.First();
	while( pF )
	{
	    if ( outSchema.Find(*pF) == 0 )
		outSchema.Insert(*pF);
	    pF = outSchema3.Next();
	}
	pF = outSchema4.First();
	while( pF )
	{
	    if ( outSchema.Find(*pF) == 0 )
		outSchema.Insert(*pF);
	    pF = outSchema4.Next();
	}
    }
    else if ( pFac->getType() == CegoFactor::CONDITION)
    {
	filterCondRef(pFac->getCondition(), inSchema, outSchema);
    }
    
}

void CegoSelect::filterExprRef(CegoExpr *pExpr, const ListT<CegoField>& inSchema, ListT<CegoField>& outSchema)
{
    switch ( pExpr->getType() )
    {
    case CegoExpr::ADD:
    case CegoExpr::SUB:
    case CegoExpr::CONCAT:
    {
	filterExprRef(pExpr->getExpr(), inSchema, outSchema);	
	filterTermRef(pExpr->getTerm(), inSchema, outSchema);
	break;
    }
    case CegoExpr::TERM:
    {
	filterTermRef(pExpr->getTerm(), inSchema, outSchema);
	break;
    }
    }
}

void CegoSelect::filterTermRef(CegoTerm *pTerm, const ListT<CegoField>& inSchema, ListT<CegoField>& outSchema)
{   
    switch ( pTerm->getType() )
    {
    case CegoTerm::MUL:
    case CegoTerm::DIV:
    {
	filterTermRef(pTerm->getTerm(), inSchema, outSchema);	
	filterFacRef(pTerm->getFactor(), inSchema, outSchema);
	break;
    }
    case CegoTerm::FACTOR:
    {	
	filterFacRef(pTerm->getFactor(), inSchema, outSchema);
	break;
    }      
    }
}

void CegoSelect::filterAttrRef(CegoAttrDesc* pAttrDesc, const ListT<CegoField>& inSchema, ListT<CegoField>& outSchema)
{    
    CegoField* pF = inSchema.First();
    while ( pF )
    {	    
	if ( ( (Chain)pAttrDesc->getAttrName() == (Chain)pF->getAttrName() 
	       || (Chain)pAttrDesc->getAttrName() == Chain(SELECTION_WILDCARD))
	     && (Chain)pAttrDesc->getTableName() == (Chain)pF->getTableAlias())
	{
	    if ( outSchema.Find(*pF) == 0 )
		outSchema.Insert(*pF);
	    pAttrDesc->setValid(true);
	}
	
	pF = inSchema.Next();
    }
}

void CegoSelect::filterPredRef(CegoPredicate *pP, const ListT<CegoField>& inSchema, ListT<CegoField>& outSchema)
{    
    if ( pP->getMode() == CegoPredicate::CONDITION )
    {
	filterCondRef(pP->getCondition(), inSchema, outSchema);
    }
    else if ( pP->getMode() == CegoPredicate::NOTPRED )
    {
	filterPredRef(pP->getNotPred(), inSchema, outSchema);
    }
    else
    {
	if ( pP->getMode() == CegoPredicate::EXPRONLY 
	     || pP->getMode() == CegoPredicate::EXPRCOMP 
	     || pP->getMode() == CegoPredicate::ISLIKE
	     || pP->getMode() == CegoPredicate::ISNOTLIKE
	     || pP->getMode() == CegoPredicate::ISNCLIKE
	     || pP->getMode() == CegoPredicate::ISNOTNCLIKE
	     || pP->getMode() == CegoPredicate::IN
	     || pP->getMode() == CegoPredicate::NOTIN
	     || pP->getMode() == CegoPredicate::INSUB
	     || pP->getMode() == CegoPredicate::NOTINSUB
	     || pP->getMode() == CegoPredicate::NULLCOMP
	     || pP->getMode() == CegoPredicate::NOTNULLCOMP
	     || pP->getMode() == CegoPredicate::BETWEEN)
	{
	    filterExprRef(pP->getExpr1(), inSchema, outSchema);

	    if ( pP->getMode() == CegoPredicate::EXPRCOMP 
		 || pP->getMode() == CegoPredicate::BETWEEN )
	    {
		filterExprRef(pP->getExpr2(), inSchema, outSchema);
	    }
	    if ( pP->getMode() == CegoPredicate::BETWEEN )
	    {
		filterExprRef(pP->getExpr3(), inSchema, outSchema);
	    }	   	    
	}
	
	if ( pP->getMode() == CegoPredicate::EXISTSCOMP
	     || pP->getMode() == CegoPredicate::INSUB
	     || pP->getMode() == CegoPredicate::NOTINSUB)
	{
	    ListT<CegoSelect*> queryList;
	    pP->getSelectQueryList(queryList);

	    CegoSelect **pSelect = queryList.First();
	    while ( pSelect )
	    {
		ListT<CegoAttrDesc*> attrRefList = (*pSelect)->getAttrRefList();
		CegoAttrDesc **pAD = attrRefList.First();
		while ( pAD )
		{
		    filterAttrRef((*pAD), inSchema, outSchema);
		    pAD = attrRefList.Next();
		}		
		pSelect = queryList.Next();
	    }
	}

	if ( pP->getMode() == CegoPredicate::IN
	     || pP->getMode() == CegoPredicate::NOTIN)
	{
	    filterExprRef(pP->getExpr1(), inSchema, outSchema);

	    ListT<CegoExpr*> exprList = pP->getExprList();
	    CegoExpr** pExpr = exprList.First();
	    while ( pExpr )
	    {
		filterExprRef(*pExpr, inSchema, outSchema);
		pExpr = exprList.Next();
	    }	    
	}       
    }
}

void CegoSelect::filterCondRef(CegoCondition* pC, const ListT<CegoField>& inSchema, ListT<CegoField>& outSchema)
{
    if (pC->Left())
    {
	filterPredRef(pC->Left(), inSchema, outSchema);
    }
    if (pC->Right())
    {
	filterPredRef(pC->Right(), inSchema, outSchema);
    }
}

void CegoSelect::getPredObjectList(CegoPredicate *pPred, ListT<CegoContentObject*>& objectList)
{
    if ( pPred->getCondition() )
    {
	getCondObjectList(pPred->getCondition(), objectList);
    }
    else
    {	
	if ( pPred->getExpr1() )
	{
	    ListT<CegoAttrDesc*> attrRefList = pPred->getExpr1()->getAttrRefList();

	    if ( pPred->getExpr2() )
	    {
		attrRefList += pPred->getExpr2()->getAttrRefList();
	    }

	    if ( pPred->getExpr3() )
	    {
		attrRefList += pPred->getExpr3()->getAttrRefList();
	    }

	    CegoExpr** pExpr = pPred->getExprList().First();
	    while ( pExpr )
	    {
		attrRefList += (*pExpr)->getAttrRefList();
		pExpr = pPred->getExprList().Next();
	    }
	    
	    CegoContentObject** pCO = _coList.First();	
	    while (pCO)
	    {	
		// check for all subCO
		CegoContentObject **pSubCO = (*pCO)->getSubCOList().First();
		while ( pSubCO )
		{		
		    CegoAttrDesc **pAttr = attrRefList.First();
		    
		    while ( pAttr )
		    {	
			if ( (Chain)(*pAttr)->getTableName() == (Chain)(*pSubCO)->getTabName() 
			     || (Chain)(*pAttr)->getTableName() == (Chain)(*pSubCO)->getName())	    
			{			    
			    // check if content object already in object list
			    bool notFound=true;
			    CegoContentObject **pCCO = objectList.First();
			    while ( pCCO && notFound )
			    {
				if ( (Chain)(*pCCO)->getName() == (Chain)(*pCO)->getName())
				    notFound=false;
				else
				    pCCO = objectList.Next();
			    }
			    if ( notFound )
			    {
				objectList.Insert(*pCO);
			    }	      
			}
			pAttr = attrRefList.Next();	    
		    }

		    pSubCO = (*pCO)->getSubCOList().Next();
		    
		}
		pCO = _coList.Next();	    
	    }
	}
    }
}

void CegoSelect::getCondObjectList(CegoCondition* pC, ListT<CegoContentObject*>& objectList)
{
    if (pC->Left())
    {
	getPredObjectList(pC->Left(), objectList);
    }
    if (pC->Right())
    {
	getPredObjectList(pC->Right(), objectList);
    }
}

// the evalReferences method resolves all occuring attribute references to full   
// table information for later processing. In these terms, the defined selection list
// but also all refrences in the condition clause are resolved.

unsigned CegoSelect::evalExtTableReferences(CegoContentObject* pCO, const ListT<CegoField>& fl)
{
    unsigned refCount = evalTableReferences(pCO, fl);
    if ( refCount > 0 )
    {
	// we do not disable cache anymore, rather in case of external references, we add external fields to query id
	// disable cache
	// _cacheEnabled = false;
    }
    // we have to aggregate since this method could be called for several objects
    _extRefCount += refCount;
    return refCount;
}

unsigned CegoSelect::evalTableReferences(CegoContentObject *pCO, const ListT<CegoField>& fl)
{
    unsigned refCount = 0;

    CegoExpr** pExpr = _exprList.First();
    while (pExpr)
    {
	refCount += (*pExpr)->evalReferences(pCO, fl);
	pExpr = _exprList.Next();
    }
    
    if (_pPred)
    {
	// cout << "Eval references for " << _pPred->toChain(0) << " for object " << pCO->getTabName() << endl;
	refCount += _pPred->evalReferences(pCO, fl);
    }

    CegoPredicate **pJoinPred = _joinPredList.First();
    while (pJoinPred)
    {
	// cout << "Eval references for " << (*pJoinPred)->toChain(0) << " for object " << pCO->getTabName() << endl;
	refCount += (*pJoinPred)->evalReferences(pCO, fl);
	pJoinPred = _joinPredList.Next();
    }
    
    CegoPredicate **pP = _viewConjunctionList.First();
    while ( pP )
    {		
	refCount += (*pP)->evalReferences(pCO, fl);
	pP = _viewConjunctionList.Next();
    }
                
    if (_pGroupList)
    {
	CegoAttrDesc **pAD = _pGroupList->First();
	while ( pAD )
	{
	    refCount += (*pAD)->evalReferences(pCO, fl);
	    pAD = _pGroupList->Next();
	}
    }
    
    if (_pNativeOrderList)
    {	
	CegoExpr **pExpr = _pNativeOrderList->First();	
	while ( pExpr )
	{
	    refCount += (*pExpr)->evalReferences(pCO, fl);	
	    pExpr = _pNativeOrderList->Next();
	}	    
    }
    
    if ( _pHavingPred )
    {
	refCount += _pHavingPred->evalReferences(pCO, fl);
    }

    return refCount;
}

void CegoSelect::evalReferences()
{
    
    _joinPredList.Empty();
    CegoContentObject** pCO = _coList.First();

    SetT<Chain> aliasSet;
    
    while (pCO)
    {
	
	if ( (*pCO)->getType() == CegoObject::JOIN )
	{
	    ((CegoJoinObject*)(*pCO))->getPredList(_joinPredList);
	    
	    if ( aliasSet.Insert(((CegoJoinObject*)(*pCO))->getLeftObject()->getTabAlias()) == false )
	    {
		Chain msg = Chain("Duplicate alias ")
		    + ((CegoJoinObject*)(*pCO))->getLeftObject()->getTabAlias()
		    + Chain(" (")
		    + ((CegoJoinObject*)(*pCO))->getLeftObject()->getTabName() + Chain(")");
		throw Exception(EXLOC, msg);	    
	    }
	    
	    if ( aliasSet.Insert(((CegoJoinObject*)(*pCO))->getRightObject()->getTabAlias()) == false )
	    {
		Chain msg = Chain("Duplicate alias ")
		    + ((CegoJoinObject*)(*pCO))->getRightObject()->getTabAlias()
		    + Chain(" (")
		    + ((CegoJoinObject*)(*pCO))->getRightObject()->getTabName() + Chain(")");
		throw Exception(EXLOC, msg);	    
	    }	    
	}
	else
	{	    
	    if ( aliasSet.Insert((*pCO)->getTabAlias()) == false )
	    {
		Chain msg = Chain("Duplicate alias ")
		    + (*pCO)->getTabAlias()
		    + Chain(" (")
		    + (*pCO)->getTabName() + Chain(")");
		throw Exception(EXLOC, msg);
	    }	    
	}
	pCO = _coList.Next();
    }

    pCO = _coList.First();
    while (pCO)
    {	      
	// uncompiled objects are detected here, just treated, if tablemanager is set up
	if ( (*pCO)->getType() == CegoObject::UNDEFINED && _pGTM != 0 )
	{
	    Chain msg = Chain("Invalid object ") + (*pCO)->getTabAlias() + Chain(" (") + (*pCO)->getTabName() + Chain(")");
	    throw Exception(EXLOC, msg);	    
	}

	ListT<CegoField> schema;
	schema = (*pCO)->getSchema();

	evalTableReferences(*pCO, schema);
	
	pCO = _coList.Next();
    }
}

// the evalSelection finalizes the selection procedure by evaluating the expressions defined in 
// the selection part. Either these are plain attributes, expressions or aggregation functions 
// the result is given in the outList parameter

void CegoSelect::evalSelection(ListT<CegoExpr*>& exprList, ListT<CegoField>** pJoinBuf, ListT<CegoField>& ol) 
{    
    if ( exprList.isEmpty() )
    {
	unsigned i=0;
	while ( pJoinBuf[i] )
	{
	    CegoField* pF = pJoinBuf[i]->First();
	    while ( pF ) 
	    {				
		ol.Insert(*pF);
		pF = pJoinBuf[i]->Next();
	    }
	    i++;
	}
	return;
    }

    if ( _selectMode == AGGREGATION || _selectMode == GROUPING )
    {
	CegoExpr** pExpr = exprList.First();
	while ( pExpr )
	{
	    (*pExpr)->getFieldList(ol, pJoinBuf);
	    pExpr = exprList.Next();
	}
    }
    else
    {
	CegoExpr** pExpr = exprList.First();
	CegoField* pEvalField = _evalSchema.First();
	
	while ( pExpr && pEvalField )
	{
	    CegoAttrDesc* pAttrDesc = (*pExpr)->checkAttr();

	    if ( pAttrDesc )
	    {
		if ( pAttrDesc->getAttrName() == Chain(SELECTION_WILDCARD) )
		{	    
		    unsigned i=0;
		    while ( pJoinBuf[i] )
		    {
			CegoField *pF = pJoinBuf[i]->First();

			while ( pF )
			{
			    if ( (Chain)pF->getTableName() ==  (Chain)pAttrDesc->getTableName()
				 || (Chain)pF->getTableAlias() ==  (Chain)pAttrDesc->getTableName() )
			    {			    
				ol.Insert(*pF);
				// skip eval schema entry
				pEvalField = _evalSchema.Next();
			    }
			    pF = pJoinBuf[i]->Next();
			}
			i++;
		    }
		}
		else
		{

		    // merge joinbuf and parent join buf
		    
		    ListT<CegoField>* fl[TABMNG_MAXJOINLEVEL];

		    unsigned i=0;
		    while ( pJoinBuf[i] )
		    {
			fl[i] = pJoinBuf[i];
			i++;
		    }
		    if ( _pParentJoinBuf )
		    {
			unsigned j=0;
			while ( _pParentJoinBuf[j] )
			{
			    fl[i] = _pParentJoinBuf[j];
			    i++;
			    j++;
			}
		    }
		    fl[i] = 0;

		    
		    pEvalField->setValue((*pExpr)->evalFieldValue(fl, _pBlock));

		    ol.Insert ( *pEvalField );		    		    
		}
	    }	
	    else
	    {

		// merge joinbuf and parent join buf
		
		ListT<CegoField>* fl[TABMNG_MAXJOINLEVEL];
		
		unsigned i=0;
		while ( pJoinBuf[i] )
		{
		    fl[i] = pJoinBuf[i];
		    i++;
		}
		if ( _pParentJoinBuf )
		{
		    unsigned j=0;
		    while ( _pParentJoinBuf[j] )
		    {
			fl[i] = _pParentJoinBuf[j];
			i++;
			j++;
		    }
		}
		fl[i] = 0;

		pEvalField->setValue((*pExpr)->evalFieldValue(fl, _pBlock));
		ol.Insert ( *pEvalField );		
	    }
	    pExpr = exprList.Next();
	    pEvalField = _evalSchema.Next();
	}
    }     
    return;   
}

void CegoSelect::encode(char *buf, CegoProcBlock* pBlock)
{
    char* pBuf = (char*)buf;

    unsigned size = 0;
    CegoExpr** pExpr = _exprList.First();
    while (pExpr)
    {
	size += (*pExpr)->getEncodingLength(pBlock);
	pExpr = _exprList.Next();
    }
    
    memcpy( pBuf, &size, sizeof(unsigned));
    pBuf = pBuf + sizeof(unsigned);

    pExpr = _exprList.First();
    while (pExpr)
    {
	(*pExpr)->encode(pBuf, pBlock);
	pBuf = pBuf + (*pExpr)->getEncodingLength(pBlock);
	pExpr = _exprList.Next();
    }

    size = 0;
    CegoContentObject** pCO = _coList.First();
    while (pCO)
    {
	size += (*pCO)->getEntrySize();
	pCO = _coList.Next();
    }

    memcpy( pBuf, &size, sizeof(unsigned));
    pBuf = pBuf + sizeof(unsigned);

    pCO = _coList.First();
    while (pCO)
    {
	(*pCO)->encode(pBuf);
	pBuf = pBuf + (*pCO)->getEntrySize();
	pCO = _coList.Next();
    }

    size = 0;
    if (_pPred)
    {
	size = _pPred->getEncodingLength(pBlock);
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);
	_pPred->encode(pBuf, pBlock);
	pBuf = pBuf + size;
    }
    else
    {
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);
    }
    
    size = 0;
    if (_pGroupList)
    {
	CegoAttrDesc **pAttrDesc = _pGroupList->First();
	while ( pAttrDesc )
	{
	    size += (*pAttrDesc)->getEncodingLength();
	    pAttrDesc = _pGroupList->Next();
	}
       
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);

	pAttrDesc = _pGroupList->First();
	while ( pAttrDesc )
	{
	    (*pAttrDesc)->encode(pBuf);
	    pBuf = pBuf + (*pAttrDesc)->getEncodingLength();
	    pAttrDesc = _pGroupList->Next();
	}

	size = 0;
	if ( _pHavingPred )
	{
	    size = _pHavingPred->getEncodingLength(pBlock);
	    memcpy( pBuf, &size, sizeof(unsigned));
	    pBuf = pBuf + sizeof(unsigned);
	    _pHavingPred->encode(pBuf, pBlock);
	    pBuf = pBuf + size;
	}
	else
	{
	    memcpy( pBuf, &size, sizeof(unsigned));
	    pBuf = pBuf + sizeof(unsigned);
	}
    }
    else
    {
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);	
    }
    size = 0;
    if ( _pOrderList )
    {
	CegoExpr **pExpr = _pOrderList->First();
	while ( pExpr )
	{
	    size += (*pExpr)->getEncodingLength(pBlock);
	    pExpr = _pOrderList->Next();
	}
       
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);
	
	pExpr = _pOrderList->First();
	while (pExpr)
	{
	    (*pExpr)->encode(pBuf, pBlock);
	    pBuf = pBuf + (*pExpr)->getEncodingLength(pBlock);
	    pExpr = _pOrderList->Next();
	}
    }
    else
    {
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);	
    }

    size = 0;
    if ( _pUnionSelect )
    {
	size = _pUnionSelect->getEncodingLength(pBlock);
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);

	_pUnionSelect->encode(pBuf, pBlock);
	pBuf = pBuf + _pUnionSelect->getEncodingLength(pBlock);
    }
    else
    {
	memcpy( pBuf, &size, sizeof(unsigned));
	pBuf = pBuf + sizeof(unsigned);
    }
}

void CegoSelect::decode(char *buf)
{
    char* pBuf = (char*)buf;
    
    unsigned size;
    memcpy( &size, pBuf, sizeof(unsigned));
    pBuf += sizeof(unsigned);
    
    unsigned i = 0;
    while ( i < size )
    {
	CegoExpr *pExpr = new CegoExpr(pBuf);
	unsigned len = pExpr->getEncodingLength();
	
	pBuf += len;
	i += len;
	
	_exprList.Insert( pExpr );
    }

    memcpy( &size, pBuf, sizeof(unsigned));
    pBuf += sizeof(unsigned);
    
    i = 0;
    while ( i < size )
    {
	CegoContentObject *pCO;

	CegoObject obj;
	unsigned size;
	obj.decodeBase(pBuf, size);   
		
	if ( obj.getType() == CegoObject::TABLE )
	{
	    pCO = new CegoTableObject();
	}
	else if ( obj.getType() == CegoObject::VIEW )
	{
	    pCO = new CegoViewObject();
	}
	else if ( obj.getType() == CegoObject::JOIN )
	{
	    pCO = new CegoJoinObject();
	}
	else
	{
	    Chain msg = Chain("Invalid object ") + obj.getName();
	    throw Exception(EXLOC, msg);	    
	}
	pCO->decode(pBuf);	    

	unsigned len = pCO->getEntrySize();
	
	pBuf += len;
	i += len;

	_coList.Insert( pCO );	
    }

    memcpy( &size, pBuf, sizeof(unsigned));
    pBuf += sizeof(unsigned);
    
    if ( size > 0 )
    {
	_pPred = new CegoPredicate(pBuf);
	pBuf += _pPred->getEncodingLength();
    }
    else
    {
	_pPred = 0;
    }

    memcpy( &size, pBuf, sizeof(unsigned));
    pBuf += sizeof(unsigned);

    if ( size > 0 )
    {
	_pGroupList = new ListT<CegoAttrDesc*>;

	i = 0;
	while ( i < size )
	{
	    CegoAttrDesc* pAD = new CegoAttrDesc(pBuf);
	    unsigned len = pAD->getEncodingLength();
	    
	    pBuf += len;
	    i += len;
	    
	    _pGroupList->Insert( pAD );	    
	}
		
	memcpy( &size, pBuf, sizeof(unsigned));
	pBuf += sizeof(unsigned);

	if ( size > 0 )
	{
	    _pHavingPred = new CegoPredicate(pBuf);
	    pBuf += _pHavingPred->getEncodingLength();
	}
	else
	{
	    _pHavingPred = 0;
	}
    }
    else
    {
	_pGroupList = 0;
	_pHavingPred = 0;
    }

    memcpy( &size, pBuf, sizeof(unsigned));
    pBuf += sizeof(unsigned);
    
    if ( size > 0 )
    {
	_pOrderList = new ListT<CegoExpr*>;

	unsigned i = 0;
	while ( i < size )
	{
	    CegoExpr *pExpr = new CegoExpr(pBuf);
	    unsigned len = pExpr->getEncodingLength();
	    
	    pBuf += len;
	    i += len;
	    
	    _pOrderList->Insert( pExpr );
	}
    }
    else
    {
	_pOrderList = 0;
    }

    memcpy( &size, pBuf, sizeof(unsigned));
    pBuf += sizeof(unsigned);
    
    if ( size > 0 )
    {
	_pUnionSelect = new CegoSelect(pBuf);
    }
    else
    {
	_pUnionSelect = 0;
    }
}

unsigned CegoSelect::getEncodingLength(CegoProcBlock* pBlock) const
{    
    unsigned len=0;

    len += sizeof(unsigned);
    CegoExpr** pExpr = _exprList.First();
    while (pExpr)
    {
	len += (*pExpr)->getEncodingLength(pBlock);
	pExpr = _exprList.Next();
    }
    
    len += sizeof(unsigned);
    CegoContentObject** pCO = _coList.First();
    while (pCO)
    {
	len += (*pCO)->getEntrySize();
	pCO = _coList.Next();
    }

    len += sizeof(unsigned);

    if (_pPred)
    {
	len += _pPred->getEncodingLength(pBlock);
    }

    len += sizeof(unsigned);

    if (_pGroupList)
    {
	CegoAttrDesc **pAttrDesc = _pGroupList->First();
	while ( pAttrDesc )
	{
	    len += (*pAttrDesc)->getEncodingLength();
	    pAttrDesc = _pGroupList->Next();
	}

	len += sizeof(unsigned);

	if ( _pHavingPred )
	{
	    len += _pHavingPred->getEncodingLength(pBlock);
	}
    }

    len += sizeof(unsigned);

    if ( _pOrderList )
    {	
	CegoExpr **pExpr = _pOrderList->First();
	while ( pExpr )
	{
	    len += (*pExpr)->getEncodingLength(pBlock);
	    pExpr = _pOrderList->Next();
	}
    }

    len += sizeof(unsigned);

    if (_pUnionSelect)
    {
	len += _pUnionSelect->getEncodingLength(pBlock);
    }

    return len;
}


/* 
   The setViewCond method sets up an outer condition for this select query.
   Especially this is used in case of view statements which are used with outer conditions
   The method reveives the required attribute condition and a field list array for 
   evaluation of the attribute values, set up as parent join buffer
   If a complete evaluation could be made by the query itself, the method returns true.
   Otherwise false is returned and the attribute condition must be evaluated by the outer instance

   With cego version 2.52.20, master predicate parameter was introduced to support inappropriate conditions ( or-conditions)
   In this case, the predicate is added to the view conjunction list and so  might be evaluated in a more efficient way.
   If master predicate is set up, the method returns false ( isCompleted = false ), since it might not be ensured,
   that the master predicate can be fully evaluated
 */

bool CegoSelect::setViewCond(const Chain& viewAlias,  CegoAttrCond& attrCond, CegoPredicate* pPred, ListT<CegoField> **pFLA)
{        
    bool isComplete = true;
    
    CegoPredicate **pP = _viewConjunctionList.First();
    while ( pP )
    {
	delete (*pP);
	pP = _viewConjunctionList.Next();
    }
    _viewConjunctionList.Empty();
    
    if ( attrCond.numComp() > 0 )
    {

	// we have to setup attrcond before retrievig the id
	if ( pFLA )
	    attrCond.setup(**pFLA);

	isComplete = attrCond.asConjunctionList(_exprList, _viewConjunctionList);
	
	_viewCondId = attrCond.getId();
    }
    else
    {
	_viewCondId = Chain();
    }

    if ( pPred )
    {	
	CegoPredicate *pMapPred = pPred->map(viewAlias, _exprList);

	if ( pMapPred )
	{
	    _viewConjunctionList.Insert(pMapPred);
	}
	else
	{
	    isComplete = false;
	}		
    }
    
    cleanUp();

    setParentJoinBuf(pFLA);
	
    if ( _pUnionSelect )
    {
	_pUnionSelect->setViewCond(viewAlias, attrCond, pPred, pFLA);
    }

    return isComplete;
}

Chain CegoSelect::toChain(unsigned defTabSetId, const Chain& indent) const
{
    Chain s;

    s = Chain("select ");

    if ( _isDistinct && _selectMode != AGGREGATION )
	s += Chain(" distinct ");
    
    if ( _exprList.Size() == 0 )
    {
	s += Chain(" * ");
    }
    else
    {
	CegoExpr** pExpr = _exprList.First();
	while (pExpr)
	{
	    s += (*pExpr)->toChain(defTabSetId, indent + indent);
	    if ( (*pExpr)->getAlias() != Chain() )
	    {
		s += Chain(" as ") + (*pExpr)->getAlias();
	    } 
	    pExpr = _exprList.Next();
	    if ( pExpr )
		s += Chain(",\n") + indent + indent;
	}
    }

    if ( _coList.Size() > 0 )
	s += Chain("\n") + indent + Chain("from ");

    CegoContentObject** pCO = _coList.First();
    while (pCO)
    {
	s += (*pCO)->toChain(defTabSetId);
	pCO = _coList.Next();
	if ( pCO )
	    s += Chain(",\n") + indent + indent;
    }
    
    if (_pPred)
    {
	s += Chain("\n") + indent;
	s += Chain("where ");
	s += _pPred->toChain(defTabSetId, indent + indent);
    }

    if (_pGroupList)
    {
	s += Chain("\n") + indent;
	s += Chain("group by ");
	
	CegoAttrDesc **pAttrDesc = _pGroupList->First();
	while ( pAttrDesc )
	{
	    s += (*pAttrDesc)->toChain();
	    pAttrDesc = _pGroupList->Next();
	    if ( pAttrDesc )
		s += Chain(",\n") + indent + indent;
	}
	
	if ( _pHavingPred )
	{
	    s += Chain("\n") + indent;
	    s += Chain("having ");
	    s += _pHavingPred->toChain(defTabSetId, indent);
	}
    }

    if ( _pNativeOrderList )
    {
	s += Chain("\n") + indent;
	s += Chain("order by ");
	
	CegoExpr **pExpr = _pNativeOrderList->First();
	CegoOrderNode::Ordering *pOrdering = _pNativeOrderOptList->First();
	while ( pExpr && pOrdering )
	{
	    s += (*pExpr)->toChain(defTabSetId, indent + indent);

	    if ( *pOrdering == CegoOrderNode::ASC )
		s += Chain(" asc");
	    else
		s += Chain(" desc");
	    
	    pExpr = _pNativeOrderList->Next();
	    if ( pExpr )
		s += Chain(",") + indent + indent;
	}
    }

    if ( _rowLimit > 0 )
    {
	s += Chain("\n") + indent;
	s += Chain("rowlimit ") + Chain(_rowLimit);
    }

    if ( _pUnionSelect )
    {
	s += Chain("\n") + indent;
	s += Chain("union all\n") + indent;
	s += _pUnionSelect->toChain(defTabSetId, indent);
    }
    return s;
}

Chain CegoSelect::dbFormat(CegoDatabaseFormater *pForm) const
{
    return pForm->formatSelect(_selectMode, _isDistinct, _exprList, _coList, _pPred, _pGroupList, _pHavingPred, _pNativeOrderList, _rowLimit, _pUnionSelect);
}

ListT<CegoAttrDesc*> CegoSelect::getAttrRefList() const
{
    ListT<CegoAttrDesc*> attrList;

    CegoContentObject** pCO = _coList.First();
    while ( pCO )
    {	
	if ( (*pCO)->getType() == CegoObject::JOIN )
	{	
	    ListT<CegoPredicate*> predList;
	    ((CegoJoinObject*)(*pCO))->getPredList(predList);
	    
	    CegoPredicate **pPred = predList.First();
	    while (pPred)
	    {
		attrList += (*pPred)->getAttrRefList();
		pPred = predList.Next();
	    }
	}
	pCO = _coList.Next();
    }

    CegoExpr** pExpr = _exprList.First();
    while (pExpr)
    {
	attrList += (*pExpr)->getAttrRefList();
	pExpr = _exprList.Next();
    }

    if (_pPred)
    {
	attrList += _pPred->getAttrRefList();
    }

    if (_pGroupList)
    {    
	CegoAttrDesc **pAttrDesc = _pGroupList->First();
	while ( pAttrDesc )
	{
	    attrList.Insert(*pAttrDesc);
	    pAttrDesc = _pGroupList->Next();
	}
	
	if ( _pHavingPred )
	{
	    attrList += _pHavingPred->getAttrRefList();
	}
    }
    
    if ( _pNativeOrderList )
    {
	CegoExpr **pExpr = _pNativeOrderList->First();
	while ( pExpr )
	{
	    attrList += (*pExpr)->getAttrRefList();
	    pExpr = _pNativeOrderList->Next();
	}	
    } 
    
    return attrList;
}

void CegoSelect::getFieldList(ListT<CegoField>& fl, ListT<CegoField>** pFLA) const
{
    ListT<CegoAttrDesc*> attrRefList = getAttrRefList();
    CegoAttrDesc **pAD = attrRefList.First();
    while ( pAD )
    {	
	fl.Insert(CegoField((*pAD)->getTableName(), (*pAD)->getAttrName()));
	pAD = attrRefList.Next();
    }
}

void CegoSelect::initAggregation()
{
    unsigned nextAid=0;
    
    CegoExpr** pExpr = _exprList.First();
    while ( pExpr )
    {
	ListT<CegoAggregation*> aggList = (*pExpr)->getAggregationList();

	CegoAggregation **pAgg = aggList.First();

	while ( pAgg )
	{	    
	    if ( (*pAgg)->getType() == CegoAggregation::COUNT )
	    {
		(*pAgg)->setFieldValue( CegoFieldValue(LONG_TYPE, Chain("0")));
	    }
	    else if ( (*pAgg)->getType() == CegoAggregation::MIN
		      || (*pAgg)->getType() == CegoAggregation::MAX )
	    {
		CegoFieldValue fv;
                (*pAgg)->setFieldValue(fv);
	    }
	    else // SUM / AVG
	    {
		CegoField f = (*pExpr)->evalField(_joinFields, true);
                // cout << "Type is " << f.getType() << " " << f.getDim() <<  endl;
                if ( f.getType() == DECIMAL_TYPE )
                {
                   BigDecimal d = BigDecimal("0").scaleTo(f.getDim());
                   // cout << "Setting init value to " << d.toChain() << endl;
                   (*pAgg)->setFieldValue( CegoFieldValue(f.getType(), d.toChain()));
                }
                else
                {
		    (*pAgg)->setFieldValue( CegoFieldValue(f.getType(), Chain("0")));
                }
	    }
	    (*pAgg)->setAggregationId(nextAid);
	    nextAid++;
	    
	    pAgg = aggList.Next();
	}
	pExpr = _exprList.Next();
    }   
}

void CegoSelect::aggregateTuple(ListT<CegoField>& aggTuple)
{
    CegoExpr** pExpr = _exprList.First();
    while ( pExpr )
    {       
	CegoQueryHelper::aggregateTuple(aggTuple, *pExpr);
	pExpr = _exprList.Next();
    }    
}

void CegoSelect::evalAggregation(ListT<CegoField>& resultList)
{   
    CegoExpr** pExpr = _exprList.First();
    while ( pExpr )
    {	
	ListT<CegoAggregation*> aggList = (*pExpr)->getAggregationList();

	CegoAggregation **pAgg = aggList.First();
	
	while ( pAgg )
	{	    
	    if ( (*pAgg)->getType() == CegoAggregation::AVG )
	    {
		CegoFieldValue avg;
		if ( _aggregationCount > 0 && (*pAgg)->getFieldValue().isNull() == false )
		{
		    CegoFieldValue fv((*pAgg)->getFieldValue().getType(), Chain(_aggregationCount));
		    avg = (CegoFieldValue)(*pAgg)->getFieldValue() / fv ; 
		}
		(*pAgg)->setFieldValue(avg);
	    }
	    pAgg = aggList.Next();
	}
	
	bool notFound=true;
	
	CegoFieldValue fv = (*pExpr)->evalFieldValue(0,0);
	
	// is the aggregation is associated with an alias ?
	// this may happen for aggregation in views
	CegoField* pF = resultList.First();
	while ( pF && notFound )
	{
	    if ( pF->getAttrName() != Chain() && pF->getAttrName() == (*pExpr)->getAlias() )
	    {
		pF->setValue(fv);
		notFound = false;
	    }
	    pF = resultList.Next();
	}
	
	// if native aggregation, we add it to list
	if (  notFound )
	    resultList.Insert(CegoField(fv));

	pExpr = _exprList.Next();
    }
}

void CegoSelect::prepareOrder()
{
#ifdef CGDEBUG
    _pDBMng->log(_modId, Logger::DEBUG, Chain("Prepare order ..."));	
#endif

    if ( _selectMode == AGGREGATION )
	_aggDone=false;
        
    if ( _pGroupList )
    {
	_groupingDone = false;

	if ( _pGroupSpace == 0 )
	    _pGroupSpace = new CegoGroupSpace();
	else
	    _pGroupSpace->resetGroupSpace();

	if ( _selectMode == AGGREGATION )
	    _selectMode = GROUPING;
	else
	    throw Exception(EXLOC, "Missing aggregation for group clause");
    }
    
    if ( _isDistinct && _selectMode != GROUPING )
    {
	if ( _pNativeOrderList == 0 )
	{	    
	    _pOrderList = new ListT<CegoExpr*>;
	    _pOrderOptList = new ListT<CegoOrderNode::Ordering>;
	    
	    _dcount=0;
	    CegoExpr** pExpr = _exprList.First();
	    while (pExpr)
	    {
		if ( _selectMode == AGGREGATION )
		{
		    ListT<CegoAttrDesc*> attrList = (*pExpr)->getAttrRefList();
		    CegoAttrDesc **pAD = attrList.First();
		    while ( pAD )
		    {
			CegoExpr* pAggExp = new CegoExpr( 
			    new CegoTerm( 
				new CegoFactor( *pAD, true)));
			_pOrderList->Insert(pAggExp);
			_pOrderOptList->Insert(CegoOrderNode::ASC);
			
			_dcount++;
			pAD = attrList.Next();
		    }
		}
		else
		{
		    _pOrderList->Insert((*pExpr)->clone(true));
		    _pOrderOptList->Insert(CegoOrderNode::ASC);
		    _dcount++;
		}	
		pExpr = _exprList.Next();
	    }
	}
	else
	{
	    _pOrderList = new ListT<CegoExpr*>;
	    _pOrderOptList = new ListT<CegoOrderNode::Ordering>;	    
	    _dcount=0;
	    
	    CegoExpr** pExpr = _pNativeOrderList->First();
	    CegoOrderNode::Ordering* pOrd = _pNativeOrderOptList->First();
	    while ( pExpr && pOrd )
	    {
		_pOrderList->Insert((*pExpr)->clone(true));
		_pOrderOptList->Insert(*pOrd);
		_dcount++;
		pExpr = _pNativeOrderList->Next();
		pOrd = _pNativeOrderOptList->Next();	  
	    }	
	    
	    if ( _selectMode != AGGREGATION )
	    {	
		pExpr = _exprList.First();
		while ( pExpr )
		{
		    CegoAttrDesc* pAttrDesc1 = (*pExpr)->checkAttr();
		    if ( pAttrDesc1 )
		    {
			bool found=false;
			CegoExpr** pOrderExpr = _pNativeOrderList->First();
			while ( pOrderExpr && found == false )
			{
			    CegoAttrDesc* pAttrDesc2 = (*pOrderExpr)->checkAttr();
			    if ( pAttrDesc2 )
			    {
				if ( pAttrDesc1->getAttrName() == pAttrDesc2->getAttrName() )
				{
				    found = true;
				}
			    }
			    pOrderExpr = _pNativeOrderList->Next();
			}
			if ( found == false )
			{
			    _pOrderList->Insert((*pExpr)->clone(true));
			    _pOrderOptList->Insert(CegoOrderNode::ASC);
			    _dcount++;		    
			}		
		    }
		    else
		    {
			_pOrderList->Insert((*pExpr)->clone(true));
			_pOrderOptList->Insert(CegoOrderNode::ASC);
			_dcount++;
		    }
		    pExpr = _exprList.Next();
		}
	    }
	}
    }
    else
    {
	if ( _pNativeOrderList )
	{
	    _pOrderList = new ListT<CegoExpr*>;
	    _pOrderOptList = new ListT<CegoOrderNode::Ordering>;
	    
	    CegoExpr** pExpr = _pNativeOrderList->First();
	    CegoOrderNode::Ordering *pOrdering = _pNativeOrderOptList->First();
	    while (pExpr && pOrdering)
	    {
		_pOrderList->Insert((*pExpr)->clone(true));
		_pOrderOptList->Insert(*pOrdering);
		pExpr = _pNativeOrderList->Next();
		pOrdering = _pNativeOrderOptList->Next();
	    }
	}
    }

    if ( _pOrderList )
    {
	_orderingDone=false;
	if ( _pOrderSpace == 0 )
	    _pOrderSpace = new CegoOrderSpace();
	else
	    _pOrderSpace->resetOrderSpace();	    
    }

    return;
}
 
void CegoSelect::setExtRefCount(unsigned refCount)
{
    _extRefCount = refCount;
}

unsigned CegoSelect::getExtRefCount() const
{
    return _extRefCount;
}

CegoSelect* CegoSelect::clone(bool isAttrRef)
{    
    ListT<CegoExpr*> exprList;
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr )
    {
	exprList.Insert((*pExpr)->clone(isAttrRef));
	pExpr = _exprList.Next();
    }
    
    CegoPredicate *pPred = 0;
    if ( _pPred )
    {
	pPred = _pPred->clone(isAttrRef);
    }

    ListT<CegoAttrDesc*> *pGroupList = 0;
    
    if ( _pGroupList )
    {
	pGroupList = new ListT<CegoAttrDesc*>;
	CegoAttrDesc **pAttrDesc = _pGroupList->First();
	while ( pAttrDesc )
	{
	    if ( isAttrRef )
		pGroupList->Insert(*pAttrDesc);
	    else
		pGroupList->Insert((*pAttrDesc)->clone());
	    pAttrDesc = _pGroupList->Next();
	}
    }
    
    CegoPredicate *pHavingPred = 0;
    if ( _pHavingPred )
    {
	pHavingPred = _pHavingPred->clone(isAttrRef);
    }

    ListT<CegoExpr*> *pOrderList = 0;
    if ( _pNativeOrderList )
    {
	pOrderList = new ListT<CegoExpr*>;
	CegoExpr **pExpr = _pNativeOrderList->First();
	while ( pExpr )
	{
	    pOrderList->Insert((*pExpr)->clone(isAttrRef));
	    pExpr = _pNativeOrderList->Next();
	}
    }
    
    ListT<CegoOrderNode::Ordering>* pOrderOptList = 0;
    if ( _pNativeOrderOptList )
    {
	pOrderOptList = new ListT<CegoOrderNode::Ordering>;
	CegoOrderNode::Ordering *pOrder = _pNativeOrderOptList->First();
	while ( pOrder )
	{
	    pOrderOptList->Insert(*pOrder);
	    pOrder = _pNativeOrderOptList->Next();
	}
    }

    ListT<CegoContentObject*> coList;
    CegoContentObject **pCO = _coList.First();
    while ( pCO )
    {
	CegoContentObject *pCloneCO = (*pCO)->clone(isAttrRef);	
	coList.Insert(pCloneCO);	
	pCO = _coList.Next();
    }
    
    CegoSelect *pClone = new CegoSelect(coList,
					exprList, 
					pPred,
					pGroupList,
					pHavingPred,
					pOrderList,
					pOrderOptList,
					_isDistinct,
					_rowLimit,
					_pGTM);        

    // copy several private variables
    pClone->_selectMode = _selectMode;
    pClone->_tabSetId = _tabSetId;
    pClone->_extRefCount = _extRefCount;

    if ( _pGTM ) 
	// we have to check for valid query cache during each clone procedure
	pClone->_pCache = _pGTM->getDBMng()->getQueryCache(_tabSetId);
 	    
    if ( pClone->_pCache && _cacheEnabled )
	pClone->_cacheEnabled = true;
    else
	pClone->_cacheEnabled = false;

    if ( _pParentJoinBuf )
	pClone->setParentJoinBuf(_pParentJoinBuf);
    if ( _pBlock )
	pClone->setProcBlock(_pBlock);
    if ( _pUnionSelect )
	pClone->setUnionSelect(_pUnionSelect->clone(isAttrRef));
    if ( _isPrepared )
	pClone->prepare();
	    
    return pClone;
}


void CegoSelect::mergeBuffer()
{
    // merge join buf and parent join buf

    if ( _pParentJoinBuf )
    {		
	unsigned i=0;
	while ( _joinBuf[i] )
	{	    
	    _mergeBuf[i] = _joinBuf[i];
	    i++;
	}
	
	// now we take parent join buf we a lower priority
	
	unsigned j=0;
	while ( _pParentJoinBuf[j] )
	{	    
	    _mergeBuf[i] = _pParentJoinBuf[j];	    
	    i++;
	    j++;

	}
		
	// terminate array
	_mergeBuf[i]=0;
	_pMergedBuf = _mergeBuf;
    }
    else
    {		    	
	_pMergedBuf = _joinBuf;
    }
}
