/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <file/fcomp.hxx>
#include <tools/debug.hxx>
#include <connectivity/sqlparse.hxx>
#include <file/fanalyzer.hxx>
#include <com/sun/star/util/Date.hpp>
#include <com/sun/star/util/DateTime.hpp>
#include <com/sun/star/util/Time.hpp>
#include <com/sun/star/sdbc/XColumnLocate.hpp>
#include <connectivity/dbexception.hxx>
#include <connectivity/dbconversion.hxx>
#include <com/sun/star/sdb/SQLFilterOperator.hpp>
#include <file/FStringFunctions.hxx>
#include <file/FDateFunctions.hxx>
#include <file/FNumericFunctions.hxx>
#include <file/FConnection.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <sqlbison.hxx>
#include <strings.hrc>

using namespace connectivity;
using namespace connectivity::file;
using namespace com::sun::star::uno;
using namespace com::sun::star::sdbc;
using namespace com::sun::star::sdb;
using namespace ::com::sun::star::container;
using namespace com::sun::star;

OPredicateCompiler::OPredicateCompiler(OSQLAnalyzer* pAnalyzer)//,OCursor& rCurs)
                     : m_pAnalyzer(pAnalyzer)
                     , m_nParamCounter(0)
{
}


OPredicateCompiler::~OPredicateCompiler()
{
    Clean();
}

void OPredicateCompiler::dispose()
{
    Clean();
    m_orgColumns        = nullptr;
}

void OPredicateCompiler::start(OSQLParseNode const * pSQLParseNode)
{
    if (!pSQLParseNode)
        return;

    m_nParamCounter = 0;
    // analyse Parse Tree (depending on Statement-type)
    // and set pointer on WHERE-clause:
    OSQLParseNode * pWhereClause = nullptr;

    if (SQL_ISRULE(pSQLParseNode,select_statement))
    {
        OSQLParseNode * pOrderbyClause = nullptr;
        DBG_ASSERT(pSQLParseNode->count() >= 4,"OFILECursor: Error in Parse Tree");

        OSQLParseNode * pTableExp = pSQLParseNode->getChild(3);
        assert(pTableExp && "Error in Parse Tree");
        DBG_ASSERT(SQL_ISRULE(pTableExp,table_exp)," Error in Parse Tree");
        DBG_ASSERT(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"Error in Parse Tree");

        // check that we don't use anything other than count(*) as function
        OSQLParseNode* pSelection = pSQLParseNode->getChild(2);
        if ( SQL_ISRULE(pSelection,scalar_exp_commalist) )
        {
            for (size_t i = 0; i < pSelection->count(); i++)
            {
                OSQLParseNode *pColumnRef = pSelection->getChild(i)->getChild(0);
                if ( SQL_ISRULE(pColumnRef,general_set_fct) && pColumnRef->count() != 4 )
                {
                    m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_COMPLEX_COUNT,nullptr);
                }
            }
        }


        pWhereClause    = pTableExp->getChild(1);
        pOrderbyClause  = pTableExp->getChild(ORDER_BY_CHILD_POS);
        (void)pOrderbyClause;
    }
    else if (SQL_ISRULE(pSQLParseNode,update_statement_searched))
    {
        DBG_ASSERT(pSQLParseNode->count() == 5,"OFILECursor: Error in Parse Tree");
        pWhereClause = pSQLParseNode->getChild(4);
    }
    else if (SQL_ISRULE(pSQLParseNode,delete_statement_searched))
    {
        DBG_ASSERT(pSQLParseNode->count() == 4,"Error in Parse Tree");
        pWhereClause = pSQLParseNode->getChild(3);
    }
    else
            // Other Statement. no selection-criteria
        return;

    if (SQL_ISRULE(pWhereClause,where_clause))
    {
        // a where-clause is not allowed to be empty:
        DBG_ASSERT(pWhereClause->count() == 2,"OFILECursor: Error in Parse Tree");

        OSQLParseNode * pComparisonPredicate = pWhereClause->getChild(1);
        DBG_ASSERT(pComparisonPredicate != nullptr,"OFILECursor: Error in Parse Tree");

        execute( pComparisonPredicate );
    }
    else
    {
        // The where-clause is optionally in the majority of cases, i.e. it might be an "optional-where-clause".
        DBG_ASSERT(SQL_ISRULE(pWhereClause,opt_where_clause),"OPredicateCompiler: Error in Parse Tree");
    }
}


OOperand* OPredicateCompiler::execute(OSQLParseNode const * pPredicateNode)
{
    OOperand* pOperand = nullptr;
    if (pPredicateNode->count() == 3 &&                         // Expression is bracketed
        SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"(") &&
        SQL_ISPUNCTUATION(pPredicateNode->getChild(2),")"))
    {
        execute(pPredicateNode->getChild(1));
    }
    else if ((SQL_ISRULE(pPredicateNode,search_condition) || SQL_ISRULE(pPredicateNode,boolean_term))
                            &&          // AND/OR-linkage:
                            pPredicateNode->count() == 3)
    {
        execute(pPredicateNode->getChild(0));                           // process the left branch
        execute(pPredicateNode->getChild(2));                           // process the right branch

        if (SQL_ISTOKEN(pPredicateNode->getChild(1),OR))                // OR-Operator
        {
            m_aCodeList.emplace_back(new OOp_OR);
        }
        else if (SQL_ISTOKEN(pPredicateNode->getChild(1),AND))      // AND-Operator
            m_aCodeList.emplace_back(new OOp_AND);
        else
        {
            OSL_FAIL("OPredicateCompiler: Error in Parse Tree");
        }
    }
    else if (SQL_ISRULE(pPredicateNode,boolean_factor))
    {
        execute(pPredicateNode->getChild(1));
        m_aCodeList.emplace_back(new OOp_NOT);
    }
    else if (SQL_ISRULE(pPredicateNode,comparison_predicate))
    {
        execute_COMPARE(pPredicateNode);
    }
    else if (SQL_ISRULE(pPredicateNode,like_predicate))
    {
        execute_LIKE(pPredicateNode);
    }
    else if (SQL_ISRULE(pPredicateNode,between_predicate))
    {
        execute_BETWEEN(pPredicateNode);
    }
    else if (SQL_ISRULE(pPredicateNode,test_for_null))
    {
        execute_ISNULL(pPredicateNode);
    }
    else if(SQL_ISRULE(pPredicateNode,num_value_exp))
    {
        execute(pPredicateNode->getChild(0));                           // process the left branch
        execute(pPredicateNode->getChild(2));                           // process the right branch
        if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"+"))
        {
            m_aCodeList.emplace_back(new OOp_ADD);
        }
        else if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"-"))
            m_aCodeList.emplace_back(new OOp_SUB);
        else
        {
            OSL_FAIL("OPredicateCompiler: Error in Parse Tree num_value_exp");
        }
    }
    else if(SQL_ISRULE(pPredicateNode,term))
    {
        execute(pPredicateNode->getChild(0));                           // process the left branch
        execute(pPredicateNode->getChild(2));                           // process the right branch
        if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"*"))
        {
            m_aCodeList.emplace_back(new OOp_MUL);
        }
        else if (SQL_ISPUNCTUATION(pPredicateNode->getChild(1),"/"))
            m_aCodeList.emplace_back(new OOp_DIV);
        else
        {
            OSL_FAIL("OPredicateCompiler: Error in Parse Tree num_value_exp");
        }
    }
    else
        pOperand = execute_Operand(pPredicateNode);                     // now only simple operands will be processed

    return pOperand;
}


void OPredicateCompiler::execute_COMPARE(OSQLParseNode const * pPredicateNode)
{
    DBG_ASSERT(pPredicateNode->count() == 3,"OFILECursor: Error in Parse Tree");

    if ( !(SQL_ISRULE(pPredicateNode->getChild(0),column_ref)               ||
          pPredicateNode->getChild(2)->getNodeType() == SQLNodeType::String     ||
          pPredicateNode->getChild(2)->getNodeType() == SQLNodeType::IntNum     ||
          pPredicateNode->getChild(2)->getNodeType() == SQLNodeType::ApproxNum  ||
          SQL_ISTOKEN(pPredicateNode->getChild(2),TRUE)                     ||
          SQL_ISTOKEN(pPredicateNode->getChild(2),FALSE)                    ||
          SQL_ISRULE(pPredicateNode->getChild(2),parameter)                 ||
          // odbc date
          SQL_ISRULE(pPredicateNode->getChild(2),set_fct_spec)              ||
          SQL_ISRULE(pPredicateNode->getChild(2),position_exp)              ||
          SQL_ISRULE(pPredicateNode->getChild(2),char_substring_fct)        ||
          // upper, lower etc.
          SQL_ISRULE(pPredicateNode->getChild(2),fold)) )
    {
        m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr);
        return;
    }

    sal_Int32 ePredicateType( SQLFilterOperator::EQUAL );
    OSQLParseNode *pPrec = pPredicateNode->getChild(1);

    if (pPrec->getNodeType() == SQLNodeType::Equal)
        ePredicateType = SQLFilterOperator::EQUAL;
    else if (pPrec->getNodeType() == SQLNodeType::NotEqual)
        ePredicateType = SQLFilterOperator::NOT_EQUAL;
    else if (pPrec->getNodeType() == SQLNodeType::Less)
        ePredicateType = SQLFilterOperator::LESS;
    else if (pPrec->getNodeType() == SQLNodeType::LessEq)
        ePredicateType = SQLFilterOperator::LESS_EQUAL;
    else if (pPrec->getNodeType() == SQLNodeType::GreatEq)
        ePredicateType = SQLFilterOperator::GREATER_EQUAL;
    else if (pPrec->getNodeType() == SQLNodeType::Great)
        ePredicateType = SQLFilterOperator::GREATER;
    else
        OSL_FAIL( "OPredicateCompiler::execute_COMPARE: unexpected node type!" );

    execute(pPredicateNode->getChild(0));
    execute(pPredicateNode->getChild(2));
    m_aCodeList.emplace_back( new OOp_COMPARE(ePredicateType) );
}


void OPredicateCompiler::execute_LIKE(OSQLParseNode const * pPredicateNode)
{
    DBG_ASSERT(pPredicateNode->count() == 2,"OFILECursor: Error in Parse Tree");
    const OSQLParseNode* pPart2 = pPredicateNode->getChild(1);

    sal_Unicode cEscape = L'\0';
    const bool bNotLike = pPart2->getChild(0)->isToken();

    OSQLParseNode* pAtom        = pPart2->getChild(pPart2->count()-2);
    OSQLParseNode* pOptEscape   = pPart2->getChild(pPart2->count()-1);

    if (!(pAtom->getNodeType() == SQLNodeType::String   ||
          SQL_ISRULE(pAtom,parameter)               ||
          // odbc date
          SQL_ISRULE(pAtom,set_fct_spec)            ||
          SQL_ISRULE(pAtom,position_exp)            ||
          SQL_ISRULE(pAtom,char_substring_fct)      ||
          // upper, lower etc.
          SQL_ISRULE(pAtom,fold)) )
    {
        m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr);
        return;
    }

    if (pOptEscape->count() != 0)
    {
        if (pOptEscape->count() != 2)
        {
            m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_INVALID_LIKE_STRING,nullptr);
        }
        OSQLParseNode *pEscNode = pOptEscape->getChild(1);
        if (pEscNode->getNodeType() != SQLNodeType::String)
        {
            m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_INVALID_LIKE_STRING,nullptr);
        }
        else
            cEscape = pEscNode->getTokenValue().toChar();
    }

    execute(pPredicateNode->getChild(0));
    execute(pAtom);

    OBoolOperator* pOperator = bNotLike
                                    ? new OOp_NOTLIKE(cEscape)
                                    : new OOp_LIKE(cEscape);
    m_aCodeList.emplace_back(pOperator);
}

void OPredicateCompiler::execute_BETWEEN(OSQLParseNode const * pPredicateNode)
{
    DBG_ASSERT(pPredicateNode->count() == 2,"OFILECursor: Error in Parse Tree");

    OSQLParseNode* pColumn = pPredicateNode->getChild(0);
    const OSQLParseNode* pPart2 = pPredicateNode->getChild(1);
    OSQLParseNode* p1stValue = pPart2->getChild(2);
    OSQLParseNode* p2ndtValue = pPart2->getChild(4);

    if (
            !(p1stValue->getNodeType() == SQLNodeType::String || SQL_ISRULE(p1stValue,parameter))
        &&  !(p2ndtValue->getNodeType() == SQLNodeType::String || SQL_ISRULE(p2ndtValue,parameter))
        )
    {
        m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_INVALID_BETWEEN,nullptr);
    }

    bool bNot = SQL_ISTOKEN(pPart2->getChild(0),NOT);

    OOperand* pColumnOp = execute(pColumn);
    OOperand* pOb1 = execute(p1stValue);
    OBoolOperator* pOperator = new OOp_COMPARE(bNot ? SQLFilterOperator::LESS : SQLFilterOperator::GREATER_EQUAL);
    m_aCodeList.emplace_back(pOperator);

    execute(pColumn);
    OOperand* pOb2 = execute(p2ndtValue);
    pOperator = new OOp_COMPARE(bNot ? SQLFilterOperator::GREATER : SQLFilterOperator::LESS_EQUAL);
    m_aCodeList.emplace_back(pOperator);

    if ( pColumnOp && pOb1 && pOb2 )
    {
        switch(pColumnOp->getDBType())
        {
            case DataType::CHAR:
            case DataType::VARCHAR:
            case DataType::LONGVARCHAR:
                pOb1->setValue(pOb1->getValue().getString());
                pOb2->setValue(pOb2->getValue().getString());
                break;
            case DataType::DECIMAL:
            case DataType::NUMERIC:
            case DataType::DOUBLE:
            case DataType::REAL:
                pOb1->setValue(pOb1->getValue().getDouble());
                pOb2->setValue(pOb2->getValue().getDouble());
                break;
            case DataType::FLOAT:
                pOb1->setValue(pOb1->getValue().getFloat());
                pOb2->setValue(pOb2->getValue().getFloat());
                break;
            case DataType::DATE:
                pOb1->setValue(pOb1->getValue().getDate());
                pOb2->setValue(pOb2->getValue().getDate());
                break;
            case DataType::TIME:
                pOb1->setValue(pOb1->getValue().getTime());
                pOb2->setValue(pOb2->getValue().getTime());
                break;
            case DataType::TIMESTAMP:
                pOb1->setValue(pOb1->getValue().getDateTime());
                pOb2->setValue(pOb2->getValue().getDateTime());
                break;
        }
    }


    OBoolOperator* pBoolOp = nullptr;
    if ( bNot )
        pBoolOp = new OOp_OR;
    else
        pBoolOp = new OOp_AND;
    m_aCodeList.emplace_back(pBoolOp);
}

void OPredicateCompiler::execute_ISNULL(OSQLParseNode const * pPredicateNode)
{
    DBG_ASSERT(pPredicateNode->count() == 2,"OFILECursor: Error in Parse Tree");
    const OSQLParseNode* pPart2 = pPredicateNode->getChild(1);
    DBG_ASSERT(SQL_ISTOKEN(pPart2->getChild(0),IS),"OFILECursor: Error in Parse Tree");

    sal_Int32 ePredicateType;
    if (SQL_ISTOKEN(pPart2->getChild(1),NOT))
        ePredicateType = SQLFilterOperator::NOT_SQLNULL;
    else
        ePredicateType = SQLFilterOperator::SQLNULL;

    execute(pPredicateNode->getChild(0));
    OBoolOperator* pOperator = (ePredicateType == SQLFilterOperator::SQLNULL) ?
                                new OOp_ISNULL : new OOp_ISNOTNULL;
    m_aCodeList.emplace_back(pOperator);
}

OOperand* OPredicateCompiler::execute_Operand(OSQLParseNode const * pPredicateNode)
{
    OOperand* pOperand = nullptr;

    if (SQL_ISRULE(pPredicateNode,column_ref))
    {
        OUString aColumnName;
        if (pPredicateNode->count() == 1)
        {
            aColumnName = pPredicateNode->getChild(0)->getTokenValue();
        }
        else if (pPredicateNode->count() == 3)
        {
            if(SQL_ISRULE(pPredicateNode->getChild(2),column_val))
                aColumnName = pPredicateNode->getChild(2)->getChild(0)->getTokenValue();
            else
                aColumnName = pPredicateNode->getChild(2)->getTokenValue();
        }

        if(!m_orgColumns->hasByName(aColumnName))
        {
            const OUString sError( m_pAnalyzer->getConnection()->getResources().getResourceStringWithSubstitution(
                    STR_INVALID_COLUMNNAME,
                    "$columnname$", aColumnName
                 ) );
            ::dbtools::throwGenericSQLException( sError, nullptr );
        }
        css::uno::Reference< css::beans::XPropertySet> xCol;
        try
        {
            if (m_orgColumns->getByName(aColumnName) >>= xCol)
            {
                pOperand = OSQLAnalyzer::createOperandAttr(Reference< XColumnLocate>(m_orgColumns,UNO_QUERY_THROW)->findColumn(aColumnName),xCol);
            }
            else
            {// Column doesn't exist in the Result-set
                const OUString sError( m_pAnalyzer->getConnection()->getResources().getResourceStringWithSubstitution(
                    STR_INVALID_COLUMNNAME,
                    "$columnname$", aColumnName
                 ) );
                ::dbtools::throwGenericSQLException( sError, nullptr );
            }
        }
        catch(Exception &)
        {
            TOOLS_WARN_EXCEPTION( "connectivity.drivers", "OPredicateCompiler::execute_Operand Exception");
        }
    }
    else if (SQL_ISRULE(pPredicateNode,parameter))
    {
        pOperand = new OOperandParam(++m_nParamCounter);
    }
    else if (pPredicateNode->getNodeType() == SQLNodeType::String ||
             pPredicateNode->getNodeType() == SQLNodeType::IntNum ||
             pPredicateNode->getNodeType() == SQLNodeType::ApproxNum ||
             pPredicateNode->getNodeType() == SQLNodeType::Name ||
             SQL_ISTOKEN(pPredicateNode,TRUE) ||
             SQL_ISTOKEN(pPredicateNode,FALSE) ||
             SQL_ISRULE(pPredicateNode,parameter))
    {
        pOperand = new OOperandConst(*pPredicateNode, pPredicateNode->getTokenValue());
    }
    else if((pPredicateNode->count() == 2) &&
            (SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"+") || SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"-")) &&
            pPredicateNode->getChild(1)->getNodeType() == SQLNodeType::IntNum)
    { // if -1 or +1 is there
        OUString aValue = pPredicateNode->getChild(0)->getTokenValue() + pPredicateNode->getChild(1)->getTokenValue();
        pOperand = new OOperandConst(*pPredicateNode->getChild(1), aValue);
    }
    else if( SQL_ISRULE(pPredicateNode,set_fct_spec) && SQL_ISPUNCTUATION(pPredicateNode->getChild(0),"{") )
    {
        const OSQLParseNode* pODBCNode = pPredicateNode->getChild(1);
        const OSQLParseNode* pODBCNodeChild = pODBCNode->getChild(0);

        // Odbc Date or time
        if (pODBCNodeChild->getNodeType() == SQLNodeType::Keyword && (
            SQL_ISTOKEN(pODBCNodeChild,D) ||
            SQL_ISTOKEN(pODBCNodeChild,T) ||
            SQL_ISTOKEN(pODBCNodeChild,TS) ))
        {
            OUString sDateTime = pODBCNode->getChild(1)->getTokenValue();
            pOperand = new OOperandConst(*pODBCNode->getChild(1), sDateTime);
            if(SQL_ISTOKEN(pODBCNodeChild,D))
            {
                pOperand->setValue(::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(sDateTime)));
            }
            else if(SQL_ISTOKEN(pODBCNodeChild,T))
            {
                pOperand->setValue(::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(sDateTime)));
            }
            else if(SQL_ISTOKEN(pODBCNodeChild,TS))
            {
                pOperand->setValue(::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDateTime(sDateTime)));
            }
        }
        else
            m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr);

    }
    else if( SQL_ISRULE(pPredicateNode,fold) )
    {
        execute_Fold(pPredicateNode);
    }
    else if(    SQL_ISRULE(pPredicateNode,set_fct_spec)
            ||  SQL_ISRULE(pPredicateNode,position_exp)
            ||  SQL_ISRULE(pPredicateNode,char_substring_fct)
            )
    {
        executeFunction(pPredicateNode);
    }
    else if( SQL_ISRULE(pPredicateNode,length_exp) )
    {
        executeFunction(pPredicateNode->getChild(0));
    }
    else
    {
        m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_TOO_COMPLEX,nullptr);
    }
    if (pOperand)
        m_aCodeList.emplace_back(pOperand);
    return pOperand;
}


bool OPredicateInterpreter::evaluate(OCodeList& rCodeList)
{
    if (!(rCodeList[0]))
        return true;        // no Predicate

    for (auto const& code : rCodeList)
    {
        OOperand* pOperand = dynamic_cast<OOperand* >(code.get());
        if (pOperand)
            m_aStack.push(pOperand);
        else
            static_cast<OOperator *>(code.get())->Exec(m_aStack);
    }

    OOperand* pOperand = m_aStack.top();
    m_aStack.pop();

    DBG_ASSERT(m_aStack.empty(), "Stack error");
    assert(pOperand && "Stack error");

    const bool bResult = pOperand->isValid();
    if (typeid(OOperandResult) == typeid(*pOperand))
        delete pOperand;
    return bResult;
}

void OPredicateInterpreter::evaluateSelection(OCodeList& rCodeList, ORowSetValueDecoratorRef const & _rVal)
{
    if (!(rCodeList[0]))
        return ;        // no Predicate

    for (auto const& code : rCodeList)
    {
        OOperand* pOperand = dynamic_cast<OOperand* >(code.get());
        if (pOperand)
            m_aStack.push(pOperand);
        else
            static_cast<OOperator *>(code.get())->Exec(m_aStack);
    }

    OOperand* pOperand = m_aStack.top();
    m_aStack.pop();

    DBG_ASSERT(m_aStack.empty(), "Stack error");
    assert(pOperand && "Stack error");

    (*_rVal) = pOperand->getValue();
    if (typeid(OOperandResult) == typeid(*pOperand))
        delete pOperand;
}

void OPredicateCompiler::execute_Fold(OSQLParseNode const * pPredicateNode)
{
    DBG_ASSERT(pPredicateNode->count() >= 4,"OFILECursor: Error in Parse Tree");

    bool bUpper = SQL_ISTOKEN(pPredicateNode->getChild(0),UPPER);

    execute(pPredicateNode->getChild(2));
    OOperator* pOperator = nullptr;
    if ( bUpper )
        pOperator = new OOp_Upper;
    else
        pOperator = new OOp_Lower;

    m_aCodeList.emplace_back(pOperator);
}

void OPredicateCompiler::executeFunction(OSQLParseNode const * pPredicateNode)
{
    OOperator* pOperator = nullptr;

    OSL_ENSURE(pPredicateNode->getChild(0)->isToken(),"The first one must be the name of the function!");
    sal_Int32 nTokenId = pPredicateNode->getChild(0)->getTokenID();
    switch ( nTokenId )
    {
        case SQL_TOKEN_CHAR_LENGTH:
        case SQL_TOKEN_LENGTH:
        case SQL_TOKEN_OCTET_LENGTH:
        case SQL_TOKEN_ASCII:
        case SQL_TOKEN_LCASE:
        case SQL_TOKEN_LTRIM:
        case SQL_TOKEN_RTRIM:
        case SQL_TOKEN_SPACE:
        case SQL_TOKEN_UCASE:
        case SQL_TOKEN_ABS:
        case SQL_TOKEN_ACOS:
        case SQL_TOKEN_ASIN:
        case SQL_TOKEN_ATAN:
        case SQL_TOKEN_CEILING:
        case SQL_TOKEN_COS:
        case SQL_TOKEN_DEGREES:
        case SQL_TOKEN_EXP:
        case SQL_TOKEN_FLOOR:
        case SQL_TOKEN_LOG10:
        case SQL_TOKEN_LN:
        case SQL_TOKEN_RADIANS:
        case SQL_TOKEN_SIGN:
        case SQL_TOKEN_SIN:
        case SQL_TOKEN_SQRT:
        case SQL_TOKEN_TAN:
        case SQL_TOKEN_DAYNAME:
        case SQL_TOKEN_DAYOFMONTH:
        case SQL_TOKEN_DAYOFWEEK:
        case SQL_TOKEN_DAYOFYEAR:
        case SQL_TOKEN_HOUR:
        case SQL_TOKEN_MINUTE:
        case SQL_TOKEN_MONTH:
        case SQL_TOKEN_MONTHNAME:
        case SQL_TOKEN_QUARTER:
        case SQL_TOKEN_SECOND:
        case SQL_TOKEN_YEAR:

            execute(pPredicateNode->getChild(2));

            switch( nTokenId )
            {
                case SQL_TOKEN_CHAR_LENGTH:
                case SQL_TOKEN_LENGTH:
                case SQL_TOKEN_OCTET_LENGTH:
                    pOperator = new OOp_CharLength;
                    break;
                case SQL_TOKEN_ASCII:
                    pOperator = new OOp_Ascii;
                    break;
                case SQL_TOKEN_LCASE:
                    pOperator = new OOp_Lower;
                    break;

                case SQL_TOKEN_LTRIM:
                    pOperator = new OOp_LTrim;
                    break;
                case SQL_TOKEN_RTRIM:
                    pOperator = new OOp_RTrim;
                    break;
                case SQL_TOKEN_SPACE:
                    pOperator = new OOp_Space;
                    break;
                case SQL_TOKEN_UCASE:
                    pOperator = new OOp_Upper;
                    break;
                case SQL_TOKEN_ABS:
                    pOperator = new OOp_Abs;
                    break;
                case SQL_TOKEN_ACOS:
                    pOperator = new OOp_ACos;
                    break;
                case SQL_TOKEN_ASIN:
                    pOperator = new OOp_ASin;
                    break;
                case SQL_TOKEN_ATAN:
                    pOperator = new OOp_ATan;
                    break;
                case SQL_TOKEN_CEILING:
                    pOperator = new OOp_Ceiling;
                    break;
                case SQL_TOKEN_COS:
                    pOperator = new OOp_Cos;
                    break;
                case SQL_TOKEN_DEGREES:
                    pOperator = new OOp_Degrees;
                    break;
                case SQL_TOKEN_EXP:
                    pOperator = new OOp_Exp;
                    break;
                case SQL_TOKEN_FLOOR:
                    pOperator = new OOp_Floor;
                    break;
                case SQL_TOKEN_LOG10:
                    pOperator = new OOp_Log10;
                    break;
                case SQL_TOKEN_LN:
                    pOperator = new OOp_Ln;
                    break;
                case SQL_TOKEN_RADIANS:
                    pOperator = new OOp_Radians;
                    break;
                case SQL_TOKEN_SIGN:
                    pOperator = new OOp_Sign;
                    break;
                case SQL_TOKEN_SIN:
                    pOperator = new OOp_Sin;
                    break;
                case SQL_TOKEN_SQRT:
                    pOperator = new OOp_Sqrt;
                    break;
                case SQL_TOKEN_TAN:
                    pOperator = new OOp_Tan;
                    break;
                case SQL_TOKEN_DAYOFWEEK:
                    pOperator = new OOp_DayOfWeek;
                    break;
                case SQL_TOKEN_DAYOFMONTH:
                    pOperator = new OOp_DayOfMonth;
                    break;
                case SQL_TOKEN_DAYOFYEAR:
                    pOperator = new OOp_DayOfYear;
                    break;
                case SQL_TOKEN_MONTH:
                    pOperator = new OOp_Month;
                    break;
                case SQL_TOKEN_DAYNAME:
                    pOperator = new OOp_DayName;
                    break;
                case SQL_TOKEN_MONTHNAME:
                    pOperator = new OOp_MonthName;
                    break;
                case SQL_TOKEN_QUARTER:
                    pOperator = new OOp_Quarter;
                    break;
                case SQL_TOKEN_YEAR:
                    pOperator = new OOp_Year;
                    break;
                case SQL_TOKEN_HOUR:
                    pOperator = new OOp_Hour;
                    break;
                case SQL_TOKEN_MINUTE:
                    pOperator = new OOp_Minute;
                    break;
                case SQL_TOKEN_SECOND:
                    pOperator = new OOp_Second;
                    break;
                default:
                    OSL_FAIL("Error in switch!");
            }
            break;
        case SQL_TOKEN_CHAR:
        case SQL_TOKEN_CONCAT:
        case SQL_TOKEN_INSERT:
        case SQL_TOKEN_LEFT:
        case SQL_TOKEN_LOCATE:
        case SQL_TOKEN_LOCATE_2:
        case SQL_TOKEN_REPEAT:
        case SQL_TOKEN_REPLACE:
        case SQL_TOKEN_RIGHT:
        case SQL_TOKEN_MOD:
        case SQL_TOKEN_ROUND:
        case SQL_TOKEN_LOGF:
        case SQL_TOKEN_LOG:
        case SQL_TOKEN_POWER:
        case SQL_TOKEN_ATAN2:
        case SQL_TOKEN_PI:
        case SQL_TOKEN_CURDATE:
        case SQL_TOKEN_CURTIME:
        case SQL_TOKEN_NOW:
        case SQL_TOKEN_WEEK:
            {
                m_aCodeList.emplace_back(new OStopOperand);
                OSQLParseNode* pList = pPredicateNode->getChild(2);
                for (size_t i=0; i < pList->count(); ++i)
                    execute(pList->getChild(i));

                switch( nTokenId )
                {
                    case SQL_TOKEN_CHAR:
                        pOperator = new OOp_Char;
                        break;
                    case SQL_TOKEN_CONCAT:
                        pOperator = new OOp_Concat;
                        break;
                    case SQL_TOKEN_INSERT:
                        pOperator = new OOp_Insert;
                        break;
                    case SQL_TOKEN_LEFT:
                        pOperator = new OOp_Left;
                        break;
                    case SQL_TOKEN_LOCATE:
                    case SQL_TOKEN_LOCATE_2:
                        pOperator = new OOp_Locate;
                        break;
                    case SQL_TOKEN_REPEAT:
                        pOperator = new OOp_Repeat;
                        break;
                    case SQL_TOKEN_REPLACE:
                        pOperator = new OOp_Replace;
                        break;
                    case SQL_TOKEN_RIGHT:
                        pOperator = new OOp_Right;
                        break;
                    case SQL_TOKEN_MOD:
                        pOperator = new OOp_Mod;
                        break;
                    case SQL_TOKEN_ROUND:
                        pOperator = new OOp_Round;
                        break;
                    case SQL_TOKEN_LOGF:
                    case SQL_TOKEN_LOG:
                        pOperator = new OOp_Log;
                        break;
                    case SQL_TOKEN_POWER:
                        pOperator = new OOp_Pow;
                        break;
                    case SQL_TOKEN_ATAN2:
                        pOperator = new OOp_ATan2;
                        break;
                    case SQL_TOKEN_PI:
                        pOperator = new OOp_Pi;
                        break;
                    case SQL_TOKEN_CURDATE:
                        pOperator = new OOp_CurDate;
                        break;
                    case SQL_TOKEN_CURTIME:
                        pOperator = new OOp_CurTime;
                        break;
                    case SQL_TOKEN_NOW:
                        pOperator = new OOp_Now;
                        break;
                    case SQL_TOKEN_WEEK:
                        pOperator = new OOp_Week;
                        break;
                    default:
                        OSL_FAIL("Error in switch!");
                }
            }
            break;

        case SQL_TOKEN_SUBSTRING:
            m_aCodeList.emplace_back(new OStopOperand);
            if ( pPredicateNode->count() == 4 ) //char_substring_fct
            {
                OSQLParseNode* pList = pPredicateNode->getChild(2);
                for (size_t i=0; i < pList->count(); ++i)
                    execute(pList->getChild(i));
            }
            else
            {
                execute(pPredicateNode->getChild(2));
                execute(pPredicateNode->getChild(4));
                execute(pPredicateNode->getChild(5)->getChild(1));
            }
            pOperator = new OOp_SubString;
            break;

        case SQL_TOKEN_POSITION:
            m_aCodeList.emplace_back(new OStopOperand);
            if ( pPredicateNode->count() == 4 ) //position_exp
            {
                OSQLParseNode* pList = pPredicateNode->getChild(2);
                for (size_t i=0; i < pList->count(); ++i)
                    execute(pList->getChild(i));
            }
            else
            {
                execute(pPredicateNode->getChild(2));
                execute(pPredicateNode->getChild(4));
            }
            pOperator = new OOp_Locate;
            break;
        default:
            m_pAnalyzer->getConnection()->throwGenericSQLException(STR_QUERY_FUNCTION_NOT_SUPPORTED,nullptr);
    }

    m_aCodeList.emplace_back(pOperator);
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
