diff options
author | Constantin Michael <constantin@codesynthesis.com> | 2011-09-16 09:32:57 +0200 |
---|---|---|
committer | Constantin Michael <constantin@codesynthesis.com> | 2011-09-16 10:03:12 +0200 |
commit | ee99d2c795d9b6afc263f7ae5c15a62abd258e4e (patch) | |
tree | 896c28be19cd8c1bf2682edcd83258e8f9ca9c78 /odb/oracle/statement.cxx | |
parent | e601ea00bf044fd1ac48db859c2c8491f45fda3f (diff) |
Corrections to LOB parameter and results callback implementation
Diffstat (limited to 'odb/oracle/statement.cxx')
-rw-r--r-- | odb/oracle/statement.cxx | 233 |
1 files changed, 111 insertions, 122 deletions
diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index 32b015a..70859a4 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -13,8 +13,9 @@ #include <odb/oracle/database.hxx> #include <odb/oracle/statement.hxx> #include <odb/oracle/connection.hxx> -#include <odb/oracle/error.hxx> #include <odb/oracle/auto-descriptor.hxx> +#include <odb/oracle/error.hxx> +#include <odb/oracle/exceptions.hxx> using namespace std; @@ -28,52 +29,48 @@ namespace odb ub4, // iteration ub4, // index void** buffer, - ub4* len, + ub4* size, ub1* piece, void** indicator) { bind& b (*static_cast<bind*> (context)); - // Only callback to user if the parameter is not NULL. + // Only call callback if the parameter is not NULL. // if (*b.indicator != -1) { - bind::piece p; - + chunk_position pos; if (!(*b.callback.param) (&b.callback_context, - buffer, - len, reinterpret_cast<ub4*> (b.size), - &p, + buffer, + size, + &pos, b.buffer, b.capacity)) return OCI_ERROR; - switch (p) + switch (pos) { - case bind::whole: + case one_chunk: { *piece = OCI_ONE_PIECE; break; } - case bind::first: + case first_chunk: { *piece = OCI_FIRST_PIECE; break; } - case bind::next: + case next_chunk: { *piece = OCI_NEXT_PIECE; break; } - case bind::last: + case last_chunk: { *piece = OCI_LAST_PIECE; break; } - default: - assert (0); - return OCI_ERROR; } } else @@ -117,17 +114,13 @@ namespace odb OCIError* err (conn_.error_handle ()); // The parameter position in OCIBindByPos is specified as a 1-based - // index. Convert 'o' to a 1-based offset. + // index. Convert o to a 1-based offset. // ++o; for (size_t e (o + c); o < e; ++c, ++b) { - bool callback ((b->type == SQLT_BLOB || b->type == SQLT_CLOB) && - b->callback.param != 0); - OCIBind* h (0); - sword r (OCIBindByPos (stmt_, &h, err, @@ -140,12 +133,13 @@ namespace odb 0, 0, 0, - callback ? OCI_DATA_AT_EXEC : OCI_DEFAULT)); + b->callback.param != 0 ? + OCI_DATA_AT_EXEC : OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); - if (callback) + if (b->callback.param != 0) { r = OCIBindDynamic (h, err, b, ¶m_callback_proxy, 0, 0); @@ -162,9 +156,9 @@ namespace odb OCIError* err (conn_.error_handle ()); - // The parameter position in OCIDefineByPos is specified as a 1-based - // index. - // + result_binds_ = b; + count_ = c; + for (size_t i (1); i <= c; ++i, ++b) { if (b->type == SQLT_BLOB || b->type == SQLT_CLOB) @@ -185,10 +179,10 @@ namespace odb if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); - // @@ LOB prefetching is only supported in OCI version 11.1 and - // greater and in Oracle server 11.1 and greater. If this code is - // called against a pre 11.1 server, the call to OCIAttrSet will - // return an error code. + // LOB prefetching is only supported in OCI version 11.1 and greater + // and in Oracle server 11.1 and greater. If this code is called + // against a pre 11.1 server, the call to OCIAttrSet will return an + // error code. // #if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >= 1) \ || OCI_MAJOR_VERSION > 11 @@ -204,7 +198,7 @@ namespace odb if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); - bool b (true); + boolean b (true); r = OCIAttrSet (h, OCI_HTYPE_DEFINE, &b, @@ -238,6 +232,81 @@ namespace odb } } + void statement:: + stream_result_lobs () + { + OCIError* err (conn_.error_handle()); + + for (size_t i (0); i < count_; ++i) + { + bind& b (result_binds_[i]); + + // Only stream if the bind specifies a LOB type, and the LOB value is + // not NULL, and a result callback has been provided. + // + if ((b.type == SQLT_BLOB || b.type == SQLT_CLOB) && + *b.indicator != -1 && + b.callback.result != 0) + { + // If b.capacity is 0, we will be stuck in an infinite loop. + // + assert (b.capacity != 0); + + OCILobLocator* locator (reinterpret_cast<OCILobLocator*> (b.size)); + + ub8 size (0); + sword r (OCILobGetLength2(conn_.handle (), + err, + locator, + &size)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + ub1 piece (OCI_FIRST_PIECE); + ub8 read (size); + + do + { + r = OCILobRead2 (conn_.handle (), + err, + locator, + &read, + 0, + 1, + b.buffer, + b.capacity, + piece, + 0, + 0, + 0, + SQLCS_IMPLICIT); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + chunk_position cp; + + if (piece == OCI_FIRST_PIECE) + cp = read == size ? one_chunk : first_chunk; + else if (r == OCI_NEED_DATA) + cp = next_chunk; + else + cp = last_chunk; + + if (!(*b.callback.result) (b.callback_context, + b.buffer, + static_cast<ub4> (read), + cp)) + throw database_exception (24343, "user defined callback error"); + + piece = OCI_NEXT_PIECE; + + } while (r == OCI_NEED_DATA); + } + } + } + statement:: ~statement () { @@ -257,13 +326,12 @@ namespace odb const string& s, binding& cond, binding& data, - std::size_t lob_prefetch_len) + std::size_t lob_prefetch_size) : statement (conn, s), - data_ (data), done_ (false) { bind_param (cond.bind, cond.count, 0); - bind_result (data.bind, data.count, lob_prefetch_len); + bind_result (data.bind, data.count, lob_prefetch_size); } void select_statement:: @@ -272,10 +340,10 @@ namespace odb if (!done_) free_result (); - // @@ Retrieve a single row into the already bound output buffers - // as an optimization? This will avoid multiple server round-trips - // in the case of a single object load. Keep in mind the implications - // on LOB prefetch. + // @@ Retrieve a single row into the already bound output buffers as an + // optimization? This will avoid multiple server round-trips in the case + // of a single object load. Keep the implications on LOB prefetch in + // mind. // sword r (OCIStmtExecute (conn_.handle (), stmt_, @@ -330,85 +398,6 @@ namespace odb } } - void select_statement:: - stream_result_lobs () - { - OCIError* err (conn_.error_handle()); - - for (size_t i (0); i < data_.count; ++i) - { - // Only stream if the bind specifies a LOB type, and the LOB - // value is not NULL, and a result callback has been provided. - // - if ((data_.bind[i].type == SQLT_BLOB || - data_.bind[i].type == SQLT_CLOB) && - *data_.bind[i].indicator != -1 && - data_.bind[i].callback.result != 0) - { - // @@ If data_.bind[i].capacity is 0, we will be stuck in an - // infinite loop. - // - assert (data_.bind[i].capacity != 0); - - OCILobLocator* locator ( - reinterpret_cast<OCILobLocator*> (data_.bind[i].size)); - - ub8 length (0); - sword r (OCILobGetLength2(conn_.handle (), - err, - locator, - &length)); - - if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); - - for (ub8 total (0), read (0); total < length; total += read) - { - read = length - total; - read = read > data_.bind[i].capacity ? - data_.bind[i].capacity : read; - - // The call to OCILobRead2 does not need to know when the last - // piece is being requested. - // - ub1 oci_piece (total == 0 ? OCI_FIRST_PIECE : OCI_NEXT_PIECE); - - r = OCILobRead2 (conn_.handle (), - err, - locator, - &read, - 0, - 1, // Starting offset. The first position is 1. - // This parameter is only used by OCI on the - // first call when polling. - data_.bind[i].buffer, - data_.bind[i].capacity, - oci_piece, - 0, - 0, - 0, - SQLCS_IMPLICIT); - - if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); - - bind::piece user_piece; - if (oci_piece == OCI_FIRST_PIECE) - user_piece = read == length ? bind::whole : bind::first; - else - user_piece = total + read < length ? bind::next : bind::last; - - if (!(*data_.bind[i].callback.result) ( - data_.bind[i].callback_context, - data_.bind[i].buffer, - read, - user_piece)) - break; - } - } - } - } - // // insert_statement // @@ -419,7 +408,7 @@ namespace odb ub4, // iter ub4, // index void** buffer, - ub4* len, + ub4* size, ub1* piece, void** indicator) { @@ -428,7 +417,7 @@ namespace odb bind& b (*static_cast<bind*> (context)); *buffer = 0; - *len = 0; + *size = 0; *piece = OCI_ONE_PIECE; b.indicator = -1; *indicator = &b.indicator; @@ -442,7 +431,7 @@ namespace odb ub4, // iter ub4, // index void** buffer, - ub4** len, + ub4** size, ub1*, // piece void** indicator, ub2** rcode) @@ -454,10 +443,10 @@ namespace odb #if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \ || OCI_MAJOR_VERSION > 11 *buffer = &b.id.value64; - **len = sizeof (unsigned long long); + **size = sizeof (unsigned long long); #else *buffer = &b.id.value32; - **len = sizeof (unsigned int); + **size = sizeof (unsigned int); #endif *indicator = &b.indicator; |