summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-11-07 15:00:07 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-11-08 16:03:17 +0200
commit1d664d31bdfc341ca642948efdf395e7922141f6 (patch)
tree73bb369f5072aee7daf621d0cc009731178bf581
parent487bd012de7efc7237581b4a7aeac7fd817138fb (diff)
Add support for SQL statement tracing
-rw-r--r--odb/oracle/connection.cxx152
-rw-r--r--odb/oracle/connection.hxx20
-rw-r--r--odb/oracle/database.hxx20
-rw-r--r--odb/oracle/forward.hxx2
-rw-r--r--odb/oracle/makefile1
-rw-r--r--odb/oracle/statement.cxx233
-rw-r--r--odb/oracle/statement.hxx34
-rw-r--r--odb/oracle/tracer.cxx62
-rw-r--r--odb/oracle/tracer.hxx62
-rw-r--r--odb/oracle/transaction-impl.cxx20
-rw-r--r--odb/oracle/transaction.hxx20
11 files changed, 472 insertions, 154 deletions
diff --git a/odb/oracle/connection.cxx b/odb/oracle/connection.cxx
index dc37a07..c0eae59 100644
--- a/odb/oracle/connection.cxx
+++ b/odb/oracle/connection.cxx
@@ -4,12 +4,14 @@
// license : ODB NCUEL; see accompanying LICENSE file
#include <string>
+#include <sstream>
#include <oci.h>
#include <odb/oracle/database.hxx>
#include <odb/oracle/connection.hxx>
#include <odb/oracle/transaction.hxx>
+#include <odb/oracle/statement.hxx>
#include <odb/oracle/error.hxx>
#include <odb/oracle/exceptions.hxx>
#include <odb/oracle/auto-descriptor.hxx>
@@ -149,154 +151,8 @@ namespace odb
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 OraText*> (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;
+ generic_statement st (*this, string (s, n));
+ return st.execute ();
}
}
}
diff --git a/odb/oracle/connection.hxx b/odb/oracle/connection.hxx
index 12add74..56ac6b9 100644
--- a/odb/oracle/connection.hxx
+++ b/odb/oracle/connection.hxx
@@ -19,6 +19,7 @@
#include <odb/oracle/version.hxx>
#include <odb/oracle/forward.hxx>
+#include <odb/oracle/tracer.hxx>
#include <odb/oracle/transaction-impl.hxx>
#include <odb/oracle/auto-handle.hxx>
#include <odb/oracle/oracle-fwd.hxx>
@@ -63,6 +64,25 @@ namespace odb
virtual unsigned long long
execute (const char* statement, std::size_t length);
+ // SQL statement tracing.
+ //
+ public:
+ typedef oracle::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
public:
OCISvcCtx*
handle ()
diff --git a/odb/oracle/database.hxx b/odb/oracle/database.hxx
index 80b6fba..232b320 100644
--- a/odb/oracle/database.hxx
+++ b/odb/oracle/database.hxx
@@ -18,6 +18,7 @@
#include <odb/oracle/version.hxx>
#include <odb/oracle/forward.hxx>
+#include <odb/oracle/tracer.hxx>
#include <odb/oracle/connection.hxx>
#include <odb/oracle/connection-factory.hxx>
#include <odb/oracle/auto-handle.hxx>
@@ -145,6 +146,25 @@ namespace odb
return environment_;
}
+ // SQL statement tracing.
+ //
+ public:
+ typedef oracle::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
public:
virtual
~database ();
diff --git a/odb/oracle/forward.hxx b/odb/oracle/forward.hxx
index ae45ed4..baad4af 100644
--- a/odb/oracle/forward.hxx
+++ b/odb/oracle/forward.hxx
@@ -18,7 +18,9 @@ namespace odb
class connection;
typedef details::shared_ptr<connection> connection_ptr;
class connection_factory;
+ class statement;
class transaction;
+ class tracer;
class query;
// Implementation details.
diff --git a/odb/oracle/makefile b/odb/oracle/makefile
index f173b95..c2fb892 100644
--- a/odb/oracle/makefile
+++ b/odb/oracle/makefile
@@ -19,6 +19,7 @@ query.cxx \
query-const-expr.cxx \
statement.cxx \
statements-base.cxx \
+tracer.cxx \
traits.cxx \
transaction.cxx \
transaction-impl.cxx \
diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx
index cb204d5..8b608a4 100644
--- a/odb/oracle/statement.cxx
+++ b/odb/oracle/statement.cxx
@@ -3,11 +3,12 @@
// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
// license : ODB NCUEL; see accompanying LICENSE file
-#include <cassert>
#include <limits>
+#include <cassert>
#include <oci.h>
+#include <odb/tracer.hxx>
#include <odb/exceptions.hxx> // object_not_persistent
#include <odb/details/unused.hxx>
@@ -134,6 +135,18 @@ namespace odb
//
statement::
+ ~statement ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+ }
+
+ statement::
statement (connection& conn, const string& s)
: conn_ (conn)
{
@@ -154,6 +167,33 @@ namespace odb
translate_error (err, r);
stmt_.reset (handle, OCI_STRLS_CACHE_DELETE, err);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+ }
+
+ const char* statement::
+ text () const
+ {
+ OCIError* err (conn_.error_handle ());
+
+ OraText* s (0);
+ sword r (OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &s,
+ 0,
+ OCI_ATTR_STATEMENT,
+ err));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ return reinterpret_cast<char*> (s);
}
void statement::
@@ -760,11 +800,166 @@ namespace odb
}
}
- statement::
- ~statement ()
+ //
+ // generic_statement
+ //
+
+ generic_statement::
+ generic_statement (connection& conn, const string& s)
+ : statement (conn, s), bound_ (false)
+ {
+ OCIError* err (conn_.error_handle ());
+
+ sword r (OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &stmt_type_,
+ 0,
+ OCI_ATTR_STMT_TYPE,
+ err));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+
+ generic_statement::
+ ~generic_statement ()
{
}
+ unsigned long long generic_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ sword r (0);
+
+ OCISvcCtx* handle (conn_.handle ());
+ OCIError* err (conn_.error_handle ());
+
+ if (stmt_type_ == OCI_STMT_SELECT)
+ {
+ // Do not prefetch any rows.
+ //
+ r = OCIStmtExecute (handle, stmt_, err, 0, 0, 0, 0, OCI_DEFAULT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ // In order to successfully 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, indicator variable
+ // pointers, and data length pointers are specified as NULL (we still
+ // specify a valid data type identifier; not doing so results in
+ // undefined behavior). This results in truncation errors being
+ // returned for all attempted row fetches. However, cursor behaves
+ // normally allowing us to return the row count for a select
+ // statement. Note also that we only need to do this once.
+ //
+ if (!bound_)
+ {
+ for (ub4 i (1); ; ++i)
+ {
+ auto_descriptor<OCIParam> param;
+ {
+ OCIParam* p (0);
+ r = OCIParamGet (stmt_,
+ OCI_HTYPE_STMT,
+ err,
+ reinterpret_cast<void**> (&p),
+ i);
+
+ if (r == OCI_ERROR) // No more result columns.
+ break;
+
+ param.reset (p);
+ }
+
+ ub2 data_type;
+ r = OCIAttrGet (param,
+ OCI_DTYPE_PARAM,
+ &data_type,
+ 0,
+ OCI_ATTR_DATA_TYPE,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ // No need to keep track of the OCIDefine handles - these will
+ // be deallocated with the statement.
+ //
+ OCIDefine* define (0);
+ r = OCIDefineByPos (stmt_,
+ &define,
+ err,
+ 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 (err, r);
+ }
+
+ bound_ = true;
+ }
+
+ for (;;)
+ {
+ r = OCIStmtFetch2 (stmt_, err, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
+
+ if (r == OCI_NO_DATA)
+ break;
+ else if (r == OCI_ERROR)
+ {
+ sb4 e;
+ r = OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR);
+
+ // ORA-01406 is returned if there is a truncation error. We expect
+ // and ignore this error.
+ //
+ if (e != 1406)
+ translate_error (err, r);
+ }
+ else if (r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+ }
+ else
+ {
+ // OCIStmtExecute requires a non-zero iters param for DML statements.
+ //
+ r = OCIStmtExecute (handle, stmt_, err, 1, 0, 0, 0, OCI_DEFAULT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+
+ ub4 row_count (0);
+ r = OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &row_count,
+ 0,
+ OCI_ATTR_ROW_COUNT,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ return row_count;
+ }
+
//
// select_statement
//
@@ -812,6 +1007,14 @@ namespace odb
if (!done_)
free_result ();
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
OCIError* err (conn_.error_handle ());
// @@ Retrieve a single row into the already bound output buffers as an
@@ -1012,6 +1215,14 @@ namespace odb
bool insert_statement::
execute ()
{
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
OCIError* err (conn_.error_handle ());
sword r (OCIStmtExecute (conn_.handle (),
@@ -1087,6 +1298,14 @@ namespace odb
unsigned long long update_statement::
execute ()
{
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
OCIError* err (conn_.error_handle ());
sword r (OCIStmtExecute (conn_.handle (),
@@ -1140,6 +1359,14 @@ namespace odb
unsigned long long delete_statement::
execute ()
{
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
OCIError* err (conn_.error_handle ());
sword r (OCIStmtExecute (conn_.handle (),
diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx
index e0d30d9..f3e86f0 100644
--- a/odb/oracle/statement.hxx
+++ b/odb/oracle/statement.hxx
@@ -12,8 +12,7 @@
#include <cstddef> // std::size_t
#include <odb/forward.hxx>
-
-#include <odb/details/shared-ptr.hxx>
+#include <odb/statement.hxx>
#include <odb/oracle/version.hxx>
#include <odb/oracle/binding.hxx>
@@ -27,12 +26,21 @@ namespace odb
{
namespace oracle
{
- class LIBODB_ORACLE_EXPORT statement: public details::shared_base
+ class LIBODB_ORACLE_EXPORT statement: public odb::statement
{
public:
virtual
~statement () = 0;
+ OCIStmt*
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
protected:
statement (connection&, const std::string& statement);
@@ -73,6 +81,26 @@ namespace odb
auto_handle<OCIStmt> stmt_;
};
+ class LIBODB_ORACLE_EXPORT generic_statement: public statement
+ {
+ public:
+ virtual
+ ~generic_statement ();
+
+ generic_statement (connection&, const std::string& statement);
+
+ unsigned long long
+ execute ();
+
+ private:
+ generic_statement (const generic_statement&);
+ generic_statement& operator= (const generic_statement&);
+
+ private:
+ ub2 stmt_type_;
+ bool bound_;
+ };
+
class LIBODB_ORACLE_EXPORT select_statement: public statement
{
public:
diff --git a/odb/oracle/tracer.cxx b/odb/oracle/tracer.cxx
new file mode 100644
index 0000000..98a8c1e
--- /dev/null
+++ b/odb/oracle/tracer.cxx
@@ -0,0 +1,62 @@
+// file : odb/oracle/tracer.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/tracer.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/statement.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/odb/oracle/tracer.hxx b/odb/oracle/tracer.hxx
new file mode 100644
index 0000000..149c627
--- /dev/null
+++ b/odb/oracle/tracer.hxx
@@ -0,0 +1,62 @@
+// file : odb/oracle/tracer.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRACER_HXX
+#define ODB_ORACLE_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ private:
+ // Allow these classes to convert oracle::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRACER_HXX
diff --git a/odb/oracle/transaction-impl.cxx b/odb/oracle/transaction-impl.cxx
index 4751366..e46aac1 100644
--- a/odb/oracle/transaction-impl.cxx
+++ b/odb/oracle/transaction-impl.cxx
@@ -7,6 +7,8 @@
#include <oci.h>
+#include <odb/tracer.hxx>
+
#include <odb/oracle/database.hxx>
#include <odb/oracle/connection.hxx>
#include <odb/oracle/error.hxx>
@@ -89,6 +91,12 @@ namespace odb
auto_t.release ();
}
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
// We never use OCITransDetach so the timeout parameter is
// of no consequence.
//
@@ -104,6 +112,12 @@ namespace odb
void transaction_impl::
commit ()
{
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
sword s (OCITransCommit (connection_->handle (),
connection_->error_handle (),
OCI_DEFAULT));
@@ -115,6 +129,12 @@ namespace odb
void transaction_impl::
rollback ()
{
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
sword s (OCITransRollback (connection_->handle (),
connection_->error_handle (),
OCI_DEFAULT));
diff --git a/odb/oracle/transaction.hxx b/odb/oracle/transaction.hxx
index 5d6c460..7b2999c 100644
--- a/odb/oracle/transaction.hxx
+++ b/odb/oracle/transaction.hxx
@@ -12,6 +12,7 @@
#include <odb/oracle/version.hxx>
#include <odb/oracle/forward.hxx>
+#include <odb/oracle/tracer.hxx>
#include <odb/oracle/details/export.hxx>
@@ -51,6 +52,25 @@ namespace odb
static void
current (transaction&);
+ // SQL statement tracing.
+ //
+ public:
+ typedef oracle::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
public:
transaction_impl&
implementation ();