/********************************************
[Home]  [oci_tl Home] [Back]
********************************************/

/* Copyright (c) Vasiliy Astapov 2000. All Rights Reserved. */

#ifndef __oci_tl__
#define __oci_tl__

#include <oci.h>
#include <string>
#include <algorithm>
#include <exception>
#include <cassert>

#include <iostream>
using namespace std;

namespace ina_lib
{
//begin namespace

using namespace std;

typedef unsigned char uchar;

#define P_ENV poci_env->p_env
#define P_ERR db->p_err
#define P_SVC db->p_svc

#define CHECKERROR() db->checkerror(rc)

/////////////////////////////////////////////////
//  oci_tl_exception
/////////////////////////////////////////////////

class oci_tl_exception : public exception
{
public:
    oci_tl_exception() throw()
    {
        error_msg="Error - Another error";
    }
    oci_tl_exception(const char* str) throw()
    {
        error_msg=string("Error - ")+str;
    }
    const char* what() const throw()
    {
        return error_msg.c_str();
    }
private:
    string error_msg;
};

/////////////////////////////////////////////////
//  oci_var
/////////////////////////////////////////////////

class oci_var_base
{
//friend class oci_cursor;
public:
    operator OCIBind**()
    {
        return &p_bnd;
    }
    operator OCIDefine**()
    {
        return &p_dfn;
    }
    const string name()
    {
        return s_name;
    }
    void* ptr()
    {
        return v;
    }
    int type()
    {
        return i_type;
    }
    int size()
    {
        return sz;
    }
    explicit oci_var_base(const char* nm) throw()
    {
        init();
        if(nm)
            s_name=nm;
        else
            s_name="";
    }
    bool is_null()
    {
        if(ind==-1)
            return true;
        return false;
    }
    sb2* get_indicator()
    {
        return &ind;
    }
    void* get_val()
    {
        if(is_null())
            *v=0;
        return v;
    }
//  Operators

protected:
    sb2 ind;
    OCIBind *p_bnd;
    OCIDefine *p_dfn;
    string s_name;

    int i_type;

    char *v;
    int sz;
    void init()
    {
        p_bnd=0;
        p_dfn=0;
        v=0;
        i_type=0;
        ind=0;
    }
private:
//No access from basic_stream <<
    operator void*();
};

template<int _sz> class oci_var : public oci_var_base
{
public:
    explicit oci_var(const char* nm):oci_var_base(nm)
    {
        init();
    }
    ~oci_var()
    {
        delete [] v;
    }
protected:
    void init()
    {
        oci_var_base::init();
        sz=_sz;
        v=new char[_sz];
        *v=0;
    }

    //currently not supported
    oci_var& operator=(oci_var&);
    oci_var(oci_var&);
};

/////////////////////////////////////////////////
//  oci_var_string
/////////////////////////////////////////////////
template <int _sz=128>
class oci_var_string : public oci_var<_sz>
{
public:
    explicit oci_var_string<_sz>(const char* nm=0):oci_var<_sz>(nm)
    {
        i_type=SQLT_STR;
    }
    const char* c_str()
    {
        return (const char*)get_val();
    }
    operator const char*()
    {
        return c_str();
    }
    oci_var_base& operator=(const char* str)
    {
		memset(v,0,sz);
        int n=sz-1<strlen(str)?sz-1:strlen(str);
        strncpy(v,str,n);
        return *this;
    }
    oci_var_base& operator=(int val)
    {
        sprintf(v,"%d",val);
        return *this;
    }
};

class oci_var_int : public oci_var<4>
{
public:
    explicit oci_var_int(const char* nm=0):oci_var<4>(nm)
    {
        i_type=SQLT_INT;
    }
    const int i_val()
    {
        return *(int*)get_val();
    }
    operator const int()
    {
        return i_val();
    }
    oci_var_base& operator=(int n)
    {
        *(int*)v=n;
        return *this;
    }
};

/////////////////////////////////////////////////
//  oci_db
/////////////////////////////////////////////////

class oci_env
{
public:
    operator OCIEnv*()
    {
        assert(p_env);
        return p_env;
    }
    oci_env() :created(false)
    {
        p_env=0;
        create();
    }
    ~oci_env()
    {
        destroy();
    }
protected:
    OCIEnv *p_env;
    bool created;
private:
    void create()
    {
        if(created)
            return;
        int rc;
        /* Initialize OCI */
        rc = OCIInitialize((ub4) OCI_THREADED|OCI_OBJECT, (dvoid *)0,
           (dvoid * (*)(dvoid *, size_t)) 0,
           (dvoid * (*)(dvoid *, dvoid *, size_t))0,
           (void (*)(dvoid *, dvoid *)) 0 );

        /* Initialize evironment */
        rc = OCIEnvInit( (OCIEnv **) &p_env, OCI_DEFAULT  /*| OCI_NO_MUTEX*/ , (size_t) 0, (dvoid **) 0 );
        if(rc)
            throw oci_tl_exception("Can't initialize environment");
        created=true;
    }
    void destroy()
    {
        if(created)
            OCIHandleFree((dvoid *) p_env, OCI_HTYPE_ENV);
        created=false;
    }
};

class oci_db
{
public:
    operator OCIError* ()
    {
        assert(p_err);
        return p_err;
    }
    operator OCISvcCtx* ()
    {
        assert(p_svc);
        return p_svc;
    }
    operator OCIEnv*()
    {
        assert(poci_env);
        return (OCIEnv*)*poci_env;
    }

    oci_db() throw()
    {
        logged_in=false;
        b_env_holder=false;
        poci_env=0;
        p_err=0;
        p_svc=0;
    }
    oci_db(oci_env &env) throw()
    {
        logged_in=false;
        b_env_holder=false;
        poci_env=&env;
    }
    oci_db(const char* username,const char* password, const char* service,oci_env* env=0)
    {
        logged_in=false;
        if(!env)
        {
            create_env();
            init_by_env(*poci_env);
        }
        else
            init_by_env(*env);
        login(username,password,service);
    }

    ~oci_db()
    {
        cerr<<"In destructor"<<endl;
        logout();
        if(b_env_holder)
            delete_env();
    }
    void init_by_env(oci_env &env)
    {
        poci_env=&env;
    }
    void login(const char* username,const char* password, const char* service)
    {
        _username=username;
        _password=password;
        _service=service;

        if(logged_in)
            logout();
        if(!poci_env)
            create_env();

        int rc=0;

        /* Initialize handles */
        rc = OCIHandleAlloc( (dvoid *) static_cast<OCIEnv*>(*this), (dvoid **) &p_err, OCI_HTYPE_ERROR,
           (size_t) 0, (dvoid **) 0);
        
        if(rc!=0)
            throw oci_tl_exception("Can't allocate error buffer");
        
        rc = OCIHandleAlloc( (dvoid *) static_cast<OCIEnv*>(*this), (dvoid **) &p_svc, OCI_HTYPE_SVCCTX,
           (size_t) 0, (dvoid **) 0);
        
        if(rc!=0)
            throw oci_tl_exception("Can't allocate service handle");

        rc = OCILogon(static_cast<OCIEnv*>(*this), p_err, &p_svc,
            (uchar*)username, strlen(username),
            (uchar*)password, strlen(password),
            (uchar*)service, strlen(service));

        checkerror(rc);

        logged_in=true;
    }

    void logout()
    {
        if(!logged_in)
            return;
        OCILogoff(p_svc, p_err);                           
        OCIHandleFree(p_svc,OCI_HTYPE_SVCCTX);
        OCIHandleFree(p_err,OCI_HTYPE_ERROR);
        //p_svc=0;
        //p_err=0;
        logged_in=false;
    }

    void checkerror(int rc)
    {
        if (rc != 0) 
        {
            *errbuf=0;
            OCIErrorGet((dvoid *)p_err, (ub4) 1, (text *) NULL, &errcode, (uchar*)errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);
//            if(check_null(errbuf))
//                return;
            throw oci_tl_exception(errbuf);
        }

    }
protected:
    void _assert_()
    {
        assert(poci_env);
        assert(p_err);
        assert(p_svc);

    }
    void create_env()
    {
        if(b_env_holder)
            delete_env();
        poci_env=new oci_env;
        b_env_holder=true;
    }
    void delete_env()
    {
        if(poci_env)
            delete poci_env;
        poci_env=0;
        b_env_holder=false;
    }
/*
    bool check_null(const char* errmsg)
    {
        string err=errmsg;
        if(err.find("ORA-01405")!=string::npos)
            return true;
        return false;
    }
*/
    oci_env* poci_env;
    OCIError *p_err;
    OCISvcCtx *p_svc;

    char errbuf[2048];
    int errcode;
    bool b_env_holder;
    bool logged_in;

    string _username;
    string _password;
    string _service;
};

//////////////////////////////////////////////////////////////////
//  oci_cursor
//////////////////////////////////////////////////////////////////

class oci_cursor
{
public:
    bool eof()
    {
        return _eof;
    }
    explicit oci_cursor(oci_db &rdb)
    {
        open();
        db=&rdb;
    }
    explicit oci_cursor(oci_db &rdb,const char* nm)
    {
        open();
        name=nm;
        db=&rdb;
    }
    ~oci_cursor()
    {
        close();
    }
    void close()
    {
        int rc;
        if(p_sql)
            rc = OCIHandleFree((dvoid *) p_sql, OCI_HTYPE_STMT);
		p_sql=0;
    }
    void open()
    {
        fetch_count=1;
        executed=false;
        cur_bind_pos_out=0;
        cur_bind_pos_in=0;
        _eof=false;
        parsed=false;
		p_bnd=0;
		p_sql=0;
    }
    void bind(int pos, oci_var_base &var)
    {
        assert(&var);
        if(!parsed)
            throw oci_tl_exception("Statment not parsed");
        int rc;
        rc = OCIBindByPos(p_sql, static_cast<OCIBind**>(var), static_cast<OCIError*>(*db)/*P_ERR*/, pos,
           (dvoid *) var.ptr(), var.size(), var.type(), /*(dvoid *) 0*/ var.get_indicator(),
           (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT);
        CHECKERROR();
    }
    void bind(const char* name, oci_var_base &var)
    {
        assert(&var);
        if(!parsed)
            throw oci_tl_exception("Statment not parsed");
        int rc;
        rc = OCIBindByName(p_sql, static_cast<OCIBind**>(var), static_cast<OCIError*>(*db)/*P_ERR*/, (text *) name,
           -1, (dvoid *) var.ptr(), var.size(), var.type(), /*(dvoid *) 0*/ var.get_indicator(),
           (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT);
        CHECKERROR();
    }
    void bind(oci_var_base &var)
    {
        assert(&var);
        if(var.name().size())
            bind(var.name().c_str(),var);
        else
        {
            //throw oci_tl_exception("OCI_TL : variable haven't name");
            bind(++cur_bind_pos_in,var);
        }
    }
    void bind_out(int pos, oci_var_base &var)
    {
        assert(&var);
        //if(!executed)
            //throw oci_tl_exception("Can't bind - wasn't executed");
        int rc;
        /* Define the select list items */
        rc = OCIDefineByPos(p_sql, static_cast<OCIDefine**>(var), static_cast<OCIError*>(*db)/*P_ERR*/, pos,
           (dvoid *) var.ptr(), var.size(), var.type(), /*(dvoid *) 0*/ var.get_indicator(),
           (ub2 *)0,(ub2 *)0, OCI_DEFAULT);
        CHECKERROR();
    }
    void bind_out(oci_var_base &var)
    {
        bind_out(++cur_bind_pos_out,var);
    }
/*
    void bind_out(const char* name,oci_cursor& var)
    {
        bind(name,var);
    }
*/
    void bind(const char* name,oci_cursor& var)
    {
        assert(&var);
        if(!parsed)
            throw oci_tl_exception("Statment not parsed");
        int rc;
        rc = OCIHandleAlloc( (dvoid *) static_cast<OCIEnv*>(*db), (dvoid **) &var.p_sql, OCI_HTYPE_STMT ,
           (size_t) 0, (dvoid **) 0);
        CHECKERROR();

        int type=SQLT_RSET;
        rc = OCIBindByName (p_sql, &var.p_bnd, static_cast<OCIError*>(*db)/*P_ERR*/,
             (text *)name, (sb4)strlen((char *)name),
             (dvoid *)&var.p_sql, (sb4) 0,  SQLT_RSET, (dvoid *)0,
               (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0,   (ub4)OCI_DEFAULT);

        CHECKERROR();
    }
    void bind(oci_cursor &var)
    {
        assert(&var);
        if(var.name.size())
            bind(var.name.c_str(),var);
        else
            throw oci_tl_exception("OCI_TL : cursor haven't name");

    }
    void execute()
    {
        _assert_();
        if(!parsed)
            throw oci_tl_exception("Statment not parsed");
        int rc;
        /* Execute the SQL statment */
        rc = OCIStmtExecute(static_cast<OCISvcCtx*>(*db), p_sql, static_cast<OCIError*>(*db)/*P_ERR*/, (ub4) fetch_count, (ub4) 0,
           (CONST OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT);

        if(rc==100)
        {
            executed=true;
            _eof=true;
            return;
        }

        CHECKERROR();
        
        executed=true;
        _eof=false;
    }
    bool fetch()
    {
        _assert_();
        int rc;
        rc = OCIStmtFetch(p_sql, static_cast<OCIError*>(*db)/*P_ERR*/, 1, 0, 0);
        if(rc == OCI_NO_DATA)
            _eof=true;
        else if(rc!=0)
        {
            CHECKERROR();
        }
        else
            _eof=false;
        return !eof();
    }
    void parse(const char* stmt)
    {
        _assert_();
        int rc;
        string s=stmt;
        transform(s.begin(),s.end(),s.begin(),toupper);
        if(s.find("SELECT")!=string::npos)
            fetch_count=0;
        /* Allocate and prepare SQL statement */
        rc = OCIHandleAlloc( (dvoid *) static_cast<OCIEnv*>(*db), (dvoid **) &p_sql,
            OCI_HTYPE_STMT, (size_t) 0, (dvoid **) 0);
        CHECKERROR();
        rc = OCIStmtPrepare(p_sql, static_cast<OCIError*>(*db)/*P_ERR*/, (uchar*)stmt,
            (ub4) strlen(stmt), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
        CHECKERROR();
        parsed=true;
    }
protected:
    bool parsed;
    bool _eof;
    int fetch_count;
    string name;
    OCIBind *p_bnd;
    OCIStmt *p_sql;
    oci_db *db;
    bool executed;
    int cur_bind_pos_out;
    int cur_bind_pos_in;

    void _assert_()
    {
        assert(db);
    }
};

//////////////////////////////////////////////////////////////////
//  oci_stream
//////////////////////////////////////////////////////////////////

class oci_stream : public oci_cursor
{
public:
    explicit oci_stream(oci_db &rdb):oci_cursor(rdb){};
    explicit oci_stream(oci_db &rdb,const char* name) : oci_cursor(rdb,name){};

    oci_stream& begin()
    {
        return *this;
    }
    bool end()
    {
        return eof();
    }
    oci_stream& parse(const char* stmt)
    {
        oci_cursor::parse(stmt);
        return *this;
    }
//operators
    oci_stream& operator<<(oci_var_base& var)
    {
        bind(var);
        return *this;
    }
    oci_stream& operator<<(oci_cursor& var)
    {
        bind(var);
        return *this;
    }
    oci_stream& operator<<(oci_stream& (*f)(oci_stream& str))
    {
        f(*this);
        return *this;
    }
    oci_stream& operator>>(oci_var_base& var)
    {
        bind_out(var);
        return *this;
    }

//Iterator class
    class iterator
    {
        oci_stream* str;
    public:
        iterator():str(0){}
        iterator(oci_stream& s):str(&s){};
    private:

//	not implemented yet
        oci_stream& operator++();
        bool operator !=(oci_stream& s);
    };
};

// global functions
oci_stream& _parse(oci_stream& s,const char* stmt);

struct smanip
{
    oci_stream& (*f)(oci_stream&,const char*);
    string str;

    smanip(oci_stream& (*ff)(oci_stream&,const char* ),const char* s):f(ff),str(s){};
};

inline smanip parse(const char* stmt)
{
    return smanip(_parse,stmt);
}

inline oci_stream& operator<<(oci_stream& str,smanip& m)
{
    return m.f(str,m.str.c_str());
}

inline oci_stream& _parse(oci_stream& s,const char* stmt)
{
    return s.parse(stmt);
}

inline oci_stream& execute(oci_stream& str)
{
    str.execute();
    return str;
}

// end ina_lib namespace
};
//
#endif //__oci_tl__

/**************************************************
[Home] [oci_tl Home ] [Back] [Top]
**************************************************/