/* $Id: cursor.cpp 535289 2017-05-08 13:38:35Z ivanov $
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Author:  Vladimir Soussov
 *
 * File Description:  ODBC cursor command
 *
 */

#include <ncbi_pch.hpp>
#include <dbapi/driver/odbc/interfaces.hpp>
#include <dbapi/error_codes.hpp>


#define NCBI_USE_ERRCODE_X   Dbapi_Odbc_Cmds

#undef NCBI_DATABASE_THROW
#undef NCBI_DATABASE_RETHROW

#define NCBI_DATABASE_THROW(ex_class, message, err_code, severity) \
    NCBI_ODBC_THROW(ex_class, message, err_code, severity)
#define NCBI_DATABASE_RETHROW(prev_ex, ex_class, message, err_code, severity) \
    NCBI_ODBC_RETHROW(prev_ex, ex_class, message, err_code, severity)

// Accommodate all the code of the form
//     string err_message = "..." + GetDbgInfo();
//     DATABASE_DRIVER_ERROR(err_message, ...);
// which will still pick up the desired context due to
// NCBI_DATABASE_(RE)THROW's above redefinitions.
#define GetDbgInfo() 0


BEGIN_NCBI_SCOPE


/////////////////////////////////////////////////////////////////////////////
//
//  CODBC_CursorCmdBase
//

CODBC_CursorCmdBase::CODBC_CursorCmdBase(CODBC_Connection& conn,
                                         const string& cursor_name,
                                         const string& query)
: CStatementBase(conn, cursor_name, query)
, m_CursCmd(conn, query)
{
    SetDbgInfo("Cursor Name: \"" + cursor_name + "\"; SQL Command: \""
               + query + "\"");
}

CODBC_CursorCmdBase::~CODBC_CursorCmdBase(void)
{
}

CDBParams& CODBC_CursorCmdBase::GetBindParams(void)
{
    return m_CursCmd.GetBindParams();
}

CDBParams& CODBC_CursorCmdBase::GetDefineParams(void)
{
    return m_CursCmd.GetDefineParams();
}

int CODBC_CursorCmdBase::RowCount(void) const
{
    return static_cast<int>(m_RowCount);
}


/////////////////////////////////////////////////////////////////////////////
//
//  CODBC_CursorCmd::
//

CODBC_CursorCmd::CODBC_CursorCmd(CODBC_Connection& conn,
                                 const string& cursor_name,
                                 const string& query)
: CODBC_CursorCmdBase(conn, cursor_name, query)
{
}

CDB_Result* CODBC_CursorCmd::OpenCursor(void)
{
    // need to close it first
    CloseCursor();

    SetHasFailed(false);

    // declare the cursor
    try {
        m_CursCmd.SetCursorName(GetCmdName());
        m_CursCmd.Send();
    } catch (const CDB_Exception& e) {
        string err_message = "Failed to declare cursor." + GetDbgInfo();
        DATABASE_DRIVER_ERROR_EX( e, err_message, 422001 );
    }

    SetCursorDeclared();

    SetCursorOpen();
    GetBindParamsImpl().LockBinding();

    m_Res.reset(new CODBC_CursorResult(&m_CursCmd));

    return Create_Result(*m_Res);
}


bool CODBC_CursorCmd::Update(const string&, const string& upd_query)
{
    if (!CursorIsOpen())
        return false;

    try {
        string buff = upd_query + " where current of " + GetCmdName();

        unique_ptr<CDB_LangCmd> cmd( GetConnection().LangCmd(buff) );
        cmd->Send();
        cmd->DumpResults();
    } catch (const CDB_Exception& e) {
        string err_message = "Update failed." + GetDbgInfo();
        DATABASE_DRIVER_ERROR_EX( e, err_message, 422004 );
    }

    return true;
}

CDB_BlobDescriptor* CODBC_CursorCmd::x_GetBlobDescriptor(unsigned int item_num)
{
    if(!CursorIsOpen() || m_Res.get() == 0) {
        return NULL;
    }

    string cond = "current of " + GetCmdName();

    return m_CursCmd.m_Res->GetBlobDescriptor(item_num, cond);
}

bool CODBC_CursorCmd::UpdateBlob(unsigned int item_num, CDB_Stream& data,
                                 bool log_it)
{
    CDB_BlobDescriptor* desc = x_GetBlobDescriptor(item_num);
    if(desc == 0) return false;
    unique_ptr<I_BlobDescriptor> g((I_BlobDescriptor*)desc);

    return GetConnection().SendData(*desc, data, log_it);
}

CDB_SendDataCmd* CODBC_CursorCmd::SendDataCmd(unsigned int item_num, size_t size,
                                              bool log_it,
                                              bool dump_results)
{
    CDB_BlobDescriptor* desc = x_GetBlobDescriptor(item_num);
    if(desc == 0) return 0;
    unique_ptr<I_BlobDescriptor> g((I_BlobDescriptor*)desc);

    return GetConnection().SendDataCmd((I_BlobDescriptor&)*desc, size, log_it,
                                       dump_results);
}

bool CODBC_CursorCmd::Delete(const string& table_name)
{
    if (!CursorIsOpen())
        return false;

    try {
        string buff = "delete " + table_name + " where current of " + GetCmdName();

        unique_ptr<CDB_LangCmd> cmd(GetConnection().LangCmd(buff));
        cmd->Send();
        cmd->DumpResults();
    } catch (const CDB_Exception& e) {
        string err_message = "Update failed." + GetDbgInfo();
        DATABASE_DRIVER_ERROR_EX( e, err_message, 422004 );
    }

    return true;
}


bool CODBC_CursorCmd::CloseCursor()
{
    if (!CursorIsOpen())
        return false;

    m_Res.reset();

    if (CursorIsOpen()) {
        SetCursorOpen(false);
    }

    if (CursorIsDeclared()) {
        m_CursCmd.CloseCursor();

        SetCursorDeclared(false);
    }

    return true;
}


CODBC_CursorCmd::~CODBC_CursorCmd()
{
    try {
        DetachInterface();

        GetConnection().DropCmd(*this);

        CloseCursor();
    }
    NCBI_CATCH_ALL_X( 2, NCBI_CURRENT_FUNCTION )
}

///////////////////////////////////////////////////////////////////////////////
CODBC_CursorCmdExpl::CODBC_CursorCmdExpl(CODBC_Connection& conn,
                                         const string& cursor_name,
                                         const string& query) :
    CODBC_CursorCmd(conn,
                    cursor_name,
                    "declare " + cursor_name + " cursor for " + query)
{
}

CODBC_CursorCmdExpl::~CODBC_CursorCmdExpl(void)
{
    try {
        DetachInterface();

        GetConnection().DropCmd(*this);

        CloseCursor();
    }
    NCBI_CATCH_ALL_X( 3, NCBI_CURRENT_FUNCTION )
}

CDB_Result* CODBC_CursorCmdExpl::OpenCursor(void)
{
    // need to close it first
    CloseCursor();

    SetHasFailed(false);

    // declare the cursor
    try {
        m_CursCmd.Send();
        m_CursCmd.DumpResults();
    } catch (const CDB_Exception& e) {
        string err_message = "Failed to declare cursor." + GetDbgInfo();
        DATABASE_DRIVER_ERROR_EX( e, err_message, 422001 );
    }

    SetCursorDeclared();

    try {
        unique_ptr<impl::CBaseCmd> stmt(GetConnection().xLangCmd("open " + GetCmdName()));

        stmt->Send();
        stmt->DumpResults();
    } catch (const CDB_Exception& e) {
        string err_message = "Failed to open cursor." + GetDbgInfo();
        DATABASE_DRIVER_ERROR_EX( e, err_message, 422002 );
    }

    SetCursorOpen();
    GetBindParamsImpl().LockBinding();

    m_LCmd.reset(GetConnection().xLangCmd("fetch " + GetCmdName()));
    m_Res.reset(new CODBC_CursorResultExpl(m_LCmd.get()));

    return Create_Result(*m_Res);
}


bool CODBC_CursorCmdExpl::Update(const string&, const string& upd_query)
{
    if (!CursorIsOpen())
        return false;

    try {
        m_LCmd->Cancel();

        string buff = upd_query + " where current of " + GetCmdName();

        unique_ptr<CDB_LangCmd> cmd( GetConnection().LangCmd(buff) );
        cmd->Send();
        cmd->DumpResults();
    } catch (const CDB_Exception& e) {
        string err_message = "Update failed." + GetDbgInfo();
        DATABASE_DRIVER_ERROR_EX( e, err_message, 422004 );
    }

    return true;
}

CDB_BlobDescriptor*
CODBC_CursorCmdExpl::x_GetBlobDescriptor(unsigned int item_num)
{
    if(!CursorIsOpen() || m_Res.get() == 0 || m_LCmd.get() == 0) {
        return NULL;
    }

    string cond = "current of " + GetCmdName();

    return m_LCmd->m_Res->GetBlobDescriptor(item_num, cond);
}

bool CODBC_CursorCmdExpl::UpdateBlob(unsigned int item_num, CDB_Stream& data,
                                     bool log_it)
{
    CDB_BlobDescriptor* desc = x_GetBlobDescriptor(item_num);
    if(desc == 0) return false;
    unique_ptr<I_BlobDescriptor> g((I_BlobDescriptor*)desc);

    m_LCmd->Cancel();

    return GetConnection().SendData(*desc, data, log_it);
}

CDB_SendDataCmd* CODBC_CursorCmdExpl::SendDataCmd(unsigned int item_num, size_t size,
                                                  bool log_it,
                                                  bool dump_results)
{
    CDB_BlobDescriptor* desc = x_GetBlobDescriptor(item_num);
    if(desc == 0) return 0;
    unique_ptr<I_BlobDescriptor> g((I_BlobDescriptor*)desc);

    m_LCmd->Cancel();

    return GetConnection().SendDataCmd((I_BlobDescriptor&)*desc, size, log_it,
                                       dump_results);
}

bool CODBC_CursorCmdExpl::Delete(const string& table_name)
{
    if (!CursorIsOpen())
        return false;

    try {
        m_LCmd->Cancel();

        string buff = "delete " + table_name + " where current of " + GetCmdName();

        unique_ptr<CDB_LangCmd> cmd(GetConnection().LangCmd(buff));
        cmd->Send();
        cmd->DumpResults();
    } catch (const CDB_Exception& e) {
        string err_message = "Update failed." + GetDbgInfo();
        DATABASE_DRIVER_ERROR_EX( e, err_message, 422004 );
    }

    return true;
}


bool CODBC_CursorCmdExpl::CloseCursor()
{
    if (!CursorIsOpen())
        return false;

    m_Res.reset();
    m_LCmd.reset();

    if (CursorIsOpen()) {
        string buff = "close " + GetCmdName();
        try {
            unique_ptr<CODBC_LangCmd> cmd(GetConnection().xLangCmd(buff));

            cmd->Send();
            cmd->DumpResults();
        } catch (const CDB_Exception& e) {
            string err_message = "Failed to close cursor." + GetDbgInfo();
            DATABASE_DRIVER_ERROR_EX( e, err_message, 422003 );
        }

        SetCursorOpen(false);
    }

    if (CursorIsDeclared()) {
        string buff = "deallocate " + GetCmdName();

        try {
            unique_ptr<CODBC_LangCmd> cmd(GetConnection().xLangCmd(buff));

            cmd->Send();
            cmd->DumpResults();
        } catch (const CDB_Exception& e) {
            string err_message = "Failed to deallocate cursor." + GetDbgInfo();
            DATABASE_DRIVER_ERROR_EX( e, err_message, 422003 );
        }

        SetCursorDeclared(false);
    }

    return true;
}


END_NCBI_SCOPE


