aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 ());
}
}
}