From c352bdb8b4ee7a636f71a71a301c181942af2d39 Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Tue, 29 Nov 2011 14:45:51 +0200 Subject: Improve the Oracle translate_error implementation The improved implementation scans all the records associated with an error handle. Furthermore, if it is established that the connection to the database has been lost, the connection is marked as such. Additionally, all special exceptions (deadlock, timeout, and connection_lost) are now supported. --- odb/oracle/connection-factory.cxx | 7 +- odb/oracle/connection.cxx | 2 + odb/oracle/connection.hxx | 14 +++ odb/oracle/error.cxx | 173 +++++++++++++++++++++++++++++++------- odb/oracle/error.hxx | 13 ++- odb/oracle/exceptions.cxx | 34 +++++++- odb/oracle/exceptions.hxx | 59 ++++++++++--- odb/oracle/statement.cxx | 22 ++--- odb/oracle/transaction-impl.cxx | 6 +- 9 files changed, 261 insertions(+), 69 deletions(-) diff --git a/odb/oracle/connection-factory.cxx b/odb/oracle/connection-factory.cxx index ed59c35..30aa63f 100644 --- a/odb/oracle/connection-factory.cxx +++ b/odb/oracle/connection-factory.cxx @@ -126,9 +126,10 @@ namespace odb // Determine if we need to keep or free this connection. // - bool keep (waiters_ != 0 || - min_ == 0 || - (connections_.size () + in_use_ <= min_)); + bool keep (!c->failed () && + (waiters_ != 0 || + min_ == 0 || + (connections_.size () + in_use_ <= min_))); in_use_--; diff --git a/odb/oracle/connection.cxx b/odb/oracle/connection.cxx index 8a82e40..adf89b3 100644 --- a/odb/oracle/connection.cxx +++ b/odb/oracle/connection.cxx @@ -26,6 +26,7 @@ namespace odb connection (database_type& db) : odb::connection (db), db_ (db), + failed_ (false), statement_cache_ (new statement_cache_type (*this)), lob_buffer_ (0) { @@ -111,6 +112,7 @@ namespace odb connection (database_type& db, OCISvcCtx* handle) : odb::connection (db), db_ (db), + failed_ (false), statement_cache_ (new statement_cache_type (*this)), lob_buffer_ (0) { diff --git a/odb/oracle/connection.hxx b/odb/oracle/connection.hxx index b745b95..45444c1 100644 --- a/odb/oracle/connection.hxx +++ b/odb/oracle/connection.hxx @@ -82,6 +82,19 @@ namespace odb using odb::connection::tracer; public: + bool + failed () const + { + return failed_; + } + + void + mark_failed () + { + failed_ = true; + } + + public: OCISvcCtx* handle () { @@ -119,6 +132,7 @@ namespace odb auto_handle error_; auto_handle handle_; + bool failed_; std::auto_ptr statement_cache_; diff --git a/odb/oracle/error.cxx b/odb/oracle/error.cxx index f00ca19..fe782f4 100644 --- a/odb/oracle/error.cxx +++ b/odb/oracle/error.cxx @@ -9,6 +9,7 @@ #include #include +#include using namespace std; @@ -17,47 +18,159 @@ namespace odb namespace oracle { void - translate_error (void* h, ub4 t, sword s) + translate_error (void* h, ub4 t, sword s, connection* conn) { - assert (s == OCI_ERROR || s == OCI_INVALID_HANDLE); + assert (s != OCI_SUCCESS && s != OCI_SUCCESS_WITH_INFO); - if (s == OCI_INVALID_HANDLE) - throw invalid_oci_handle (); + if (s != OCI_ERROR) + { + switch (s) + { + case OCI_STILL_EXECUTING: + { + throw database_exception (0, "statement still executing"); + break; + } + case OCI_NEED_DATA: + case OCI_NO_DATA: + { + throw database_exception (0, "unhandled OCI_*_DATA condition"); + break; + } + case OCI_INVALID_HANDLE: + { + throw invalid_oci_handle (); + break; + } + } + } + sword r (0); sb4 e; - details::buffer b; - b.capacity (128); + char b[512]; // Error message will be truncated if it does not fit. - bool trunc (true); - while (trunc) + if (t == OCI_HTYPE_ERROR) { - trunc = OCIErrorGet (h, - 1, - 0, - &e, - reinterpret_cast (b.data ()), - b.capacity (), - t) == OCI_ERROR; - - if (trunc) - b.capacity (b.capacity () * 2); + // Mark the connection as failed if necessary. + // + if (conn != 0) + { + OCIServer* server (0); + r = OCIAttrGet (conn->handle (), + OCI_HTYPE_SVCCTX, + &server, + 0, + OCI_ATTR_SERVER, + conn->error_handle ()); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + ub4 server_status (0); + r = OCIAttrGet (server, + OCI_HTYPE_SERVER, + &server_status, + 0, + OCI_ATTR_SERVER_STATUS, + conn->error_handle ()); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + if (server_status == OCI_SERVER_NOT_CONNECTED) + conn->mark_failed (); + } + + // We need to translate certain Oracle error codes to special + // exceptions, such as deadlock, timeout, etc. The problem is we can + // have multiple records potentially with different error codes. If we + // have both, say, a deadlock code and some other code, then we should + // probably throw database_exception, which is more severe. To + // implement this we are going to pre-scan the records looking for the + // codes we are interested in. If in the process we see any other code, + // then we stop and go ahead to prepare and throw database_exception. + // + enum code + { + code_none, + code_deadlock, + code_timeout, + code_connection_lost, + }; + + code c (code_none); + + for (sb4 i (1);; ++i) + { + r = OCIErrorGet (h, i, 0, &e, reinterpret_cast (b), 512, t); + + if (r == OCI_NO_DATA) + break; + + code nc; + + if (e == 60 || // Deadlock detected while waiting for resource. + e == 104) // Deadlock detected; all public servers blocked. + nc = code_deadlock; + else if (e == 51 || // Timeout occurred while waiting for a resource. + e == 54) // Resource busy and acquisition timeout expired. + nc = code_timeout; + else if (e == 28 || // Session has been killed. + e == 3135 || // Connection lost contact. + e == 3136 || // Inbound connection timed out. + e == 3138) // Connection terminated. + nc = code_connection_lost; + else + { + c = code_none; + break; + } + + if (c != code_none && c != nc) + { + // Several different codes. + // + c = code_none; + break; + } + + c = nc; + } + + switch (c) + { + case code_deadlock: + throw deadlock (); + case code_timeout: + throw timeout (); + case code_connection_lost: + throw connection_lost (); + case code_none: + break; + } } - // @@ Need to find a source of OCI specific codes. + // Some other error code. Prepare database_exception. // - // There are no symbolic definitions for error codes in the OCI - // header files. - // - switch (e) + database_exception dbe; + + for (sb4 i (1);; ++i) { - case 60: - throw deadlock (); - case 3135: - case 3136: - throw connection_lost (); - default: - throw database_exception (e, b.data ()); + r = OCIErrorGet (h, + i, + 0, + &e, + reinterpret_cast (b), + 512, + t); + + if (r == OCI_NO_DATA) + break; + + dbe.append (e, b); } + + throw dbe; } } } diff --git a/odb/oracle/error.hxx b/odb/oracle/error.hxx index 43a314d..a5dbb10 100644 --- a/odb/oracle/error.hxx +++ b/odb/oracle/error.hxx @@ -10,6 +10,7 @@ #include +#include #include #include @@ -18,19 +19,15 @@ namespace odb namespace oracle { void - translate_error (void* h, ub4 t, sword s); - - // @@ Check connection state attribute once connection has been - // implemented. - // + translate_error (void* h, ub4 t, sword s, connection*); // Translate OCI error given an error handle and throw an appropriate // exception. // inline LIBODB_ORACLE_EXPORT void - translate_error (OCIError* h, sword s) + translate_error (OCIError* h, sword s, connection* c = 0) { - translate_error (h, OCI_HTYPE_ERROR, s); + translate_error (h, OCI_HTYPE_ERROR, s, c); } // Translate an OCI error given an environment handle error and throw @@ -39,7 +36,7 @@ namespace odb inline LIBODB_ORACLE_EXPORT void translate_error (OCIEnv* h) { - translate_error (h, OCI_HTYPE_ENV, OCI_ERROR); + translate_error (h, OCI_HTYPE_ENV, OCI_ERROR, 0); } } } diff --git a/odb/oracle/exceptions.cxx b/odb/oracle/exceptions.cxx index 903608e..04928f6 100644 --- a/odb/oracle/exceptions.cxx +++ b/odb/oracle/exceptions.cxx @@ -3,6 +3,8 @@ // copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC // license : ODB NCUEL; see accompanying LICENSE file +#include + #include using namespace std; @@ -15,9 +17,9 @@ namespace odb // database_exception // - database_exception:: - database_exception (int error, const string& message) - : error_ (error), message_ (message) + database_exception::record:: + record (int e, const string& m) + : error_ (e), message_ (m) { } @@ -26,10 +28,34 @@ namespace odb { } + database_exception:: + database_exception () + { + } + + database_exception:: + database_exception (int e, const string& m) + { + append (e, m); + } + + void database_exception:: + append (int e, const string& m) + { + records_.push_back (record (e, m)); + + if (!what_.empty ()) + what_ += '\n'; + + ostringstream ostr; + ostr << e << ": " << m; + what_ += ostr.str (); + } + const char* database_exception:: what () const throw () { - return message_.c_str (); + return what_.c_str (); } // diff --git a/odb/oracle/exceptions.hxx b/odb/oracle/exceptions.hxx index ae5a0e7..db4ffe9 100644 --- a/odb/oracle/exceptions.hxx +++ b/odb/oracle/exceptions.hxx @@ -9,6 +9,7 @@ #include #include +#include #include @@ -21,28 +22,66 @@ namespace odb { struct LIBODB_ORACLE_EXPORT database_exception: odb::database_exception { - database_exception (int error, const std::string& message); + struct record + { + record (int error, const std::string& message); - ~database_exception () throw (); + int + error () const + { + return error_; + } + + const std::string& + message () const + { + return message_; + } + + private: + int error_; + std::string message_; + }; + + typedef std::vector records; + + typedef records::size_type size_type; + typedef records::const_iterator iterator; + + iterator + begin () const + { + return records_.begin (); + } - int - error () const + iterator + end () const { - return error_; + return records_.end (); } - const std::string& - message () const + size_type + size () const { - return message_; + return records_.size (); } + public: + ~database_exception () throw (); + + database_exception (); + database_exception (int error, const std::string& message); + virtual const char* what () const throw (); + void + append (int error, + const std::string& message); + private: - int error_; - std::string message_; + records records_; + std::string what_; }; struct LIBODB_ORACLE_EXPORT cli_exception: odb::exception diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index 921a826..26f1358 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -229,7 +229,7 @@ namespace odb OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + translate_error (err, r, &conn_); stmt_.reset (handle, OCI_STRLS_CACHE_DELETE, err); @@ -1023,7 +1023,7 @@ namespace odb cs_form); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + translate_error (err, r, &conn_); chunk_position cp; @@ -1114,7 +1114,7 @@ namespace odb r = OCIStmtExecute (handle, stmt_, err, 0, 0, 0, 0, OCI_DEFAULT); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + translate_error (err, r, &conn_); // In order to successfully execute a select statement, OCI/Oracle // requires that there be OCIDefine handles provided for all select @@ -1195,7 +1195,7 @@ namespace odb // and ignore this error. // if (e != 1406) - translate_error (err, r); + translate_error (err, r, &conn_); } else if (r == OCI_INVALID_HANDLE) translate_error (err, r); @@ -1208,7 +1208,7 @@ namespace odb r = OCIStmtExecute (handle, stmt_, err, 1, 0, 0, 0, OCI_DEFAULT); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + translate_error (err, r, &conn_); } ub4 row_count (0); @@ -1328,7 +1328,7 @@ namespace odb OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + translate_error (err, r, &conn_); done_ = r == OCI_NO_DATA; @@ -1372,7 +1372,7 @@ namespace odb OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (conn_.error_handle (), r); + translate_error (conn_.error_handle (), r, &conn_); else if (r == OCI_NO_DATA) done_ = true; } @@ -1393,7 +1393,7 @@ namespace odb OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (conn_.error_handle (), r); + translate_error (conn_.error_handle (), r, &conn_); done_ = true; } @@ -1561,7 +1561,7 @@ namespace odb if (e == 1) return false; else - translate_error (err, r); + translate_error (err, r, &conn_); } ub4 row_count (0); @@ -1640,7 +1640,7 @@ namespace odb OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + translate_error (err, r, &conn_); ub4 row_count (0); r = OCIAttrGet (stmt_, @@ -1706,7 +1706,7 @@ namespace odb OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + translate_error (err, r, &conn_); ub4 row_count (0); r = OCIAttrGet (stmt_, diff --git a/odb/oracle/transaction-impl.cxx b/odb/oracle/transaction-impl.cxx index e46aac1..a22d5c7 100644 --- a/odb/oracle/transaction-impl.cxx +++ b/odb/oracle/transaction-impl.cxx @@ -106,7 +106,7 @@ namespace odb OCI_TRANS_NEW); if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) - translate_error (err, s); + translate_error (err, s, connection_.get ()); } void transaction_impl:: @@ -123,7 +123,7 @@ namespace odb OCI_DEFAULT)); if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) - translate_error (connection_->error_handle (), s); + translate_error (connection_->error_handle (), s, connection_.get ()); } void transaction_impl:: @@ -140,7 +140,7 @@ namespace odb OCI_DEFAULT)); if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) - translate_error (connection_->error_handle (), s); + translate_error (connection_->error_handle (), s, connection_.get ()); } } } -- cgit v1.1