From a8a2cd61bb9f3657c65acf3a1d9dfaaa68271f4e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 20 Nov 2011 14:57:35 +0200 Subject: Implement support for transactions --- odb/mssql/connection.cxx | 22 +++++++---- odb/mssql/error.cxx | 30 +++++++++++--- odb/mssql/error.hxx | 2 +- odb/mssql/makefile | 1 + odb/mssql/transaction-impl.cxx | 38 +++++++++++------- odb/mssql/transaction.cxx | 26 ++++++++++++ odb/mssql/transaction.hxx | 89 ++++++++++++++++++++++++++++++++++++++++++ odb/mssql/transaction.ixx | 47 ++++++++++++++++++++++ 8 files changed, 226 insertions(+), 29 deletions(-) create mode 100644 odb/mssql/transaction.cxx create mode 100644 odb/mssql/transaction.hxx create mode 100644 odb/mssql/transaction.ixx diff --git a/odb/mssql/connection.cxx b/odb/mssql/connection.cxx index 788fecb..0d09461 100644 --- a/odb/mssql/connection.cxx +++ b/odb/mssql/connection.cxx @@ -10,7 +10,7 @@ #include #include #include -//#include +#include //#include //#include #include @@ -42,6 +42,19 @@ namespace odb handle_.reset (h); } + // Set the manual commit mode. + // + r = SQLSetConnectAttr (handle_, + SQL_ATTR_AUTOCOMMIT, + SQL_AUTOCOMMIT_OFF, + SQL_IS_UINTEGER); + + if (!SQL_SUCCEEDED (r)) + // Still use the handle version of translate_error since there + // is no connection yet. + // + translate_error (r, handle_, SQL_HANDLE_DBC); + // Connect. // { @@ -56,9 +69,6 @@ namespace odb SQL_DRIVER_NOPROMPT); if (!SQL_SUCCEEDED (r)) - // Still use the handle version of translate_error since there - // is no connection. - // translate_error (r, handle_, SQL_HANDLE_DBC); state_ = state_connected; @@ -93,9 +103,7 @@ namespace odb transaction_impl* connection:: begin () { - //@@ - //return new transaction_impl (connection_ptr (inc_ref (this))); - return 0; + return new transaction_impl (connection_ptr (inc_ref (this))); } unsigned long long connection:: diff --git a/odb/mssql/error.cxx b/odb/mssql/error.cxx index b659bd6..dffe4cf 100644 --- a/odb/mssql/error.cxx +++ b/odb/mssql/error.cxx @@ -20,7 +20,8 @@ namespace odb translate_error (SQLRETURN r, SQLHANDLE h, SQLSMALLINT htype, - connection* conn) + connection* conn, + bool end_tran) { // First see if we have one of the errors indicated via the // return error code. @@ -120,6 +121,18 @@ namespace odb break; } + // If a call to SQLEndTran() fails, then the connection is + // put into the so called "suspended state" and should be + // disconnected unless we know the transaction was rolled + // back. See SQLEndTran() documentation for details. + // + if (end_tran && + s != "25S03" && // Transaction is rolled back. + s != "40001" && // Serialization failure. + s != "40002" && // Integrity constraint. + s != "HYC00") // Optional feature not implemented. + conn->mark_failed (); + if (c != code_none && c != nc) { // Several different codes. @@ -173,7 +186,12 @@ namespace odb string s (sqlstate); if (s == "08S01" || // Link failure. - s == "HYT01") // Connection timeout. + s == "HYT01" || // Connection timeout. + (end_tran && + s != "25S03" && + s != "40001" && + s != "40002" && + s != "HYC00")) conn->mark_failed (); } @@ -191,9 +209,9 @@ namespace odb } void - translate_error (SQLRETURN r, connection& c) + translate_error (SQLRETURN r, connection& c, bool end_tran) { - translate_error (r, c.handle (), SQL_HANDLE_DBC, &c); + translate_error (r, c.handle (), SQL_HANDLE_DBC, &c, end_tran); } void @@ -201,13 +219,13 @@ namespace odb connection& c, const auto_handle& h) { - translate_error (r, h, SQL_HANDLE_STMT, &c); + translate_error (r, h, SQL_HANDLE_STMT, &c, false); } void translate_error (SQLRETURN r, SQLHANDLE h, SQLSMALLINT htype) { - translate_error (r, h, htype, 0); + translate_error (r, h, htype, 0, false); } } } diff --git a/odb/mssql/error.hxx b/odb/mssql/error.hxx index f160514..126f7d9 100644 --- a/odb/mssql/error.hxx +++ b/odb/mssql/error.hxx @@ -20,7 +20,7 @@ namespace odb namespace mssql { LIBODB_MSSQL_EXPORT void - translate_error (SQLRETURN, connection&); + translate_error (SQLRETURN, connection&, bool end_tran = false); LIBODB_MSSQL_EXPORT void translate_error (SQLRETURN, diff --git a/odb/mssql/makefile b/odb/mssql/makefile index 10e9e28..6a2ff51 100644 --- a/odb/mssql/makefile +++ b/odb/mssql/makefile @@ -13,6 +13,7 @@ connection-factory.cxx \ database.cxx \ error.cxx \ exceptions.cxx \ +transaction.cxx \ transaction-impl.cxx diff --git a/odb/mssql/transaction-impl.cxx b/odb/mssql/transaction-impl.cxx index 4d0d77f..308f078 100644 --- a/odb/mssql/transaction-impl.cxx +++ b/odb/mssql/transaction-impl.cxx @@ -5,6 +5,7 @@ #include +#include #include #include #include @@ -34,7 +35,6 @@ namespace odb void transaction_impl:: start () { - /* // Grab a connection if we don't already have one. // if (connection_ == 0) @@ -43,57 +43,65 @@ namespace odb odb::transaction_impl::connection_ = connection_.get (); } + /* + @@ TODO { odb::tracer* t; if ((t = connection_->tracer ()) || (t = database_.tracer ())) t->execute (*connection_, "BEGIN"); } - - if (mssql_real_query (connection_->handle (), "begin", 5) != 0) - translate_error (*connection_); */ + + // In ODBC a transaction is started automatically before the first + // statement is executed. + // } void transaction_impl:: commit () { /* - connection_->clear (); - + @@ TODO { odb::tracer* t; if ((t = connection_->tracer ()) || (t = database_.tracer ())) t->execute (*connection_, "COMMIT"); } + */ - if (mssql_real_query (connection_->handle (), "commit", 6) != 0) - translate_error (*connection_); + SQLRETURN r ( + SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_COMMIT)); - // Release the connection. + if (!SQL_SUCCEEDED (r)) + translate_error (r, *connection_, true); + + // We cannot release the connection early since we may still need + // to free (query) statements. // //connection_.reset (); - */ } void transaction_impl:: rollback () { /* - connection_->clear (); - + @@ TODO { odb::tracer* t; if ((t = connection_->tracer ()) || (t = database_.tracer ())) t->execute (*connection_, "ROLLBACK"); } + */ + + SQLRETURN r ( + SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_ROLLBACK)); - if (mssql_real_query (connection_->handle (), "rollback", 8) != 0) - translate_error (*connection_); + if (!SQL_SUCCEEDED (r)) + translate_error (r, *connection_, true); // Release the connection. // //connection_.reset (); - */ } } } diff --git a/odb/mssql/transaction.cxx b/odb/mssql/transaction.cxx new file mode 100644 index 0000000..d049d2e --- /dev/null +++ b/odb/mssql/transaction.cxx @@ -0,0 +1,26 @@ +// file : odb/mssql/transaction.cxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +#include + +namespace odb +{ + namespace mssql + { + transaction& transaction:: + current () + { + // While the impl type can be of the concrete type, the transaction + // object can be created as either odb:: or odb::mssql:: 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 (b.implementation ()); + return reinterpret_cast (b); + } + } +} diff --git a/odb/mssql/transaction.hxx b/odb/mssql/transaction.hxx new file mode 100644 index 0000000..57806b8 --- /dev/null +++ b/odb/mssql/transaction.hxx @@ -0,0 +1,89 @@ +// file : odb/mssql/transaction.hxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +//@@ disabled functionality + +#ifndef ODB_MSSQL_TRANSACTION_HXX +#define ODB_MSSQL_TRANSACTION_HXX + +#include + +#include + +#include +#include +//#include + +#include + +namespace odb +{ + namespace mssql + { + class transaction_impl; + + class LIBODB_MSSQL_EXPORT transaction: public odb::transaction + { + public: + typedef mssql::database database_type; + typedef mssql::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&); + + /* + // SQL statement tracing. + // + public: + typedef mssql::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 (); + }; + } +} + +#include + +#include + +#endif // ODB_MSSQL_TRANSACTION_HXX diff --git a/odb/mssql/transaction.ixx b/odb/mssql/transaction.ixx new file mode 100644 index 0000000..6e4daae --- /dev/null +++ b/odb/mssql/transaction.ixx @@ -0,0 +1,47 @@ +// file : odb/mssql/transaction.ixx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +#include +#include + +namespace odb +{ + namespace mssql + { + 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 + // mssql::transaction. + // + return static_cast ( + odb::transaction::implementation ()); + } + + inline transaction::database_type& transaction:: + database () + { + return static_cast (odb::transaction::database ()); + } + + inline transaction::connection_type& transaction:: + connection () + { + return implementation ().connection (); + } + + inline void transaction:: + current (transaction& t) + { + odb::transaction::current (t); + } + } +} -- cgit v1.1