diff options
-rw-r--r-- | odb/oracle/connection-factory.cxx | 7 | ||||
-rw-r--r-- | odb/oracle/connection.cxx | 2 | ||||
-rw-r--r-- | odb/oracle/connection.hxx | 14 | ||||
-rw-r--r-- | odb/oracle/error.cxx | 173 | ||||
-rw-r--r-- | odb/oracle/error.hxx | 13 | ||||
-rw-r--r-- | odb/oracle/exceptions.cxx | 34 | ||||
-rw-r--r-- | odb/oracle/exceptions.hxx | 59 | ||||
-rw-r--r-- | odb/oracle/statement.cxx | 22 | ||||
-rw-r--r-- | 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<OCIError> error_; auto_handle<OCISvcCtx> handle_; + bool failed_; std::auto_ptr<statement_cache_type> 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 <odb/oracle/error.hxx> #include <odb/oracle/exceptions.hxx> +#include <odb/oracle/connection.hxx> 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<OraText*> (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<OraText*> (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<OraText*> (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 <oci.h> +#include <odb/oracle/forward.hxx> #include <odb/oracle/version.hxx> #include <odb/oracle/details/export.hxx> @@ -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 <sstream> + #include <odb/oracle/exceptions.hxx> 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 <odb/pre.hxx> #include <string> +#include <vector> #include <odb/exceptions.hxx> @@ -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<record> 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 ()); } } } |