diff options
author | Constantin Michael <constantin@codesynthesis.com> | 2011-09-14 10:10:36 +0200 |
---|---|---|
committer | Constantin Michael <constantin@codesynthesis.com> | 2011-09-14 10:10:36 +0200 |
commit | 5b650a2f0b506fb871beaab6de71e1f628b6baf7 (patch) | |
tree | 590f6927641e8ed7d5849d468da5c583a2467068 | |
parent | 3d76b241bc6af7f7f3beda3cc9d6609cf5db0f43 (diff) |
Implement LOB result callbacks using OCI LOB interface
-rw-r--r-- | odb/oracle/oracle-types.hxx | 16 | ||||
-rw-r--r-- | odb/oracle/statement.cxx | 243 | ||||
-rw-r--r-- | odb/oracle/statement.hxx | 13 |
3 files changed, 159 insertions, 113 deletions
diff --git a/odb/oracle/oracle-types.hxx b/odb/oracle/oracle-types.hxx index 1f5fe57..5911d4f 100644 --- a/odb/oracle/oracle-types.hxx +++ b/odb/oracle/oracle-types.hxx @@ -19,7 +19,8 @@ namespace odb { ub2 type; // The type stored by buffer. This must be an external // OCI type identifier of the form SQLT_XXX. - void* buffer; + void* buffer; // Image buffer pointer. If this bind is associated with + // an output LOB column, interpret as an OCILobLocator*. ub2* size; // The number of bytes in buffer. Due to // inconsistencies in the OCI interface, this will be // interpreted as a ub4 when callbacks are in use. @@ -27,16 +28,14 @@ namespace odb // buffer. sb2* indicator; // Pointer to an OCI indicator variable. - // Callback function signature used to specify parameters that are + // Callback function signature used to specify LOB parameters that are // passed to the database. // typedef bool (*param_callback_type) ( void** context, // [in/out] The user context. - void** buffer, // [out] A a buffer containing the parameter data - // to write to the database. + void** buffer, // [out] On return, a pointer to a buffer containing + // parameter data. ub4* size, // [out] The parameter data length in bytes. - bool* null, // [out] True if the parameter is null. Both buffer - // and size are ignored is this is true. bool* last, // [out] True if buffer contains the last piece of // data. void* temp_buffer, // [in] A temporary buffer that may be used if @@ -44,14 +43,13 @@ namespace odb // this buffer on return if it is used. ub4 capacity); // [in] The temporary buffer length in bytes. - // Callback function signature used to specify values returned from the - // database. + // Callback function signature used to specify LOB values returned from + // the database. // typedef bool (*result_callback_type) ( void** context, // [in/out] The user context. void* buffer, // [in] A buffer containing the result data. ub4 size, // [in] The result data length in bytes. - bool null, // [in] True if the result data is NULL. bool last); // [in] True if this is the last piece of result data. param_callback_type param_callback; diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index 447ef06..4ab8af1 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -12,6 +12,7 @@ #include <odb/oracle/statement.hxx> #include <odb/oracle/connection.hxx> #include <odb/oracle/error.hxx> +#include <odb/oracle/auto-descriptor.hxx> using namespace std; @@ -22,8 +23,8 @@ namespace odb static sb4 param_callback_proxy (void* context, OCIBind*, - ub4, // iteration - ub4, // index + ub4, // iteration + ub4, // index void** buffer, ub4* len, ub1* piece, @@ -31,61 +32,44 @@ namespace odb { bind& b (*static_cast<bind*> (context)); - bool last (false), null (false); - - if (!(*b.param_callback) (&b.callback_context, - buffer, - len, - &null, - &last, - b.buffer, - b.capacity)) - return OCI_ROWCBK_DONE; - - - *b.indicator = null ? -1 : 0; - *indicator = &b.indicator; - - if (null) - *piece = OCI_ONE_PIECE; - else if (b.first_piece) + if (b.indicator != -1) { - b.first_piece = false; - *piece = last ? OCI_ONE_PIECE : OCI_FIRST_PIECE; + bool last (false); + if (!(*b.param_callback) (&b.callback_context, + buffer, + len, + &last, + b.buffer, + b.capacity)) + return OCI_ERROR; + + if (b.first_piece) + { + b.first_piece = false; + *piece = last ? OCI_ONE_PIECE : OCI_FIRST_PIECE; + } + else if (last) + *piece = OCI_LAST_PIECE; + else + *piece = OCI_NEXT_PIECE; } - else if (last) - *piece = OCI_LAST_PIECE; else - *piece = OCI_NEXT_PIECE; + *piece = OCI_ONE_PIECE; + + *indicator = &b.indicator; return OCI_CONTINUE; } static sb4 - result_callback_proxy (void* context, - OCIDefine*, - ub4, // iteration - void** buffer, - ub4** len, - ub1* piece, - void** indicator, - ub2**) // return_code) + lob_result_callback_proxy (void* context, + const void* buffer, + ub8 len, + ub1 piece, + void** new_buffer, + ub8* new_len) { bind& b (*static_cast<bind*> (context)); - - if (*piece == OCI_NEXT_PIECE) - if (!(*b.result_callback) (&b.callback_context, - &b.buffer, - *reinterpret_cast<ub4*> (b.size), - false, - false)) - return OCI_ROWCBK_DONE; - - *buffer = b.buffer; - *len = reinterpret_cast<ub4*> (b.size); - **len = static_cast<ub4> (b.capacity); - *indicator = &b.indicator; - return OCI_CONTINUE; } @@ -128,6 +112,10 @@ namespace odb for (size_t e (o + c); o < e; ++c, ++b) { + bool callback ((b->type == SQLT_BLOB || + b->type == SQLT_CLOB || + b->type == SQLT_NCLOB) && b->param_callback != 0); + OCIBind* h (0); sword r (OCIBindByPos (stmt_, @@ -142,13 +130,12 @@ namespace odb 0, 0, 0, - b->param_callback != 0 ? - OCI_DATA_AT_EXEC : OCI_DEFAULT)); + callback ? OCI_DATA_AT_EXEC : OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); - if (b->param_callback != 0) + if (callback) { r = OCIBindDynamic (h, err, b, ¶m_callback_proxy, 0, 0); @@ -168,27 +155,56 @@ namespace odb // for (size_t i (1); i <= c; ++i, ++b) { - OCIDefine* h (0); - - sword r (OCIDefineByPos (stmt_, - &h, - err, - i, - b->buffer, - b->capacity, - b->type, - b->indicator, - b->size, - 0, - b->result_callback != 0 ? - OCI_DYNAMIC_FETCH : OCI_DEFAULT)); + if (b->type == SQLT_BLOB || + b->type == SQLT_CLOB || + b->type == SQLT_NCLOB) + { + auto_descriptor<OCILobLocator> locator; + { + sword r (OCIDescriptorAlloc (conn_.db ().environment (), + &b->buffer, + OCI_DTYPE_LOB, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw bad_alloc (); + + locator.reset (static_cast<OCILobLocator*> (b->buffer)); + } + + OCIDefine* h (0); + sword r (OCIDefineByPos (stmt_, + &h, + err, + i, + &locator, + sizeof (OCILobLocator*), + b->type, + b->indicator, + 0, + 0, + OCI_DEFAULT)); - if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); - if (b->result_callback != 0) + locator.reset (); + } + else { - r = OCIDefineDynamic (h, err, b, &result_callback_proxy); + OCIDefine* h (0); + sword r (OCIDefineByPos (stmt_, + &h, + err, + i, + b->buffer, + b->capacity, + b->type, + b->indicator, + b->size, + 0, + OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); @@ -196,20 +212,6 @@ namespace odb } } - void statement:: - finalize_result (bind* b, std::size_t c) - { - for (size_t i (0); i < c; ++i) - { - if (b[i].result_callback != 0) - (*b[i].result_callback) (&b[i].callback_context, - b[i].buffer, - *reinterpret_cast<ub4*> (b[i].size), - *b[i].indicator == -1, - true); - } - } - statement:: ~statement () { @@ -228,7 +230,8 @@ namespace odb select_statement (connection& conn, const string& s, binding& cond, - binding& data) + binding& data, + std::size_t lob_prefetch_len) : statement (conn, s), data_ (data), done_ (false) @@ -245,8 +248,8 @@ namespace odb // @@ 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. Remember to invoke - // finalize_result on a successful prefetch. + // in the case of a single object load. Keep in mind the implications + // on LOB prefetch. // sword r (OCIStmtExecute (conn_.handle (), stmt_, @@ -261,46 +264,90 @@ namespace odb translate_error (conn_.error_handle (), r); } - void select_statement:: - free_result () + select_statement::result select_statement:: + fetch () { if (!done_) { sword r (OCIStmtFetch2 (stmt_, conn_.error_handle (), - 0, + 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (conn_.error_handle (), r); - - done_ = true; + else if (r == OCI_NO_DATA) + done_ = true; + else + finalize_result (data_.bind, data_.count); } + + return done_ ? no_data : success; } - select_statement::result select_statement:: - fetch () + void select_statement:: + free_result () { if (!done_) { sword r (OCIStmtFetch2 (stmt_, conn_.error_handle (), - 1, + 0, OCI_FETCH_NEXT, 0, OCI_DEFAULT)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (conn_.error_handle (), r); - else if (r == OCI_NO_DATA) - done_ = true; - else - finalize_result (data_.bind, data_.count); + + done_ = true; } + } - return done_ ? no_data : success; + void select_statement:: + stream_result_lobs () + { + OCIError* err (conn_.error_handle()); + + for (size_t i (0); i < data_.count; ++i) + { + if ((data_.bind[i].type == SQLT_BLOB || + data_.bind[i].type == SQLT_CLOB || + data_.bind[i].type == SQLT_NCLOB) && + data_.bind[i].indicator != -1) + { + OCILobLocator* locator ( + reinterpret_cast<OCILobLocator> (data_.bind[i].buffer)); + + ub8 length (0); + sword r (OCILobGetLength2(conn_.handle (), + err, + locator, + &length)); + + if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + + sword r (OCILobRead2 (conn_.handle (), + conn_.error_handle (), + err, + &length, + 0, + 1, // offset - first position is 1. + 0, + 0, + OCI_FIRST_PIECE, + &data_.bind[i], + &lob_result_callback_proxy, + 0, + SQLCS_IMPLICIT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_.error_handle (), r); + } + } } // diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx index ffdb91a..3ab60bc 100644 --- a/odb/oracle/statement.hxx +++ b/odb/oracle/statement.hxx @@ -50,11 +50,6 @@ namespace odb void bind_result (bind*, std::size_t count); - // Finalize the result by invoking the user provided callbacks for the - // final time. - // - void finalize_result (bind*, std::size_t count); - protected: connection& conn_; auto_handle<OCIStmt> stmt_; @@ -69,7 +64,8 @@ namespace odb select_statement (connection& conn, const std::string& statement, binding& cond, - binding& data); + binding& data, + std::size_t lob_prefetch_len = 0); enum result { success, @@ -85,6 +81,11 @@ namespace odb void free_result (); + // Stream the result lobs, invoking user callbacks where necessary. + // + void + stream_result_lobs (); + private: select_statement (const select_statement&); select_statement& operator= (const select_statement&); |