diff options
Diffstat (limited to 'odb/oracle/error.cxx')
-rw-r--r-- | odb/oracle/error.cxx | 173 |
1 files changed, 143 insertions, 30 deletions
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; } } } |