///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoExpr.cc
// -----------
// Cego expression class implementation
//                                                
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoExpr
//
// Description: Query expression container class
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// CEGO INCLUDES
#include "CegoExpr.h"
#include "CegoXMLdef.h"
#include "CegoDatabaseFormater.h"

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

CegoExpr::CegoExpr(char* buf)
{
    _pTerm=0;
    _pExpr=0;
    _isAgg = false;
    _isExtern = false;
    decode(buf);
}

CegoExpr::CegoExpr()
{
    _pTerm=0;
    _pExpr=0;
    _isAgg = false;
    _isExtern = false;
}

CegoExpr::CegoExpr(CegoExpr *pExpr, CegoTerm *pTerm, ExpType expType, const Chain& alias)
{
    _expType = expType;
    _pExpr=pExpr;
    _pTerm=pTerm;
    _isAgg = false;
    _alias = alias;
    _isExtern = false;
}

CegoExpr::CegoExpr(CegoTerm *pTerm, const Chain& alias)
{
    _expType = TERM;
    _pTerm=pTerm;
    _pExpr = 0;
    _isAgg = false;
    _alias = alias;
    _isExtern = false;
}

CegoExpr::~CegoExpr()
{
    if ( _pExpr )
	delete _pExpr;
    if ( _pTerm )
	delete _pTerm;
}

void CegoExpr::cleanUp()
{
    if ( _pExpr )
	_pExpr->cleanUp();
    if ( _pTerm )
	_pTerm->cleanUp();
}

void CegoExpr::setQueryManager(CegoQueryManager* pTabMng)
{
    if ( _pTerm )
	_pTerm->setQueryManager(pTabMng);
    if ( _pExpr )
	_pExpr->setQueryManager(pTabMng);
}

void CegoExpr::setTabSetId(unsigned tabSetId)
{
    if ( _pExpr )
	_pExpr->setTabSetId(tabSetId);
    if ( _pTerm )
	_pTerm->setTabSetId(tabSetId);
}

void CegoExpr::setAlias(const Chain& alias)
{
    _alias = alias;
}

const Chain& CegoExpr::getAlias() const 
{
    return _alias;
}

bool CegoExpr::checkConst() const
{
    if ( _expType == TERM)
    {
	return _pTerm->checkConst();
    }
    else
    {
	return _pExpr->checkConst() && _pTerm->checkConst();
    }
}

bool CegoExpr::checkVar(Chain& var) const
{
    if ( _pTerm )
    {
	return _pTerm->checkVar(var);
    }
    return false;
}

CegoAttrDesc* CegoExpr::checkAttr() const
{
    if ( _pTerm && _expType == TERM)
    {
	return _pTerm->checkAttr();
    }
    return 0;
}

CegoCondition* CegoExpr::checkCondition() const
{
    if ( _pTerm && _expType == TERM)
    {
	return _pTerm->checkCondition();
    }
    return 0;
}


bool CegoExpr::checkLob(CegoFieldValue& fv, CegoProcBlock *pBlock)
{
    if ( _pTerm && _expType == TERM)
    {
	return _pTerm->checkLob(fv, pBlock);
    }
    return false;
}

void CegoExpr::getFunctionList(SetT<CegoObject>& funcList)
{
    if ( _pExpr )
	_pExpr->getFunctionList(funcList);
    if ( _pTerm )
	_pTerm->getFunctionList(funcList);
}

void CegoExpr::getSelectQueryList(ListT<CegoSelect*>& queryList)
{
    if ( _pTerm )
    {
	_pTerm->getSelectQueryList(queryList);
    }
    if ( _pExpr )
    {
	_pExpr->getSelectQueryList(queryList);
    }
}

void CegoExpr::clearAttrCache()
{
    if ( _pTerm )
    {
	_pTerm->clearAttrCache();
    }
    if ( _pExpr )
    {
	_pExpr->clearAttrCache();
    }
}

void CegoExpr::getFieldList(ListT<CegoField>& fl, ListT<CegoField>** pFLA) const
{
    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
    {
	_pExpr->getFieldList(fl, pFLA);
	_pTerm->getFieldList(fl, pFLA);
	break;
    }
    case TERM:
    {
	_pTerm->getFieldList(fl, pFLA);
	break;
    }
    }
}

ListT<CegoAttrDesc*> CegoExpr::getAttrRefList() const
{    
    ListT<CegoAttrDesc*> attrList;
    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
    {
	attrList = _pExpr->getAttrRefList() + _pTerm->getAttrRefList();
	break;
    }
    case TERM:
    {
	attrList = _pTerm->getAttrRefList();
	break;
    }
    }
    return attrList;
}

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

    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
    {
	refCount += _pExpr->evalReferences(pCO, fl);
	refCount += _pTerm->evalReferences(pCO, fl);
	break;
    }
    case TERM:
    {
	refCount+= _pTerm->evalReferences(pCO, fl);
	break;
    }
    }

    return refCount;
}

CegoField CegoExpr::evalField(const ListT<CegoField>& fl, bool graceful) const
{
    CegoField f;

    switch ( _expType )
    {
    case ADD:
    {
	CegoField f1 =  _pExpr->evalField(fl, graceful);
	CegoField f2 =  _pTerm->evalField(fl, graceful);

	// the type conversion refers to method : CegoFieldValue operator + ( const CegoFieldValue& fv1, const CegoFieldValue& fv2 )
	CegoDataType dt;
	unsigned len;
	unsigned dim;
	
	if ( f1.getType() == DECIMAL_TYPE )
	{
	    dt = f1.getType();
	    len = f1.getLength();
	    dim = f1.getDim();
	}
	else if ( f2.getType() == DECIMAL_TYPE )
	{
	    dt = f2.getType();
	    len = f2.getLength();
	    dim = f2.getDim();
	}
	else
	{
	    dt =  f1.getLength() > f2.getLength() ? f1.getType() : f2.getType();
	    len = f1.getLength() > f2.getLength() ? f1.getLength() : f2.getLength();
	    dim = f1.getLength() > f2.getLength() ? f1.getDim() : f2.getDim();
	}
	
	f = CegoField( Chain("EXPR"),
		       Chain("EXPR"),
		       Chain("(") + f1.getAttrName() + Chain("+") + f2.getAttrName() + Chain(")"),
		       dt, len, dim);
	break;	

    }
    case SUB:
    {
	CegoField f1 =  _pExpr->evalField(fl, graceful);
	CegoField f2 =  _pTerm->evalField(fl, graceful);

	CegoDataType dt;
	unsigned len;
	unsigned dim;
	
	if ( f1.getType() == DECIMAL_TYPE )
	{
	    dt = f1.getType();
	    len = f1.getLength();
	    dim = f1.getDim();
	}
	else if ( f2.getType() == DECIMAL_TYPE )
	{
	    dt = f2.getType();
	    len = f2.getLength();
	    dim = f2.getDim();
	}
	else
	{
	    dt =  f1.getLength() > f2.getLength() ? f1.getType() : f2.getType();
	    len = f1.getLength() > f2.getLength() ? f1.getLength() : f2.getLength();
	    dim = f1.getLength() > f2.getLength() ? f1.getDim() : f2.getDim();
	}
		
	f = CegoField( Chain("EXPR"),
		       Chain("EXPR"),
		       Chain("(") + f1.getAttrName() + Chain("-") + f2.getAttrName() + Chain(")"),
		       dt, len, dim);
	break;	

    }
    case CONCAT:
    {
	f = CegoField( Chain("EXPR"), Chain("EXPR"), toChain(0), VARCHAR_TYPE, 20 );
	break;
    }
    case TERM:
    {
	f = _pTerm->evalField(fl, graceful);
	break;
    }
    }
    
    if ( _alias != Chain() )
    {	
	f.setAttrName(_alias);
    }
    return f;
}

CegoFieldValue CegoExpr::evalFieldValue(ListT<CegoField>** pFLA, CegoProcBlock *pBlock) const
{
    switch ( _expType )
    {
    case ADD:
    {
	return ( _pExpr->evalFieldValue(pFLA, pBlock) + _pTerm->evalFieldValue(pFLA, pBlock) );	
    }
    case SUB:
    {
	return ( _pExpr->evalFieldValue(pFLA, pBlock) - _pTerm->evalFieldValue(pFLA, pBlock) );
    }
    case CONCAT:
    {	
	Chain con = _pExpr->evalFieldValue(pFLA, pBlock).valAsChain() + _pTerm->evalFieldValue(pFLA, pBlock).valAsChain();
	CegoFieldValue  fv(VARCHAR_TYPE, con );
	return fv;
    }
    case TERM:
    {
	return ( _pTerm->evalFieldValue(pFLA, pBlock) );
    }
    default:
        throw Exception(EXLOC, Chain("Unknown expression type"));
    }
}

CegoExpr::ExpType CegoExpr::getType() const
{
    return _expType;
}

CegoTerm *CegoExpr::getTerm() const
{
    return _pTerm;
}

CegoExpr *CegoExpr::getExpr() const
{
    return _pExpr;
}

ListT<CegoAggregation*>& CegoExpr::getAggregationList()
{
    if ( _isAgg == false )
    {
	switch ( _expType )
	{
	case ADD:
	case SUB:
	case CONCAT:
	    _aggList =  _pExpr->getAggregationList() + _pTerm->getAggregationList();
	    break;
	case TERM:
	    _aggList = _pTerm->getAggregationList();
	    break;
	}
	_isAgg = true;
    }

    return _aggList;
}

CegoExpr* CegoExpr::clone(bool isAttrRef)
{
    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
	return ( new CegoExpr( _pExpr->clone(isAttrRef), _pTerm->clone(isAttrRef), _expType, _alias ) );	
    case TERM:
	return ( new CegoExpr( _pTerm->clone(isAttrRef), _alias ) );
    default:
        throw Exception(EXLOC, Chain("Unknown expression type"));
    }    
}    

CegoExpr& CegoExpr::operator = ( const CegoExpr& e)
{
    _alias = e._alias;
    _expType = e._expType;
    _pExpr = e._pExpr;
    _pTerm = e._pTerm;
    _isExtern = e._isExtern;
    return (*this);
}

Chain CegoExpr::getId(CegoProcBlock *pBlock) const
{
    Chain s;
    switch ( _expType )
    {
    case CegoExpr::ADD:
	s = _pExpr->getId(pBlock) + "+" + _pTerm->getId(pBlock);
	break;
    case CegoExpr::SUB:
	s = _pExpr->getId(pBlock) + "-" + _pTerm->getId(pBlock);
	break;
    case CegoExpr::CONCAT:
	s = _pExpr->getId(pBlock) + "|" + _pTerm->getId(pBlock);
	break;
    case CegoExpr::TERM:
	s = _pTerm->getId(pBlock);
	break;
    }
    return s;
}

Chain CegoExpr::toChain(unsigned defTabSetId, const Chain& indent) const
{
    Chain s;
    switch ( _expType )
    {
    case CegoExpr::ADD:
	s = _pExpr->toChain(defTabSetId, indent) + " + " + _pTerm->toChain(defTabSetId, indent);
	break;
    case CegoExpr::SUB:
	s = _pExpr->toChain(defTabSetId, indent) + " - " + _pTerm->toChain(defTabSetId, indent);
	break;
    case CegoExpr::CONCAT:
	s = _pExpr->toChain(defTabSetId, indent) + " | " + _pTerm->toChain(defTabSetId, indent);
	break;
    case CegoExpr::TERM:
	s = _pTerm->toChain(defTabSetId, indent);
	break;
    }
    return s;
}

Chain CegoExpr::dbFormat(CegoDatabaseFormater *pForm) const
{
    return pForm->formatExpr(_expType, _pExpr, _pTerm);
}

void CegoExpr::encode(char *buf, CegoProcBlock *pBlock)
{   
    char* pE = (char*)buf;

    memcpy( pE, &_expType, sizeof(CegoExpr::ExpType));
    pE = pE + sizeof(CegoExpr::ExpType);

    switch (_expType )
    {
    case CegoExpr::ADD:
    case CegoExpr::SUB:
    case CegoExpr::CONCAT:
    {
	_pExpr->encode(pE, pBlock);
	pE = pE + _pExpr->getEncodingLength(pBlock);
	_pTerm->encode(pE, pBlock);
	pE = pE + _pTerm->getEncodingLength(pBlock);
	break;
    }
    case CegoExpr::TERM:
    {
	_pTerm->encode(pE, pBlock);
	pE = pE + _pTerm->getEncodingLength(pBlock);
	break;
    }
    }
}

void CegoExpr::decode(char *buf)
{
    char* pE = (char*)buf;

    memcpy( &_expType, pE,  sizeof(CegoExpr::ExpType));
    pE = pE + sizeof(CegoExpr::ExpType);

    switch (_expType )
    {
    case CegoExpr::ADD:
    case CegoExpr::SUB:
    case CegoExpr::CONCAT:
    {
	_pExpr = new CegoExpr(pE);
	pE = pE + _pExpr->getEncodingLength();
	_pTerm = new CegoTerm(pE);
	pE = pE + _pTerm->getEncodingLength();
	break;
    }
    case CegoExpr::TERM:
    {
	_pExpr=0;
	_pTerm = new CegoTerm(pE);
	pE = pE + _pTerm->getEncodingLength();
	break;
    }
    }
}

unsigned CegoExpr::getEncodingLength(CegoProcBlock* pBlock) const
{
    unsigned len = 0;
    
    len += sizeof(CegoExpr::ExpType);

    switch (_expType )
    {
    case CegoExpr::ADD:
    case CegoExpr::SUB:
    case CegoExpr::CONCAT:
    {
	len +=_pExpr->getEncodingLength(pBlock);
	len +=_pTerm->getEncodingLength(pBlock);
	break;
    }
    case CegoExpr::TERM:
    {
	len +=_pTerm->getEncodingLength(pBlock);
	break;
    }
    }

    return len;
}

void CegoExpr::getPlanList(ListT<Element*>& planList)
{
    if ( _pTerm )
    {
	_pTerm->getPlanList(planList);
    }
    if ( _pExpr )
    {
	_pExpr->getPlanList(planList);
    }
}

ostream& operator << (ostream& s, const CegoExpr& e)
{
    s << e.toChain(0);
    return s;
}

CegoExpr* CegoExpr::map(const Chain& viewAlias, const ListT<CegoExpr*>& exprList) const
{
    CegoExpr* pMapExpr = 0;
    CegoTerm* pMapTerm = 0;

    bool doMap=true;
    
    if ( _pTerm )
    {
	pMapTerm = _pTerm->map(viewAlias, exprList);
	if ( pMapTerm == 0 )
	    doMap=false;
    }
    if ( _pExpr && doMap == true)
    {
	if ( _alias == viewAlias )
	{
	    pMapExpr = _pExpr->map(viewAlias, exprList);
	    if ( pMapExpr == 0 )
		doMap=false;
	}
	else
	    doMap = false;	
    }
    
    if ( doMap == false )
    {
	if ( pMapTerm )
	    delete pMapTerm;
	if ( pMapExpr )
	    delete pMapExpr;
	return 0;
    }
       
    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
	return new CegoExpr(pMapExpr, pMapTerm,  _expType, _alias );	
    case TERM:
	return new CegoExpr( pMapTerm, _alias );
    default:
        throw Exception(EXLOC, Chain("Unknown expression type"));
    }
}
