summaryrefslogtreecommitdiff
path: root/odb/oracle/connection.cxx
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/connection.cxx
parentd6518580059c6a0d34d7a1683fabc3bfcc4b5e27 (diff)
Add database, connection, connection-factory, and transaction support
Diffstat (limited to 'odb/oracle/connection.cxx')
-rw-r--r--odb/oracle/connection.cxx300
1 files changed, 300 insertions, 0 deletions
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;
+ }
+ }
+}