aboutsummaryrefslogtreecommitdiff
path: root/odb/oracle
diff options
context:
space:
mode:
authorConstantin Michael <constantin@codesynthesis.com>2011-09-05 12:10:45 +0200
committerConstantin Michael <constantin@codesynthesis.com>2011-09-05 16:08:45 +0200
commit63295f6e051e75cf07cb6212a2631df8eb8a90c4 (patch)
tree79aa54d3cdfa2b29c7f1a52829d12495250e6d9f /odb/oracle
parentd6518580059c6a0d34d7a1683fabc3bfcc4b5e27 (diff)
Add database, connection, connection-factory, and transaction support
Diffstat (limited to 'odb/oracle')
-rw-r--r--odb/oracle/connection-factory.cxx173
-rw-r--r--odb/oracle/connection-factory.hxx164
-rw-r--r--odb/oracle/connection.cxx300
-rw-r--r--odb/oracle/connection.hxx105
-rw-r--r--odb/oracle/database.cxx230
-rw-r--r--odb/oracle/database.hxx159
-rw-r--r--odb/oracle/database.ixx20
-rw-r--r--odb/oracle/forward.hxx48
-rw-r--r--odb/oracle/transaction-impl.cxx126
-rw-r--r--odb/oracle/transaction-impl.hxx59
-rw-r--r--odb/oracle/transaction-impl.ixx16
-rw-r--r--odb/oracle/transaction.cxx26
-rw-r--r--odb/oracle/transaction.hxx65
-rw-r--r--odb/oracle/transaction.ixx47
14 files changed, 1538 insertions, 0 deletions
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);
+ }
+ }
+}