diff options
author | Constantin Michael <constantin@codesynthesis.com> | 2011-09-05 12:10:45 +0200 |
---|---|---|
committer | Constantin Michael <constantin@codesynthesis.com> | 2011-09-05 16:08:45 +0200 |
commit | 63295f6e051e75cf07cb6212a2631df8eb8a90c4 (patch) | |
tree | 79aa54d3cdfa2b29c7f1a52829d12495250e6d9f | |
parent | d6518580059c6a0d34d7a1683fabc3bfcc4b5e27 (diff) |
Add database, connection, connection-factory, and transaction support
-rw-r--r-- | odb/oracle/connection-factory.cxx | 173 | ||||
-rw-r--r-- | odb/oracle/connection-factory.hxx | 164 | ||||
-rw-r--r-- | odb/oracle/connection.cxx | 300 | ||||
-rw-r--r-- | odb/oracle/connection.hxx | 105 | ||||
-rw-r--r-- | odb/oracle/database.cxx | 230 | ||||
-rw-r--r-- | odb/oracle/database.hxx | 159 | ||||
-rw-r--r-- | odb/oracle/database.ixx | 20 | ||||
-rw-r--r-- | odb/oracle/forward.hxx | 48 | ||||
-rw-r--r-- | odb/oracle/transaction-impl.cxx | 126 | ||||
-rw-r--r-- | odb/oracle/transaction-impl.hxx | 59 | ||||
-rw-r--r-- | odb/oracle/transaction-impl.ixx | 16 | ||||
-rw-r--r-- | odb/oracle/transaction.cxx | 26 | ||||
-rw-r--r-- | odb/oracle/transaction.hxx | 65 | ||||
-rw-r--r-- | odb/oracle/transaction.ixx | 47 |
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); + } + } +} |