aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConstantin Michael <constantin@codesynthesis.com>2011-11-29 14:45:51 +0200
committerConstantin Michael <constantin@codesynthesis.com>2011-11-30 11:32:07 +0200
commitc352bdb8b4ee7a636f71a71a301c181942af2d39 (patch)
tree5b7faa4d826a23162a59e241cb800a9f0f7e2db8
parent3ed6a781cf4aad7986f805dc8a8e5c487d10a805 (diff)
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.
-rw-r--r--odb/oracle/connection-factory.cxx7
-rw-r--r--odb/oracle/connection.cxx2
-rw-r--r--odb/oracle/connection.hxx14
-rw-r--r--odb/oracle/error.cxx173
-rw-r--r--odb/oracle/error.hxx13
-rw-r--r--odb/oracle/exceptions.cxx34
-rw-r--r--odb/oracle/exceptions.hxx59
-rw-r--r--odb/oracle/statement.cxx22
-rw-r--r--odb/oracle/transaction-impl.cxx6
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 ());
}
}
}