From 62e234c114d2b6ead93a1d39593c66b648c3d0a6 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 1 Feb 2024 15:47:37 +0300 Subject: Turn libodb-oracle repository into package for muti-package repository --- libodb-oracle/odb/oracle/error.cxx | 225 +++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 libodb-oracle/odb/oracle/error.cxx (limited to 'libodb-oracle/odb/oracle/error.cxx') diff --git a/libodb-oracle/odb/oracle/error.cxx b/libodb-oracle/odb/oracle/error.cxx new file mode 100644 index 0000000..3c9a7df --- /dev/null +++ b/libodb-oracle/odb/oracle/error.cxx @@ -0,0 +1,225 @@ +// file : odb/oracle/error.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include + +#include // std::strlen +#include + +#include + +#include +#include +#include + +using namespace std; + +namespace odb +{ + namespace oracle + { + static void + translate_error (void* h, ub4 htype, sword r, connection* conn, + size_t pos, multiple_exceptions* mex) + { + assert (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO); + + switch (r) + { + 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; + } + default: + { + break; + } + } + + sb4 e; + char b[512]; // Error message will be truncated if it does not fit. + + if (htype == OCI_HTYPE_ERROR) + { + // 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, + htype); + + 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. + e == 2049) // Distributed lock timeout. + nc = code_timeout; + else if (e == 28 || // Session has been killed. + e == 3113 || // End-of-file on communication channel. + 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; + } + + // Check if the connection is lost. If code is connection_lost, + // then we know it is gone. If code is deadlock, then the + // connection is most likely ok. + // + if (conn != 0 && (c == code_none || c == code_timeout)) + { + OCIServer* server; + 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; + 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 (); + } + + switch (c) + { + case code_deadlock: + throw deadlock (); + case code_timeout: + throw timeout (); + case code_connection_lost: + { + if (conn != 0) + conn->mark_failed (); + + throw connection_lost (); + } + case code_none: + break; + } + } + + // Some other error code. Prepare database_exception. + // + database_exception dbe; + + for (sb4 i (1);; ++i) + { + r = OCIErrorGet (h, + i, + 0, + &e, + reinterpret_cast (b), + 512, + htype); + + if (r == OCI_NO_DATA) + break; + + // Get rid of a trailing newline if there is one. + // + size_t n (strlen (b)); + if (n != 0 && b[n - 1] == '\n') + b[n - 1] = '\0'; + + dbe.append (e, b); + } + + if (mex == 0) + throw dbe; + else + // It could be that some of these errors are fatal. I guess we + // will just have to learn from experience which ones are. The + // client code can always treat specific error codes as fatal. + // + mex->insert (pos, dbe); + } + + void + translate_error (OCIError* h, sword r, connection* c, + size_t pos, multiple_exceptions* mex) + { + translate_error (h, OCI_HTYPE_ERROR, r, c, pos, mex); + } + + void + translate_error (connection& c, sword r) + { + translate_error (c.error_handle (), OCI_HTYPE_ERROR, r, &c, 0, 0); + } + + void + translate_error (OCIEnv* h) + { + translate_error (h, OCI_HTYPE_ENV, OCI_ERROR, 0, 0, 0); + } + } +} -- cgit v1.1