/*!
  @file           DBProc_Handler.cpp
  @author         ThomasA
  @ingroup        DBProc
  @brief          methods for db-procedure and db-function execution

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2000-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



\endif
*/

#include "SQLManager/SQLMan_Types.hpp"
#include "SQLManager/SQLMan_Context.hpp"
#include "SQLManager/SQLMan_ContextSessionMembers.hpp"
#include "SQLManager/Catalog/Catalog_MessBlockObject.hpp"
#include "SQLManager/DBProc/DBProc_DBProcDescriptor.hpp"
#include "SQLManager/SQLMan_MessBlock.hpp"
#include "SQLManager/SQLMan_Tracing.hpp"
#include "SAPDBCommon/Fields/SAPDBFields_FieldMap.hpp"
#include "SQLManager/DBProc/DBProc_Handler.hpp"
#include "SQLManager/DBProc/DBProc_Messages.hpp"
#include "SQLManager/SQLMan_SQLStatementContext.hpp"
#include "SQLManager/Catalog/Catalog_Object.hpp"
#include "SAPDBCommon/SAPDB_AutoPtr.hpp"
#include "SAPDBCommon/ErrorsAndMessages/SAPDBErr_MessageList.hpp"
#include "DBProc/Proc_IFactory.hpp"
#include "DBProc/Proc_ServerSessionFactory.hpp"
#include "DBProc/Proc_ISession.hpp"
#include "DBProc/Proc_FactoryDirectory.hpp"
#include "DBProc/Proc_ParameterCopy.hpp"
#include "RunTime/ProcServer/ProcRTE_Runtime.hpp"

#if defined(KERNEL80)
#include "SQLManager/KernelSQL/KSQL_Connection.hpp"
#include "SQLManager/KernelSQL/KSQL_PreparedStatement.hpp"
#include "SQLManager/SQLMan_SQLStatementContext.hpp"
#endif

#include "SAPDBCommon/MemoryManagement/SAPDBMem_NewDestroy.hpp"

#include "hak07.h"
#include "hak071.h"
#include "hak260.h"
#include "hak262.h"
#include "ggg01.h"
#include "hgg04.h"
#include "hkb71.h"
#include "hgg10.h"
#include "hak651.h"
#include "hsp51.h"
#include "hsp81.h"

void DBProc_Handler::CalculateFunctionResult (tgg00_TransContext                   &transContext,
                                              const tgg00_StackEntry               &functionId,
                                              const SAPDBFields_FieldMap           &inParameters,
                                              const ParameterDescriptors           &parameterDescriptors,
                                              const tgg00_Rec*                     &pResult,
                                              int                                  &resultLen)
{ 
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::CalculateFunctionResult", DBProc_Trace, 1);
    SQLMan_Context* pContext = REINTERPRET_CAST(SQLMan_Context*, transContext.trAcvPtr_gg00);
    if (NULL == pContext)
    {
        pContext = new(*REINTERPRET_CAST(SAPDBMem_IRawAllocator*, transContext.trAllocator_gg00)) SQLMan_Context(transContext);
    }
    tak_sysbufferaddress p;
    a262LoadCode (*pContext, *reinterpret_cast<const SQLMan_Surrogate*>(&functionId), 
        *reinterpret_cast<const tsp00_C2*> (cak_init_linkage), p);
    if (p)
    {
        SQLMan_MessBlock* pMblock = reinterpret_cast<SQLMan_MessBlock*> (&p->smessblock().mbr_mess_block);
        tgg00_SelectFieldsParam sel;
        g04init_select_fields (sel, &pMblock->mb_data()->mbp_buf(), pMblock->mb_data_size(),
            pMblock->mb_valuearr(), pMblock->mb_validx_max(),
            pContext->a_work_st_addr, pContext->a_work_st_max,
            pContext->a_work_buf_addr, pContext->a_work_buf_size,
            pContext->a_sqlmode);
        sel.sfp_bd_mess_type()    = pMblock->MessType();
        sel.sfp_bd_mess2_type()   = pMblock->MessType2();
        sel.sfp_result_wanted()   = false;
        sel.sfp_m_result_addr()   = NULL;
        sel.sfp_m_result_size()   = 0;
        sel.sfp_first_qual()      = true;
        sel.sfp_sqlmode()         = pMblock->SqlMode();
        sel.sfp_dateTimeFormat()  = pMblock->DateTimeFormat();
        // create buffer for dbproc variables 
        int moveLen;
        int varBufferLen;
        if (pMblock->StringCount() > 0)
        {
            if (1 == pMblock->StackEntry(pMblock->StringPos()).epos())
            {
                moveLen      = 0;
                varBufferLen = pMblock->StackEntry(pMblock->StringPos()).elen_var();
            }
            else
            {
                varBufferLen = pMblock->mb_data()->mbp_rec().recLen_gg00();
                moveLen      = varBufferLen;
            }
        }
        else
        {
            moveLen      = pMblock->mb_data()->mbp_rec().recLen_gg00();
            varBufferLen = moveLen;
        }
        unsigned char buf[1000];
        void* pVarBuffer = (varBufferLen <= 1000) ? &buf[0] : pContext->GetAllocator().Allocate(varBufferLen);
        sel.sfp_oldrec_addr()   = reinterpret_cast<tsp00_MoveObjPtr>(pVarBuffer);
        sel.sfp_oldrec_pos()    = 1;
        sel.sfp_oldrec_len()    = varBufferLen;
        sel.sfp_oldkey_len()    = 0;
        sel.sfp_acv_addr()      = reinterpret_cast<tsp00_Addr>(pContext);
        sel.sfp_data_addr()     = &pMblock->mb_data()->mbp_buf();
        sel.sfp_data_size()     = pMblock->mb_data_size();
        sel.sfp_workbuf_len()   = 0; 
        sel.sfp_workbuf_top()   = 0;
        sel.sfp_work_st_top()   = reinterpret_cast<tgg00_StackEntry*>(sel.sfp_work_st_addr());
        sel.sfp_work_st_frame() = sel.sfp_work_st_top();  
        SAPDBFields_FieldMap::IndexType fieldCnt = inParameters.GetFieldCount(); 
        for (SAPDBFields_FieldMap::IndexType fieldIdx = 0; fieldIdx < fieldCnt; ++fieldIdx)
        {
            SAPDBFields_Field field;
            inParameters.GetField(fieldIdx, field); 
            // push field to interpreter stack
            int paramLength = field.GetLength();
            if (sel.sfp_workbuf_len() + parameterDescriptors[fieldIdx].m_fixedLength > sel.sfp_workbuf_size())
            {
                pMblock->mb_trns()->trError_gg00 = e_stack_overflow;
                return;
            }
            SAPDB_Byte defByte;
            if (parameterDescriptors[fieldIdx].m_toUnicode) {
                unsigned int destOut;
                defByte = (unsigned char) csp_unicode_def_byte;
                (*sel.sfp_workbuf_addr())[sel.sfp_workbuf_top()] = defByte;
                sp81ASCIItoUCS2((tsp81_UCS2Char*) (sel.sfp_workbuf_addr() + sel.sfp_workbuf_top() + 1),
                    sel.sfp_workbuf_size() - sel.sfp_workbuf_len(), false, &destOut,
                    field.GetPointer(1), field.GetLength() - 1);
                paramLength = 1 + (paramLength - 1) * 2;
            }
            else {
                defByte = *field.GetPointer(0);
                memcpy(sel.sfp_workbuf_addr()[sel.sfp_workbuf_top()], field.GetPointer(0), field.GetLength());
            }
            if (paramLength < parameterDescriptors[fieldIdx].m_fixedLength)
            {
                switch (defByte) {
                case csp_undef_byte :
                    break;
                case csp_unicode_def_byte :
                    g10filuni ("DBPROC", 1, parameterDescriptors[fieldIdx].m_fixedLength,
                        &sel.sfp_workbuf_addr()[sel.sfp_workbuf_top()], paramLength + 1,
                        parameterDescriptors[fieldIdx].m_fixedLength - paramLength, csp_unicode_blank,
                        pMblock->mb_trns()->trError_gg00);
                    break;
                default :
                    memset (&sel.sfp_workbuf_addr()[sel.sfp_workbuf_top() + paramLength],
                        defByte, parameterDescriptors[fieldIdx].m_fixedLength - paramLength);
                }
                paramLength = parameterDescriptors[fieldIdx].m_fixedLength;
            }
            ++sel.sfp_work_st_top();
            sel.sfp_work_st_top()->etype().becomes(st_result);
            sel.sfp_work_st_top()->epos()     = sel.sfp_workbuf_top() + 1;
            sel.sfp_work_st_top()->elen_var() = paramLength;
            sel.sfp_workbuf_top() += field.GetLength();
            sel.sfp_workbuf_len() += field.GetLength();
        }
        k71sel_qualification_test (*pMblock, sel, false, pMblock->mb_data()->mbp_rec());
        if (e_ok == pMblock->mb_trns()->trError_gg00)
        {
            tsp00_MoveObjPtr pOperandAddr;
            k71get_operand (sel, false, pOperandAddr, resultLen, pMblock->mb_trns()->trError_gg00); 
            sel.sfp_workbuf_top() += resultLen;
            pResult = REINTERPRET_CAST(tgg00_Rec*, pOperandAddr);
        }
        else
        {
            SQLMan_Context* pContext = SQLMan_Context::GetContext();
            if (pContext)
            {
                int rc = a07_return_code (pMblock->mb_trns()->trError_gg00, pContext->a_sqlmode);
                pContext->GetErrorList().AppendNewMessage (SAPDBErr_MessageList (DBPROC_CONTEXT,
                    DBPROC_FUNCTION_CALL_FAILED, ToStr(rc)));
            }
        }
     }
     else
     {
         transContext.trError_gg00 = e_dbfunc_code_not_found;
     }
}

//----------------------------------------------------------------------------------

tgg00_BasisError DBProc_Handler::ExecuteSqlStatement (
                                     SQLMan_Context*          pContext,
                                     tgg00_SelectFieldsParam& interpreterContext,
                                     tsp00_MoveObjPtr         pReturnCode,
                                     tgg00_StackEntry*        pCode,
                                     tsp00_Int4&              currQualIndex)
{
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::ExecuteSqlStatement", DBProc_Trace, 1);
    tgg00_BasisError e;
    void* pOperandStack;
    int operandStackSize = sizeof(tgg00_StackEntry) * 
        (interpreterContext.sfp_work_st_top() - REINTERPRET_CAST(tgg00_StEntryAddr, interpreterContext.sfp_work_st_addr()));
    if (operandStackSize > 0)
    {
         pOperandStack = pContext->GetAllocator().Allocate(operandStackSize);
         if (pOperandStack)
         {
             memcpy (pOperandStack, interpreterContext.sfp_work_st_addr(), operandStackSize);  
         }
         else
         {
             pContext->GetErrorList().AppendNewMessage (SAPDBErr_MessageList (DBPROC_CONTEXT,
                DBPROC_OUT_OF_MEMORY, ToStr(operandStackSize)));
             return e_no_more_memory;
         }
    }
    void* pWorkBuf;
    int workBufLength = interpreterContext.sfp_workbuf_len();
    if (workBufLength > 0)
    {
        pWorkBuf = pContext->GetAllocator().Allocate(workBufLength);
        if (pWorkBuf)
        {
            memcpy (pWorkBuf, interpreterContext.sfp_workbuf_addr(), workBufLength);
        }
        else
        {
            pContext->GetErrorList().AppendNewMessage (SAPDBErr_MessageList (DBPROC_CONTEXT,
                DBPROC_OUT_OF_MEMORY, ToStr(workBufLength)));
            return e_no_more_memory;
        }
    }

    a262execute (pContext, interpreterContext, pReturnCode, *REINTERPRET_CAST(tgg00_StackList*, pCode), currQualIndex, e); 
    if (operandStackSize > 0)
    {
        memcpy (interpreterContext.sfp_work_st_addr(), pOperandStack, operandStackSize);
        pContext->GetAllocator().Deallocate(pOperandStack);
    }
    if (workBufLength > 0)
    {
        memcpy (interpreterContext.sfp_workbuf_addr(), pWorkBuf, workBufLength);
        pContext->GetAllocator().Deallocate(pWorkBuf);
    }
    return e;
}

//----------------------------------------------------------------------------------

void DBProc_Handler::UserDefinedFunctionCall  (
            tgg00_TransContext&      transContext,
            tgg00_SelectFieldsParam& interpreterContext,
            const tgg00_StackEntry&  functionCode,
            const tgg00_StackEntry&  functionId,
            short&                   error)
{
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::UserDefinedFunctionCall", DBProc_Trace, 1);
    error = e_ok;
    interpreterContext.sfp_work_st_frame() = interpreterContext.sfp_work_st_top() - functionCode.ecol_tab()[0];
    if (interpreterContext.sfp_work_st_frame() < interpreterContext.sfp_work_st_bottom())
    {
        error =  e_stack_type_illegal;
    }
    else
    {
        SQLMan_Context* pContext = REINTERPRET_CAST(SQLMan_Context*, transContext.trAcvPtr_gg00);
        if (functionCode.ecol_tab()[1])
        {
            // function contains sql, i.e. a new statment context is required
            SQLMan_SQLStatementContext savedContext(*pContext);
            a262FunctionCall (pContext, interpreterContext, functionId.esurrogate(), error);
        }
        else
        {
            // function contains no sql, i.e. no new statment context is required
            a262FunctionCall (pContext, interpreterContext, functionId.esurrogate(), error);
        }
        if (e_ok == error)
        {
            tgg00_StackEntry* pTop = ++interpreterContext.sfp_work_st_top();
            interpreterContext.sfp_work_st_top() = interpreterContext.sfp_work_st_frame() + 1;
            *interpreterContext.sfp_work_st_top() = *pTop; 
        }
    }
}

//----------------------------------------------------------------------------------

inline void DBProc_EvalExpression (SQLMan_Context&    context,
                                       tgg00_StackEntry&  current,
                                       int                startIdx,
                                       int                endIdx)
{
    SQLMan_StackEntry aux = current;
    current.epos()        = current.elen_var();
    current.elen_var()    = (dfloat == aux.eparamdatatype()) ?  csp_float_frac : aux.efraction();
    a651value_calculate (context, startIdx + 1, endIdx + 1, false, false, current.epos(), 1);
    current = aux;
}

//----------------------------------------------------------------------------------

inline void DBProc_GetInputParameter (SQLMan_Context&   context,
                                                           SQLMan_MessBlock&  callCode, 
                                                           tgg00_StackEntry&  paramDesc,
                                                           tgg00_StackEntry&  valueDesc,
                                                           SAPDBFields_Field& param)

{ 
    tsp00_MoveObjPtr pParam;
    if (1 == valueDesc.ecol_tab()[0]) // value from sql packet
    {
        pParam = &context.a_data_ptr[valueDesc.epos()-1];
        if ((SAPDB_Byte) csp_defined_byte == (*pParam)[0])
        {
            switch (paramDesc.eparamdatatype()) 
            {
            case dcha :
            case ddate:
            case dtime :
            case dtimestamp :
                (*pParam)[0] = csp_ascii_blank;
                break;
            case dunicode :
                (*pParam)[0] = csp_unicode_def_byte;
                break;
            }
        }
        param.Assign(pParam, valueDesc.elen_var()); 
    }
    else
    { // value from call mess block
        pParam          = (tsp00_MoveObjPtr) &callCode.DataBuf()[valueDesc.epos()-1];
        int valueLength = valueDesc.elen_var();
        if (valueLength > paramDesc.elen_var())
        { // calculated expression does not fit into parameter
            if ((dfloat == paramDesc.eparamdatatype()) ||
                (dfixed == paramDesc.eparamdatatype()))
            {
                // round number to required length
                int fraction = (dfloat == paramDesc.eparamdatatype()) ? csp_float_frac : paramDesc.efraction();
                tsp00_NumError numError;
                s51kroun (*pParam, 2, valueLength-1, *pParam, 2,
                    paramDesc.elen_var(), fraction, valueLength, numError);
                if (num_ok != numError)
                {
                    param.Deassign();
                    tgg00_BasisError e;
                    k71num_err_to_b_err (numError, e);
                    a07_b_put_error (context, e, 1);
                    return;
                }
            }
            else
            {
                param.Deassign();
                a07_b_put_error (context, e_column_trunc, 1);
                return;
            }
        }
        param.Assign(pParam, valueLength);
    }
}

//----------------------------------------------------------------------------------

static void
showLanguage (
    DBProc_DBProcDescriptor & descriptor)
{
    const char * lang = descriptor.GetLanguageString ();
}

void DBProc_Handler::ProcedureCall (SQLMan_Context&   context,
                                    int&              outParamCnt)
{
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::ProcedureCall", DBProc_Trace, 1);
    SQLMan_MessBlock& callCode = context.GetMessBlock();
    /* load the code of the procedure first */
    Catalog_ISessionCache& sessionCache = context.GetSessionCatalogCache();
    Catalog_MessBlockObject* pCode = sessionCache.LoadCode(callCode.GetTableId());
    if (pCode)
    {
       const bool isPL = !callCode.GetQualBool();
       SQLMan_MessBlock& dbprocCode = pCode->MessBlock();
       SAPDB_AutoPtr<SAPDBFields_FieldMap>    pFieldMap(context.GetAllocator());
       SAPDB_AutoPtr<DBProc_DBProcDescriptor> pDBProcDescriptor(context.GetAllocator());
       if (!isPL)
       {   // external procedure call (java, ...), provide procedure description an parameter field map
           pDBProcDescriptor.SetPtr(new(context.GetAllocator()) DBProc_DBProcDescriptor(sessionCache, dbprocCode.GetTableId()));  
           if (pDBProcDescriptor.IsAssigned()) 
           {
               if (!pDBProcDescriptor->IsValid())
               {
                   context.ThrowError(pDBProcDescriptor->GetLastError());
                   return;
               }
               pFieldMap.SetPtr(new(context.GetAllocator()) SAPDBFields_FieldMap (context.GetAllocator(), pDBProcDescriptor->ParamCount()));
               if (!pFieldMap.IsAssigned())
               {
                   context.SetNoMoreMemoryError();
                   return;
               }
           }
           else
           {
               context.SetNoMoreMemoryError();
               return;
           }
       }
       outParamCnt     = 0;
       int st_ix       = callCode.GetQualPos() - 1;
       int end_ix      = st_ix + callCode.GetQualCount();
       int valueCnt    = 0;
       int currParam   = -1;
       int firstIdx    = 0;
       /* Push all IN/INOUT parameters from callCode into dbprocCode */
       SQLMan_StackEntry* pValue;
       while (st_ix < end_ix)
       {
           SQLMan_StackEntry* pCurrSt = callCode.StackEntryPtr(st_ix);
           SQLMAN_TRACE_STACK_ENTRY (DBProc_Trace, 5, "call code", *pCurrSt, st_ix + 1);
           switch (pCurrSt->etype())
           {
           case st_jump_output :
               {
                   st_ix = pCurrSt->epos();
                   continue;
               }
           case st_jump_absolute :
               {
                   st_ix += pCurrSt->epos() + 1;
                   continue;
               }
           case st_value :
               {   // one parameter value
                   if (0 == valueCnt)
                   {
                       firstIdx = st_ix;
                       pValue   = pCurrSt;
                   }
                   ++valueCnt;
                   break;
               }
           case st_result :
               {   // push current parameter
                   ++currParam;
                   if (valueCnt > 1)
                   {
                       // parameter expression has not been evaluated yet, do it now
                       DBProc_EvalExpression (context, *pCurrSt, firstIdx, st_ix);  
                   }
                   valueCnt = 0;
                   if (pCurrSt->eop() != op_not_in)
                   { // push input/inout parameter
                       SAPDBFields_Field param;
                       DBProc_GetInputParameter (context, callCode, *pCurrSt, *pValue, param);
                       if (param.IsAssigned())
                       {
                           SQLMAN_TRACE_BUFFER (DBProc_Trace, 5, "Push Parameter", param.GetDataPtr(),  param.GetLength());
                           g10mv ("DBPROC",   1,    
                               param.GetLength(), dbprocCode.DataSize(),
                               param.GetDataPtr(), 1,
                               dbprocCode.DataBuf(), pCurrSt->ecol_pos() + 1, param.GetLength(),
                               context.a_returncode);
                           if (pFieldMap.IsAssigned())
                           {
                               pFieldMap->Fill (currParam, currParam, param);
                           }
                       }
                   }
                   else
                   { // output parameter, initialize to NULL value
                       SAPDBTRACE_WRITELN( DBProc_Trace, 1, "out param at " << pCurrSt->ecol_pos() << " length " << pCurrSt->elen_var());
                       dbprocCode.DataBuf()[pCurrSt->ecol_pos()] = (SAPDB_Byte) csp_undef_byte;
                   }
                   if (pCurrSt->eop() != op_in)
                   {  // output parameter
                       ++outParamCnt;
                       if (pFieldMap.IsAssigned())
                       {
                           SAPDBFields_Field outParam(&dbprocCode.DataBuf()[pCurrSt->ecol_pos()], pCurrSt->elen_var());
                           pFieldMap->Fill (currParam, currParam, outParam);
                       }
                   }
                   break;
               }
           }
           ++st_ix;
       }
       if (isPL)
       {
           // call pl sql procedure
           DBProc_Handler::PLDBProcCall (context, pCode, dbprocCode);
       }
       else
       {
           showLanguage (*pDBProcDescriptor);
           DBProc_Handler::ExternalProcCall (context, *pDBProcDescriptor, *pFieldMap);
       }
    }
    else
    {
        // could not load code
    }
}

//----------------------------------------------------------------------------------

void DBProc_Handler::PLDBProcCall (SQLMan_Context&          context,
                                   Catalog_MessBlockObject* pCode,
                                   SQLMan_MessBlock&        dbprocCode)
{
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::PLDBProcCall", DBProc_Trace, 1);
    if (dbprocCode.StringCount() > 0)
    {   // initialize local variables with NULL
        SQLMan_StackEntry& varDescriptor = dbprocCode.StackEntry(dbprocCode.StringPos()-1);
        g10fil ("DBPROC",   3,
            dbprocCode.DataSize(), dbprocCode.DataPtr(),
            cgg_rec_key_offset + varDescriptor.epos(), varDescriptor.elen_var(), (SAPDB_Byte) csp_undef_byte,
            context.a_returncode);
    }
    if (context.IsOk())
    {
       a262Call (context, REINTERPRET_CAST(tak_sysbufferaddress, pCode), dbprocCode.DataPtr());
    }
}

//----------------------------------------------------------------------------------

#if !defined (KERNEL80)
DBProc_Handler::CallResult DBProc_Handler::ExternalProcCall (SQLMan_Context&          context,
                                       DBProc_DBProcDescriptor& DBProcDescriptor,
                                       SAPDBFields_FieldMap&    parameterMap)
{
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::ExternalProcCall", DBProc_Trace, 1);
    //
    //
    //
    // Here the code for the external procedure call has to be inserted
    //
    //
    // as an example simulate dbproc test (in P1 char(20), in P2 char(20), out P3 char(20))
    // return SUBSTR(P1, 1, 10) || SUBSTR (P2, 1, 10);

    char resBuffer[21];
    char* pRes   = &resBuffer[1];
    resBuffer[0] = csp_ascii_blank;
    DBProc_DBProcDescriptor::ParameterIterator paramIter = DBProcDescriptor.FirstParameter();
    while (paramIter)
    {
        if (paramIter.IsInputParameter())
        {
            SAPDBFields_Field field;
            parameterMap.GetField(paramIter.ParameterNo(), field);
            SQLMAN_TRACE_BUFFER(DBProc_Trace, 1, "Input Parameter", field.GetDataPtr(), field.GetLength());
            memcpy (pRes, field.GetDataPtr(), 10);
            pRes += 10;
        }
        else
        {
            SAPDBFields_Field field;
            parameterMap.GetField(paramIter.ParameterNo(), field);
            memcpy (field.GetDataPtr(), &resBuffer, sizeof(resBuffer));
        }
        ++paramIter;
    }
    SAPDBTRACE_WRITELN( DBProc_Trace, 1, "Name        : " << DBProcDescriptor.GetExternalInfo());
    SAPDBTRACE_WRITELN( DBProc_Trace, 1, "DBProc Name : " << DBProcDescriptor.GetName());
    SAPDBTRACE_WRITELN( DBProc_Trace, 1, "Date/Time   : " << DBProcDescriptor.GetCreateDateTime());

    //
    //
    //
    // end of external procedure call 
    //
    //
    //
    const bool error = false;
    if (error)
    {
        context.SetStopMsg (-4711, " unexpected error", 17); 
        return call_failed;
    }

    // return the output parameters, temporary solution only !!
    SAPDB_Int2 e = e_ok;
    a262ReturnParam (&context, dunknown, NULL, 0, e);
    DBProc_DBProcDescriptor::ParameterIterator outParamIter = DBProcDescriptor.FirstParameter();
    while (outParamIter)
    {
        if (outParamIter.IsOutputParameter())
        {
            SAPDBFields_Field field;
            parameterMap.GetField(outParamIter.ParameterNo(), field);
            a262ReturnParam (&context, outParamIter.ParamDataType() , 
                REINTERPRET_CAST(tsp00_MoveObjPtr, field.GetDataPtr()), field.GetLength(), e);
            if (e_ok != e)
            {
                context.ThrowError(e);
            }
        }
        ++outParamIter;
    }
    return call_ok;
}

#else

/*----------------------------------------*/

static Proc_ISession *
findProcServerSession (
    SQLMan_Context           & context,
    DBProc_DBProcDescriptor  & DBProcDescriptor)
{
    SAPDBTRACE_ROUTINE_DEBUG ("findProcServerSession", DBProc_Trace, 1);
    /*
     * find an existing session 
     *   depending on the dbproc language
     *   TODO
     */
    Proc_ISession * session = NULL;
    return session;
}

/*----------------------------------------*/

static bool
selectProcServerConfig (
    SQLMan_Context * context,
    const char     * languageString,
    char           * serverKind,
    char           * commandLine,
    char           * extraInfo)
{
    bool            result = false;
    IFR_Length      kindLength = 1;
    IFR_Length      commandlineLength = 256;
    IFR_Length      extraLength = 256;
    static const char * select = "SELECT INFOKIND, COMMANDLINE, EXTRAINFO INTO ?, ?, ? FROM SYSDBA.SYS_PROCSERVER_CONFIG WHERE SERVERKIND = ?";
    KSQL_PreparedStatement::SQLCode rc = 0;
    SQLMan_SQLStatementContext newStatementScope(*context);
    KSQL_Connection con;
    if (con.isConnected()) {
        /*
         * prepare statement
         */
        KSQL_PreparedStatement stmt = con.createPreparedStatement();
        rc = stmt.prepare(select);
        if (rc == 0) {
            /*
             * bind parameters
             */
            stmt.bindAsciiOutput (1, serverKind, &kindLength, false);
            stmt.bindAsciiOutput (2, commandLine, &commandlineLength);
            stmt.bindAsciiOutput (3, extraInfo, &extraLength);
            stmt.bindInput (4, languageString, strlen (languageString));
            /*
             * execute query
             */
            rc = stmt.execute();
        }
        if (rc != 0) {
            /*
             * handle errors
             * TODO
             */
        }
        else {
            result = true;
        }
    }
    return result;
}

/*----------------------------------------*/

static Proc_IFactory *
createFactory (
    SQLMan_Context           * context,
    DBProc_DBProcDescriptor  * DBProcDescriptor)
{
    Proc_IFactory * result = NULL;
    char            serverKind;
    char            commandLine [256];
    char            extraInfo [256];
    const char *    languageString = DBProcDescriptor->GetLanguageString ();
    
    if (!selectProcServerConfig (context, languageString,
            &serverKind, commandLine, extraInfo)) {
        return NULL;
    }
    //result = new (ProcRTE_Runtime::allocator ()) 
    //    Proc_ServerSessionFactory(commandLine);
    result = reinterpret_cast<Proc_IFactory *> (
        ProcRTE_Runtime::alloc (sizeof (Proc_ServerSessionFactory)));
    if (result == NULL) {
        return NULL;
    }
    new (reinterpret_cast<SAPDB_Byte*> (result)) Proc_ServerSessionFactory(commandLine);
    Proc_FactoryDirectory::addFactory (DBProcDescriptor->GetLanguage (),
            languageString, result);
    return result;
}

/*----------------------------------------*/

static Proc_ISession *
createProcServerSession (
    SQLMan_Context           & context,
    DBProc_DBProcDescriptor  & DBProcDescriptor)
{
    SAPDBTRACE_ROUTINE_DEBUG ("createProcServerSession", DBProc_Trace, 1);
    /*
     * find or create the proc server session factory
     *   depending on the dbproc language
     *   TODO
     */
    Proc_IFactory * factory = Proc_FactoryDirectory::findFactory (
        DBProcDescriptor.GetLanguage (), DBProcDescriptor.GetLanguageString ());
    if (factory == NULL) {
        /*
         * create a new factory
         */
        factory = createFactory (&context, &DBProcDescriptor);
    }
    if (factory == NULL) {
        context.GetErrorList ().AppendNewMessage (SAPDBErr_MessageList (DBPROC_CONTEXT,
            DBPROC_ERR_TYPE_NOT_DEFINED, DBProcDescriptor.GetLanguageString ()));
        return NULL;
    }
    /*
     * create a new session object
     */
    Proc_ISession * session = factory->createSession (&context.GetErrorList ());
    if (session == NULL) {
        context.SetStopMsg (-4711, "failed to start proc server", 27); 
        return NULL;
    }
    /*
     * store the newly created session object 
     *  in the session context
     *   TODO
     */
    /*
     * return result
     */
    return session;
}

/*----------------------------------------*/

DBProc_Handler::CallResult DBProc_Handler::ExternalProcCall (
    SQLMan_Context          & context,
    DBProc_DBProcDescriptor & DBProcDescriptor,
    SAPDBFields_FieldMap    & parameterMap)
{
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::ExternalProcCall", DBProc_Trace, 1);
    Proc::Error err;
    
    SAPDBTRACE_WRITELN( DBProc_Trace, 1, "Name        : " << DBProcDescriptor.GetExternalInfo());
    SAPDBTRACE_WRITELN( DBProc_Trace, 1, "DBProc Name : " << DBProcDescriptor.GetName());
    SAPDBTRACE_WRITELN( DBProc_Trace, 1, "Date/Time   : " << DBProcDescriptor.GetCreateDateTime());
    SAPDBTRACE_WRITELN( DBProc_Trace, 1, "Language    : " << DBProcDescriptor.GetLanguageString());
    /*
     * get the proc server session
     */
    Proc_ISession * session;
    session = findProcServerSession (
            context, DBProcDescriptor);
    if (session != NULL) {
        err = session->callDBProc (&context, &DBProcDescriptor, &parameterMap);
    }
    else {
        Proc_ParameterCopy paramCopy (&context.GetAllocator (), &parameterMap);
        session = createProcServerSession (context, DBProcDescriptor);
        if (session == NULL) {
            return call_failed;
        }
        err = session->callDBProc (&context, &DBProcDescriptor, &paramCopy);
    }
    session->release ();
    //
    //
    //
    // end of external procedure call 
    //
    //
    //
    const bool error = false;
    if (error)
    {
        context.SetStopMsg (-4711, " unexpected error", 17); 
        return call_failed;
    }

    // return the output parameters, temporary solution only !!
    SAPDB_Int2 e = e_ok;
    a262ReturnParam (&context, dunknown, NULL, 0, e);
    DBProc_DBProcDescriptor::ParameterIterator outParamIter = DBProcDescriptor.FirstParameter();
    while (outParamIter)
    {
        if (outParamIter.IsOutputParameter())
        {
            SAPDBFields_Field field;
            parameterMap.GetField(outParamIter.ParameterNo(), field);
            a262ReturnParam (&context, outParamIter.ParamDataType() , 
                REINTERPRET_CAST(tsp00_MoveObjPtr, field.GetDataPtr()), field.GetLength(), e);
            if (e_ok != e)
            {
                context.ThrowError(e);
            }
        }
        ++outParamIter;
    }
    return call_ok;
}

#endif
/*----------------------------------------*/

bool DBProc_Handler::CheckExternalProc (
    SQLMan_Context          & context,
    DBProc_DBProcDescriptor & DBProcDescriptor)
{
    SAPDBTRACE_ROUTINE_DEBUG ("DBProc_Handler::CheckExternalProc", DBProc_Trace, 1);
    return true;
}

/*----------------------------------------*/

