From 63295f6e051e75cf07cb6212a2631df8eb8a90c4 Mon Sep 17 00:00:00 2001
From: Constantin Michael <constantin@codesynthesis.com>
Date: Mon, 5 Sep 2011 12:10:45 +0200
Subject: Add database, connection, connection-factory, and transaction support

---
 odb/oracle/connection-factory.cxx | 173 ++++++++++++++++++++++
 odb/oracle/connection-factory.hxx | 164 +++++++++++++++++++++
 odb/oracle/connection.cxx         | 300 ++++++++++++++++++++++++++++++++++++++
 odb/oracle/connection.hxx         | 105 +++++++++++++
 odb/oracle/database.cxx           | 230 +++++++++++++++++++++++++++++
 odb/oracle/database.hxx           | 159 ++++++++++++++++++++
 odb/oracle/database.ixx           |  20 +++
 odb/oracle/forward.hxx            |  48 ++++++
 odb/oracle/transaction-impl.cxx   | 126 ++++++++++++++++
 odb/oracle/transaction-impl.hxx   |  59 ++++++++
 odb/oracle/transaction-impl.ixx   |  16 ++
 odb/oracle/transaction.cxx        |  26 ++++
 odb/oracle/transaction.hxx        |  65 +++++++++
 odb/oracle/transaction.ixx        |  47 ++++++
 14 files changed, 1538 insertions(+)
 create mode 100644 odb/oracle/connection-factory.cxx
 create mode 100644 odb/oracle/connection-factory.hxx
 create mode 100644 odb/oracle/connection.cxx
 create mode 100644 odb/oracle/connection.hxx
 create mode 100644 odb/oracle/database.cxx
 create mode 100644 odb/oracle/database.hxx
 create mode 100644 odb/oracle/database.ixx
 create mode 100644 odb/oracle/forward.hxx
 create mode 100644 odb/oracle/transaction-impl.cxx
 create mode 100644 odb/oracle/transaction-impl.hxx
 create mode 100644 odb/oracle/transaction-impl.ixx
 create mode 100644 odb/oracle/transaction.cxx
 create mode 100644 odb/oracle/transaction.hxx
 create mode 100644 odb/oracle/transaction.ixx

diff --git a/odb/oracle/connection-factory.cxx b/odb/oracle/connection-factory.cxx
new file mode 100644
index 0000000..ed59c35
--- /dev/null
+++ b/odb/oracle/connection-factory.cxx
@@ -0,0 +1,173 @@
+// file      : odb/oracle/connection-factory.cxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/connection-factory.hxx>
+#include <odb/oracle/exceptions.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+  using namespace details;
+
+  namespace oracle
+  {
+    //
+    // connection_factory
+    //
+
+    connection_factory::
+    ~connection_factory ()
+    {
+    }
+
+    //
+    // new_connection_factory
+    //
+
+    connection_ptr new_connection_factory::
+    connect ()
+    {
+      return connection_ptr (new (shared) connection (*db_));
+    }
+
+    void new_connection_factory::
+    database (database_type& db)
+    {
+      db_ = &db;
+    }
+
+    //
+    // connection_pool_factory
+    //
+
+    connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+    create ()
+    {
+      return pooled_connection_ptr (new (shared) pooled_connection (*db_));
+    }
+
+    connection_pool_factory::
+    ~connection_pool_factory ()
+    {
+      // Wait for all the connections currently in use to return to
+      // the pool.
+      //
+      lock l (mutex_);
+      while (in_use_ != 0)
+      {
+        waiters_++;
+        cond_.wait ();
+        waiters_--;
+      }
+    }
+
+    connection_ptr connection_pool_factory::
+    connect ()
+    {
+      lock l (mutex_);
+
+      while (true)
+      {
+        // See if we have a spare connection.
+        //
+        if (connections_.size () != 0)
+        {
+          shared_ptr<pooled_connection> c (connections_.back ());
+          connections_.pop_back ();
+
+          c->pool_ = this;
+          in_use_++;
+          return c;
+        }
+
+        // See if we can create a new one.
+        //
+        if (max_ == 0 || in_use_ < max_)
+        {
+          shared_ptr<pooled_connection> c (create ());
+          c->pool_ = this;
+          in_use_++;
+          return c;
+        }
+
+        // Wait until someone releases a connection.
+        //
+        waiters_++;
+        cond_.wait ();
+        waiters_--;
+      }
+    }
+
+    void connection_pool_factory::
+    database (database_type& db)
+    {
+      db_ = &db;
+
+      if (min_ > 0)
+      {
+        connections_.reserve (min_);
+
+        for (size_t i (0); i < min_; ++i)
+          connections_.push_back (create ());
+      }
+    }
+
+    bool connection_pool_factory::
+    release (pooled_connection* c)
+    {
+      c->pool_ = 0;
+
+      lock l (mutex_);
+
+      // Determine if we need to keep or free this connection.
+      //
+      bool keep (waiters_ != 0 ||
+                 min_ == 0 ||
+                 (connections_.size () + in_use_ <= min_));
+
+      in_use_--;
+
+      if (keep)
+        connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+
+      if (waiters_ != 0)
+        cond_.signal ();
+
+      return !keep;
+    }
+
+    //
+    // connection_pool_factory::pooled_connection
+    //
+
+    connection_pool_factory::pooled_connection::
+    pooled_connection (database_type& db)
+        : connection (db), pool_ (0)
+    {
+      callback_.arg = this;
+      callback_.zero_counter = &zero_counter;
+      shared_base::callback_ = &callback_;
+    }
+
+    connection_pool_factory::pooled_connection::
+    pooled_connection (database_type& db, OCISvcCtx* handle)
+        : connection (db, handle), pool_ (0)
+    {
+      callback_.arg = this;
+      callback_.zero_counter = &zero_counter;
+      shared_base::callback_ = &callback_;
+    }
+
+    bool connection_pool_factory::pooled_connection::
+    zero_counter (void* arg)
+    {
+      pooled_connection* c (static_cast<pooled_connection*> (arg));
+      return c->pool_ ? c->pool_->release (c) : true;
+    }
+  }
+}
diff --git a/odb/oracle/connection-factory.hxx b/odb/oracle/connection-factory.hxx
new file mode 100644
index 0000000..76bc874
--- /dev/null
+++ b/odb/oracle/connection-factory.hxx
@@ -0,0 +1,164 @@
+// file      : odb/oracle/connection-factory.hxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_CONNECTION_FACTORY_HXX
+#define ODB_ORACLE_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/connection.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    class LIBODB_ORACLE_EXPORT connection_factory
+    {
+    public:
+      virtual connection_ptr
+      connect () = 0;
+
+    public:
+      typedef oracle::database database_type;
+
+      virtual void
+      database (database_type&) = 0;
+
+      virtual
+      ~connection_factory ();
+    };
+
+    class LIBODB_ORACLE_EXPORT new_connection_factory: public connection_factory
+    {
+    public:
+      new_connection_factory ()
+          : db_ (0)
+      {
+      }
+
+      virtual connection_ptr
+      connect ();
+
+      virtual void
+      database (database_type&);
+
+    private:
+      new_connection_factory (const new_connection_factory&);
+      new_connection_factory& operator= (const new_connection_factory&);
+
+    private:
+      database_type* db_;
+    };
+
+    class LIBODB_ORACLE_EXPORT connection_pool_factory:
+      public connection_factory
+    {
+    public:
+      // The max_connections argument specifies the maximum number of
+      // concurrent connections this pool will maintain. If this value
+      // is 0 then the pool will create a new connection every time all
+      // of the existing connections are in use.
+      //
+      // The min_connections argument specifies the minimum number of
+      // connections that should be maintained by the pool. If the
+      // number of connections maintained by the pool exceeds this
+      // number and there are no active waiters for a new connection,
+      // then the pool will release the excess connections. If this
+      // value is 0 then the pool will maintain all the connections
+      // that were ever created.
+      //
+      connection_pool_factory (std::size_t max_connections = 0,
+                               std::size_t min_connections = 0)
+          : max_ (max_connections),
+            min_ (min_connections),
+            in_use_ (0),
+            waiters_ (0),
+            db_ (0),
+            cond_ (mutex_)
+      {
+        // @@ check min_ <= max_
+      }
+
+      virtual connection_ptr
+      connect ();
+
+      virtual void
+      database (database_type&);
+
+      virtual
+      ~connection_pool_factory ();
+
+    private:
+      connection_pool_factory (const connection_pool_factory&);
+      connection_pool_factory& operator= (const connection_pool_factory&);
+
+    protected:
+      class pooled_connection: public connection
+      {
+      public:
+        pooled_connection (database_type&);
+        pooled_connection (database_type&, OCISvcCtx*);
+
+      private:
+        static bool
+        zero_counter (void*);
+
+      private:
+        friend class connection_pool_factory;
+
+        shared_base::refcount_callback callback_;
+
+        // NULL pool value indicates that the connection is not in use.
+        //
+        connection_pool_factory* pool_;
+      };
+
+      friend class pooled_connection;
+
+      typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+      typedef std::vector<pooled_connection_ptr> connections;
+
+      // This function is called whenever the pool needs to create a new
+      // connection.
+      //
+      virtual pooled_connection_ptr
+      create ();
+
+    protected:
+      // Return true if the connection should be deleted, false otherwise.
+      //
+      bool
+      release (pooled_connection*);
+
+    protected:
+      const std::size_t max_;
+      const std::size_t min_;
+
+      std::size_t in_use_;  // Number of connections currently in use.
+      std::size_t waiters_; // Number of threads waiting for a connection.
+
+      database_type* db_;
+      connections connections_;
+
+      details::mutex mutex_;
+      details::condition cond_;
+    };
+  }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_CONNECTION_FACTORY_HXX
diff --git a/odb/oracle/connection.cxx b/odb/oracle/connection.cxx
new file mode 100644
index 0000000..12e8ea0
--- /dev/null
+++ b/odb/oracle/connection.cxx
@@ -0,0 +1,300 @@
+// file      : odb/oracle/connection.cxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#include <string>
+
+#include <oci.h>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/transaction.hxx>
+#include <odb/oracle/error.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/auto-descriptor.hxx>
+
+// #include <odb/oracle/statement-cache.hxx>
+
+using namespace std;
+
+namespace odb
+{
+  namespace oracle
+  {
+    connection::
+    connection (database_type& db)
+        : odb::connection (db),
+          db_ (db)
+          // statement_cache_ (new statement_cache_type (*this))
+    {
+      sword r (0);
+
+      {
+        OCIError* e (0);
+        r = OCIHandleAlloc (db_.environment (),
+                            reinterpret_cast<void**> (&e),
+                            OCI_HTYPE_ERROR,
+                            0,
+                            0);
+
+        if (r != OCI_SUCCESS)
+          throw invalid_oci_handle ();
+
+        error_.reset (e);
+      }
+
+      auto_handle<OCIAuthInfo> auth_info;
+      {
+        OCIAuthInfo* a (0);
+        r = OCIHandleAlloc (db_.environment (),
+                            reinterpret_cast<void**> (&a),
+                            OCI_HTYPE_AUTHINFO,
+                            0,
+                            0);
+
+        if (r != OCI_SUCCESS)
+          throw invalid_oci_handle ();
+
+        auth_info.reset (a);
+      }
+
+      r = OCIAttrSet (
+        auth_info,
+        OCI_HTYPE_AUTHINFO,
+        reinterpret_cast<text*> (const_cast<char*> (db_.user ().c_str ())),
+        static_cast <ub4> (db_.user ().size ()),
+        OCI_ATTR_USERNAME,
+        error_);
+
+      if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+        translate_error (error_, r);
+
+      r = OCIAttrSet (
+        auth_info,
+        OCI_HTYPE_AUTHINFO,
+        reinterpret_cast<text*> (const_cast<char*> (db_.password ().c_str ())),
+        static_cast<ub4> (db_.password ().size ()),
+        OCI_ATTR_PASSWORD,
+        error_);
+
+      if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+        translate_error (error_, r);
+
+      {
+        OCISvcCtx* s (0);
+
+        r = OCISessionGet (
+          db_.environment (),
+          error_,
+          &s,
+          auth_info,
+          reinterpret_cast<text*> (const_cast<char*> (db_.db ().c_str ())),
+          static_cast<ub4> (db_.db ().size ()),
+          0,
+          0,
+          0,
+          0,
+          0,
+          OCI_SESSGET_STMTCACHE);
+
+        if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+          translate_error (error_, r);
+
+        handle_.reset (s, error_);
+      }
+    }
+
+    connection::
+    connection (database_type& db, OCISvcCtx* handle)
+        : odb::connection (db),
+          db_ (db)
+          // statement_cache_ (new statement_cache_type (*this))
+    {
+      sword r (0);
+
+      {
+        OCIError* e (0);
+        r = OCIHandleAlloc (db_.environment (),
+                            reinterpret_cast<void**> (&e),
+                            OCI_HTYPE_ERROR,
+                            0,
+                            0);
+
+        if (r != OCI_SUCCESS)
+          throw invalid_oci_handle ();
+
+        error_.reset (e);
+      }
+
+      handle_.reset (handle, error_);
+    }
+
+    connection::
+    ~connection ()
+    {
+      // Deallocate prepared statements before we close the connection.
+      //
+      // statement_cache_.reset ();
+    }
+
+    transaction_impl* connection::
+    begin ()
+    {
+      return new transaction_impl (connection_ptr (inc_ref (this)));
+    }
+
+    unsigned long long connection::
+    execute (const char* s, std::size_t n)
+    {
+      sword r (0);
+
+      // OCI requires statement text to be NULL terminated.
+      //
+      string sql (s, n);
+
+      auto_handle<OCIStmt> stmt;
+      {
+        OCIStmt* s (0);
+        r = OCIStmtPrepare2 (handle_,
+                             &s,
+                             error_,
+                             reinterpret_cast<const text*> (sql.c_str ()),
+                             static_cast<ub4> (sql.size ()),
+                             0,
+                             0,
+                             OCI_NTV_SYNTAX,
+                             OCI_DEFAULT);
+
+        if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+          translate_error (error_, r);
+
+        stmt.reset (s, OCI_STRLS_CACHE_DELETE, error_);
+      }
+
+      ub2 stmt_type;
+      r = OCIAttrGet (stmt,
+                      OCI_HTYPE_STMT,
+                      &stmt_type,
+                      0,
+                      OCI_ATTR_STMT_TYPE,
+                      error_);
+
+      if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+        translate_error (error_, r);
+
+      ub4 row_count (0);
+
+      if (stmt_type == OCI_STMT_SELECT)
+      {
+        // Do not prefetch any rows.
+        //
+        r = OCIStmtExecute (handle_, stmt, error_, 0, 0, 0, 0, OCI_DEFAULT);
+
+        if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+          translate_error (error_, r);
+
+        // In order to succesfully execute a select statement, OCI/Oracle
+        // requires that there be OCIDefine handles provided for all select
+        // list columns. Since we are not interested in any data returned by
+        // the select statement, all buffer pointers, indication variable
+        // pointers, and data length pointers are specified as NULL (we still
+        // specify a valid data type identifier as not doing so results in
+        // undefined behaviour). This action results in truncation errors
+        // being returned for all attempted row fetches. However, cursor
+        // behaviour is normal, thus allowing us to return the row count for
+        // a select statement.
+        //
+        for (ub4 i (1); ; ++i)
+        {
+          auto_descriptor<OCIParam> param;
+          {
+            OCIParam* p (0);
+            r = OCIParamGet (stmt,
+                             OCI_HTYPE_STMT,
+                             error_,
+                             reinterpret_cast<void**> (&p),
+                             i);
+
+            if (r == OCI_ERROR)
+              break;
+
+            param.reset (p);
+          }
+
+          ub2 data_type;
+          r = OCIAttrGet (param,
+                          OCI_DTYPE_PARAM,
+                          &data_type,
+                          0,
+                          OCI_ATTR_DATA_TYPE,
+                          error_);
+
+          if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+            translate_error (error_, r);
+
+          // No need to keep track of the OCIDefine handles - these will
+          // be deallocated with the statement.
+          //
+          OCIDefine* define (0);
+          r = OCIDefineByPos (stmt,
+                              &define,
+                              error_,
+                              i,
+                              0,    // NULL value buffer pointer
+                              0,    // zero length value buffer
+                              data_type,
+                              0,    // NULL indicator pointer
+                              0,    // NULL length data pointer
+                              0,    // NULL column level return code pointer
+                              OCI_DEFAULT);
+
+          if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+            translate_error (error_, r);
+        }
+
+        while (1)
+        {
+          r = OCIStmtFetch2 (stmt, error_, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
+
+          if (r == OCI_NO_DATA)
+            break;
+          else if (r == OCI_ERROR)
+          {
+            sb4 e;
+            r = OCIErrorGet (error_, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR);
+
+            // ORA-01406 is returned if there is a truncation error. We expect
+            // and ignore all truncation errors.
+            //
+            if (e != 1406)
+              translate_error (error_, r);
+          }
+          else if (r ==  OCI_INVALID_HANDLE)
+            translate_error (error_, r);
+        }
+      }
+      else
+      {
+        // OCIStmtExecute requires a non-zero iters param for DML statements.
+        //
+        r = OCIStmtExecute (handle_, stmt, error_, 1, 0, 0, 0, OCI_DEFAULT);
+
+        if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+          translate_error (error_, r);
+      }
+
+      r = OCIAttrGet (stmt,
+                      OCI_HTYPE_STMT,
+                      &row_count,
+                      0,
+                      OCI_ATTR_ROW_COUNT,
+                      error_);
+
+      if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+        translate_error (error_, r);
+
+      return row_count;
+    }
+  }
+}
diff --git a/odb/oracle/connection.hxx b/odb/oracle/connection.hxx
new file mode 100644
index 0000000..8d0e447
--- /dev/null
+++ b/odb/oracle/connection.hxx
@@ -0,0 +1,105 @@
+// file      : odb/oracle/connection.hxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_CONNECTION_HXX
+#define ODB_ORACLE_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <memory> // std::auto_ptr
+
+#include <odb/forward.hxx>
+#include <odb/connection.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/transaction-impl.hxx>
+#include <odb/oracle/auto-handle.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    class statement;
+    class statement_cache;
+
+    class connection;
+    typedef details::shared_ptr<connection> connection_ptr;
+
+    class LIBODB_ORACLE_EXPORT connection: public odb::connection
+    {
+    public:
+      typedef oracle::statement_cache statement_cache_type;
+      typedef oracle::database database_type;
+
+      virtual
+      ~connection ();
+
+      connection (database_type&);
+      connection (database_type&, OCISvcCtx* handle);
+
+      database_type&
+      database ()
+      {
+        return db_;
+      }
+
+    public:
+      virtual transaction_impl*
+      begin ();
+
+    public:
+      using odb::connection::execute;
+
+      virtual unsigned long long
+      execute (const char* statement, std::size_t length);
+
+    public:
+      OCISvcCtx*
+      handle ()
+      {
+        return handle_;
+      }
+
+      OCIError*
+      error_handle ()
+      {
+        return error_;
+      }
+
+      // statement_cache_type&
+      // statement_cache ()
+      // {
+      //   return *statement_cache_;
+      // }
+
+    private:
+      connection (const connection&);
+      connection& operator= (const connection&);
+
+    private:
+      database_type& db_;
+
+      // It is important that the error_ member is declared before the
+      // handle_ member as handle_ depends on error_ during destruction.
+      //
+      auto_handle<OCIError> error_;
+
+      auto_handle<OCISvcCtx> handle_;
+
+      // std::auto_ptr<statement_cache_type> statement_cache_;
+    };
+  }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_CONNECTION_HXX
diff --git a/odb/oracle/database.cxx b/odb/oracle/database.cxx
new file mode 100644
index 0000000..239fa0d
--- /dev/null
+++ b/odb/oracle/database.cxx
@@ -0,0 +1,230 @@
+// file      : odb/oracle/database.cxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <oci.h>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/error.hxx>
+
+#include <odb/oracle/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+  namespace oracle
+  {
+    database::
+    database (const string& user,
+              const string& password,
+              const string& db,
+              OCIEnv* environment,
+              auto_ptr<connection_factory> factory)
+        : user_ (user),
+          password_ (password),
+          db_ (db),
+          port_ (0),
+          environment_ (environment),
+          factory_ (factory)
+    {
+      if (environment_ == 0)
+      {
+        // @@ Be aware of encoding caveats.
+        //
+        sword s (OCIEnvNlsCreate (&environment_,
+                                  OCI_THREADED,
+                                  0, 0, 0, 0, 0, 0, 0, 0));
+
+        if (s == OCI_ERROR)
+          translate_error (environment_);
+
+        auto_environment_.reset (environment_);
+      }
+    }
+
+    database::
+    database (const string& user,
+              const string& password,
+              const string& service,
+              const string& host,
+              unsigned int port,
+              OCIEnv* environment,
+              auto_ptr<connection_factory> factory)
+        : user_ (user),
+          password_ (password),
+          service_ (service),
+          host_ (host),
+          port_ (port),
+          environment_ (environment),
+          factory_ (factory)
+    {
+      if (environment_ == 0)
+      {
+        // @@ Be aware of encoding caveats.
+        //
+        sword s (OCIEnvNlsCreate (&environment_,
+                                  OCI_THREADED,
+                                  0, 0, 0, 0, 0, 0, 0, 0));
+
+        if (s == OCI_ERROR)
+          translate_error (environment_);
+
+        auto_environment_.reset (environment_);
+      }
+
+      ostringstream ss;
+
+      if (!host.empty ())
+      {
+        ss << "//" << host_;
+
+        if (port != 0)
+          ss << ":" << port;
+      }
+
+      if (!service_.empty ())
+      {
+        if (!host.empty ())
+          ss << "/" << service_;
+        else
+          ss << service_;
+      }
+
+      // @@ Quote FQ connect identifier.
+      //
+      db_ = ss.str ();
+
+      if (factory_.get () == 0)
+        factory_.reset (new connection_pool_factory ());
+
+      factory_->database (*this);
+    }
+
+    database::
+    database (int& argc,
+              char* argv[],
+              bool erase,
+              OCIEnv* environment,
+              auto_ptr<connection_factory> factory)
+        : port_ (0),
+          environment_ (environment),
+          factory_ (factory)
+    {
+      if (environment_ == 0)
+      {
+        // @@ Be aware of encoding caveats.
+        //
+        sword s (OCIEnvNlsCreate (&environment_,
+                                  OCI_THREADED,
+                                  0, 0, 0, 0, 0, 0, 0, 0));
+
+        if (s == OCI_ERROR)
+          translate_error (environment_);
+
+        auto_environment_.reset (environment_);
+      }
+
+      using namespace details;
+
+      try
+      {
+        cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+        options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+        if (ops.user_specified ())
+          user_ = ops.user ();
+
+        if (ops.password_specified ())
+          password_ = ops.password ();
+
+        if (ops.database_specified ())
+        {
+          if (ops.host_specified () ||
+              ops.port_specified () ||
+              ops.service_specified ())
+
+            throw cli_exception ("--host, --port, or --service options "
+                                 "cannot be specified together with "
+                                 "--database option");
+          db_ = ops.database ();
+        }
+        else
+        {
+          bool host_present (false);
+          ostringstream oss;
+
+          if (ops.host_specified () && !ops.host ().empty ())
+          {
+            host_present = true;
+
+            host_ = ops.host ();
+            oss << "//" << host_;
+
+            if (ops.port_specified ())
+            {
+              port_ = ops.port ();
+
+              if (port_ != 0)
+                oss << ":" << port_;
+            }
+          }
+
+          if (ops.service_specified () && !ops.service ().empty ())
+          {
+            service_ = ops.service ();
+
+            if (host_present)
+              oss << "/" << service_;
+            else
+              oss << service_;
+          }
+
+          db_ = oss.str ();
+        }
+
+        // @@ Quote FQ connect identifier.
+        //
+      }
+      catch (const cli::exception& e)
+      {
+        ostringstream oss;
+        oss << e;
+        throw cli_exception (oss.str ());
+      }
+
+      if (factory_.get () == 0)
+        factory_.reset (new connection_pool_factory ());
+
+      factory_->database (*this);
+    }
+
+    void database::
+    print_usage (std::ostream& os)
+    {
+      details::options::print_usage (os);
+    }
+
+    database::
+    ~database ()
+    {
+    }
+
+    transaction_impl* database::
+    begin ()
+    {
+      return new transaction_impl (*this);
+    }
+
+    odb::connection* database::
+    connection_ ()
+    {
+      connection_ptr c (factory_->connect ());
+      return c.release ();
+    }
+  }
+}
diff --git a/odb/oracle/database.hxx b/odb/oracle/database.hxx
new file mode 100644
index 0000000..a9be13d
--- /dev/null
+++ b/odb/oracle/database.hxx
@@ -0,0 +1,159 @@
+// file      : odb/oracle/database.hxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_DATABASE_HXX
+#define ODB_ORACLE_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <memory> // std::auto_ptr
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/connection-factory.hxx>
+#include <odb/oracle/auto-handle.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    class transaction_impl;
+
+    class LIBODB_ORACLE_EXPORT database: public odb::database
+    {
+    public:
+      database (const std::string& user,
+                const std::string& password,
+                const std::string& db,
+                OCIEnv* environment = 0,
+                std::auto_ptr<connection_factory> factory =
+                  std::auto_ptr<connection_factory> (0));
+
+      database (const std::string& user,
+                const std::string& password,
+                const std::string& service,
+                const std::string& host = "",
+                unsigned int port = 0,
+                OCIEnv* environment = 0,
+                std::auto_ptr<connection_factory> factory =
+                  std::auto_ptr<connection_factory> (0));
+
+      // Extract the database parameters from the command line. The
+      // following options are recognized:
+      //
+      // --user
+      // --password
+      // --database
+      // --service
+      // --host
+      // --port
+      // --options-file
+      //
+      // For more information, see the output of the print_usage() function
+      // below. If erase is true, the above options are removed from the
+      // argv array and the argc count is updated accordingly. This
+      // constructor may throw the cli_exception exception.
+      //
+      database (int& argc,
+                char* argv[],
+                bool erase = false,
+                OCIEnv* environment = 0,
+                std::auto_ptr<connection_factory> =
+                  std::auto_ptr<connection_factory> (0));
+
+      static void
+      print_usage (std::ostream&);
+
+    public:
+      virtual transaction_impl*
+      begin ();
+
+    public:
+      connection_ptr
+      connection ();
+
+    public:
+      const std::string&
+      user () const
+      {
+        return user_;
+      }
+
+      const std::string&
+      password () const
+      {
+        return password_;
+      }
+
+      const std::string&
+      db () const
+      {
+        return db_;
+      }
+
+      const std::string&
+      service () const
+      {
+        return service_;
+      }
+
+      const std::string&
+      host () const
+      {
+        return host_;
+      }
+
+      unsigned int
+      port () const
+      {
+        return port_;
+      }
+
+      OCIEnv*
+      environment ()
+      {
+        return environment_;
+      }
+
+    public:
+      virtual
+      ~database ();
+
+    protected:
+      virtual odb::connection*
+      connection_ ();
+
+    private:
+      std::string user_;
+      std::string password_;
+
+      std::string db_;
+
+      std::string service_;
+      std::string host_;
+      unsigned int port_;
+
+      auto_handle<OCIEnv> auto_environment_;
+      OCIEnv* environment_;
+
+      std::auto_ptr<connection_factory> factory_;
+    };
+  }
+}
+
+#include <odb/oracle/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_DATABASE_HXX
diff --git a/odb/oracle/database.ixx b/odb/oracle/database.ixx
new file mode 100644
index 0000000..763ff90
--- /dev/null
+++ b/odb/oracle/database.ixx
@@ -0,0 +1,20 @@
+// file      : odb/oracle/database.ixx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+  namespace oracle
+  {
+    inline connection_ptr database::
+    connection ()
+    {
+      // Go through the virtual connection_() function instead of
+      // directly to allow overriding.
+      //
+      return connection_ptr (
+        static_cast<oracle::connection*> (connection_ ()));
+    }
+  }
+}
diff --git a/odb/oracle/forward.hxx b/odb/oracle/forward.hxx
new file mode 100644
index 0000000..98fc752
--- /dev/null
+++ b/odb/oracle/forward.hxx
@@ -0,0 +1,48 @@
+// file      : odb/oracle/forward.hxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_FORWARD_HXX
+#define ODB_ORACLE_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    class database;
+    class connection;
+    typedef details::shared_ptr<connection> connection_ptr;
+    class connection_factory;
+    class transaction;
+    class query;
+
+    // Implementation details.
+    //
+    class binding;
+    class select_statement;
+
+    template <typename T>
+    class object_statements;
+
+    template <typename T>
+    class container_statements;
+  }
+
+  namespace details
+  {
+    template <>
+    struct counter_type<oracle::connection>
+    {
+      typedef shared_base counter;
+    };
+  }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_FORWARD_HXX
diff --git a/odb/oracle/transaction-impl.cxx b/odb/oracle/transaction-impl.cxx
new file mode 100644
index 0000000..708747c
--- /dev/null
+++ b/odb/oracle/transaction-impl.cxx
@@ -0,0 +1,126 @@
+// file      : odb/oracle/transaction-impl.cxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <oci.h>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/error.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/transaction-impl.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    transaction_impl::
+    transaction_impl (database_type& db)
+        : odb::transaction_impl (db)
+    {
+    }
+
+    transaction_impl::
+    transaction_impl (connection_ptr c)
+        : odb::transaction_impl (c->database (), *c), connection_ (c)
+    {
+    }
+
+    transaction_impl::
+    ~transaction_impl ()
+    {
+    }
+
+    void transaction_impl::
+    start ()
+    {
+      database_type& db (static_cast<database_type&> (database_));
+
+      if (connection_ == 0)
+      {
+        connection_ = db.connection ();
+        odb::transaction_impl::connection_ = connection_.get ();
+      }
+
+      OCISvcCtx* svc (connnection_->handle ());
+      OCIError* err (connection_->error_handle ());
+
+      // Allocate a transaction handle if there is none associated with
+      // the connection.
+      //
+      OCITrans* t (0);
+      sword s (OCIAttrGet (svc,
+                           OCI_HTYPE_SVCCTX,
+                           reinterpret_cast<void*> (&t),
+                           0,
+                           OCI_ATTR_TRANS,
+                           err));
+
+      if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+        translate_error (err, s);
+      else if (t == 0)
+      {
+        auto_handle<OCITrans> auto_t;
+
+        s = OCIHandleAlloc (db.environment (),
+                            reinterpret_cast<void**> (&t),
+                            OCI_HTYPE_TRANS,
+                            0,
+                            0);
+
+        if (s != OCI_SUCCESS)
+          throw invalid_oci_handle ();
+
+        auto_t.reset (t);
+
+        s = OCIAttrSet (svc,
+                        OCI_HTYPE_SVCCTX,
+                        reinterpret_cast<void*> (t),
+                        0,
+                        OCI_ATTR_TRANS,
+                        err);
+
+        if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+          translate_error (err, s);
+
+        auto_t.release ();
+      }
+
+      // We never use OCITransDetach so the timeout parameter is
+      // of no consequence.
+      //
+      s = OCITransStart (svc,
+                         err,
+                         0,
+                         OCI_TRANS_NEW);
+
+      if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+        translate_error (err, s);
+    }
+
+    void transaction_impl::
+    commit ()
+    {
+      sword s (OCITransCommit (connection_->handle (),
+                               connection_->error_handle (),
+                               OCI_DEFAULT));
+
+      if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+        translate_error (err_, s);
+    }
+
+    void transaction_impl::
+    rollback ()
+    {
+      sword s (OCITransRollback (connection_->handle (),
+                                 connection_->error_handle (),
+                                 OCI_DEFAULT));
+
+      if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+        translate_error (err_, s);
+    }
+  }
+}
diff --git a/odb/oracle/transaction-impl.hxx b/odb/oracle/transaction-impl.hxx
new file mode 100644
index 0000000..9338008
--- /dev/null
+++ b/odb/oracle/transaction-impl.hxx
@@ -0,0 +1,59 @@
+// file      : odb/oracle/transaction-impl.hxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRANSACTION_IMPL_HXX
+#define ODB_ORACLE_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    class LIBODB_ORACLE_EXPORT transaction_impl: public odb::transaction_impl
+    {
+    public:
+      typedef oracle::database database_type;
+      typedef oracle::connection connection_type;
+
+      transaction_impl (database_type&);
+      transaction_impl (connection_ptr);
+
+      virtual
+      ~transaction_impl ();
+
+      virtual void
+      start ();
+
+      virtual void
+      commit ();
+
+      virtual void
+      rollback ();
+
+      connection_type&
+      connection ();
+
+    private:
+      connection_ptr connection_;
+    };
+  }
+}
+
+#include <odb/oracle/transaction-impl.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRANSACTION_IMPL_HXX
diff --git a/odb/oracle/transaction-impl.ixx b/odb/oracle/transaction-impl.ixx
new file mode 100644
index 0000000..d6f668f
--- /dev/null
+++ b/odb/oracle/transaction-impl.ixx
@@ -0,0 +1,16 @@
+// file      : odb/oracle/transaction-impl.ixx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+  namespace oracle
+  {
+    inline transaction_impl::connection_type& transaction_impl::
+    connection ()
+    {
+      return *connection_;
+    }
+  }
+}
diff --git a/odb/oracle/transaction.cxx b/odb/oracle/transaction.cxx
new file mode 100644
index 0000000..412328b
--- /dev/null
+++ b/odb/oracle/transaction.cxx
@@ -0,0 +1,26 @@
+// file      : odb/oracle/transaction.cxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/transaction.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    transaction& transaction::
+    current ()
+    {
+      // While the impl type can be of the concrete type, the transaction
+      // object can be created as either odb:: or odb::oracle:: type. To
+      // work around that we are going to hard-cast one to the other
+      // relying on the fact that they have the same representation and
+      // no virtual functions. The former is checked in the tests.
+      //
+      odb::transaction& b (odb::transaction::current ());
+      dynamic_cast<transaction_impl&> (b.implementation ());
+      return reinterpret_cast<transaction&> (b);
+    }
+  }
+}
diff --git a/odb/oracle/transaction.hxx b/odb/oracle/transaction.hxx
new file mode 100644
index 0000000..5d6c460
--- /dev/null
+++ b/odb/oracle/transaction.hxx
@@ -0,0 +1,65 @@
+// file      : odb/oracle/transaction.hxx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRANSACTION_HXX
+#define ODB_ORACLE_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    class transaction_impl;
+
+    class LIBODB_ORACLE_EXPORT transaction: public odb::transaction
+    {
+    public:
+      typedef oracle::database database_type;
+      typedef oracle::connection connection_type;
+
+      explicit
+      transaction (transaction_impl*);
+
+      // Return the database this transaction is on.
+      //
+      database_type&
+      database ();
+
+      // Return the underlying database connection for this transaction.
+      //
+      connection_type&
+      connection ();
+
+      // Return current transaction or throw if there is no transaction
+      // in effect.
+      //
+      static transaction&
+      current ();
+
+      // Set the current thread's transaction.
+      //
+      static void
+      current (transaction&);
+
+    public:
+      transaction_impl&
+      implementation ();
+    };
+  }
+}
+
+#include <odb/oracle/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRANSACTION_HXX
diff --git a/odb/oracle/transaction.ixx b/odb/oracle/transaction.ixx
new file mode 100644
index 0000000..acfa613
--- /dev/null
+++ b/odb/oracle/transaction.ixx
@@ -0,0 +1,47 @@
+// file      : odb/oracle/transaction.ixx
+// author    : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license   : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction-impl.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    inline transaction::
+    transaction (transaction_impl* impl)
+        : odb::transaction (impl)
+    {
+    }
+
+    inline transaction_impl& transaction::
+    implementation ()
+    {
+      // We can use static_cast here since we have an instance of
+      // oracle::transaction.
+      //
+      return static_cast<transaction_impl&> (
+        odb::transaction::implementation ());
+    }
+
+    inline transaction::database_type& transaction::
+    database ()
+    {
+      return static_cast<database_type&> (odb::transaction::database ());
+    }
+
+    inline transaction::connection_type& transaction::
+    connection ()
+    {
+      return implementation ().connection ();
+    }
+
+    inline void transaction::
+    current (transaction& t)
+    {
+      odb::transaction::current (t);
+    }
+  }
+}
-- 
cgit v1.1