From 159c8b877add5c523b575aa3d3d34a4931b1266c Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 14 Aug 2014 09:37:06 +0200 Subject: Draft implementation for INSERT --- odb/oracle/binding.hxx | 22 +- odb/oracle/error.cxx | 21 +- odb/oracle/error.hxx | 12 +- odb/oracle/exceptions.cxx | 24 ++ odb/oracle/exceptions.hxx | 12 + odb/oracle/oracle-types.hxx | 3 +- odb/oracle/simple-object-statements.hxx | 17 +- odb/oracle/simple-object-statements.txx | 17 +- odb/oracle/statement.cxx | 381 +++++++++++++++++++++++++------- odb/oracle/statement.hxx | 39 +++- 10 files changed, 428 insertions(+), 120 deletions(-) diff --git a/odb/oracle/binding.hxx b/odb/oracle/binding.hxx index 94e7128..be201a7 100644 --- a/odb/oracle/binding.hxx +++ b/odb/oracle/binding.hxx @@ -11,6 +11,7 @@ #include #include +#include #include @@ -24,10 +25,23 @@ namespace odb typedef oracle::bind bind_type; typedef oracle::change_callback change_callback_type; - binding (): bind (0), count (0), version (0), change_callback (0) {} + binding () + : bind (0), count (0), version (0), + batch (0), skip (0), status (0), + change_callback (0) {} binding (bind_type* b, std::size_t n) - : bind (b), count (n), version (0), change_callback (0) + : bind (b), count (n), version (0), + batch (1), skip (0), status (0), + change_callback (0) + { + } + + binding (bind_type* b, std::size_t n, + std::size_t bt, std::size_t s, auto_handle* st) + : bind (b), count (n), version (0), + batch (bt), skip (s), status (st), + change_callback (0) { } @@ -35,6 +49,10 @@ namespace odb std::size_t count; std::size_t version; + std::size_t batch; + std::size_t skip; + auto_handle* status; // Batch status array. + change_callback_type* change_callback; private: diff --git a/odb/oracle/error.cxx b/odb/oracle/error.cxx index afab2c7..68bdc44 100644 --- a/odb/oracle/error.cxx +++ b/odb/oracle/error.cxx @@ -19,7 +19,8 @@ namespace odb namespace oracle { static void - translate_error (void* h, ub4 htype, sword r, connection* conn) + 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); @@ -186,25 +187,33 @@ namespace odb dbe.append (e, b); } - throw dbe; + 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) + translate_error (OCIError* h, sword r, connection* c, + size_t pos, multiple_exceptions* mex) { - translate_error (h, OCI_HTYPE_ERROR, r, 0); + 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); + 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); + translate_error (h, OCI_HTYPE_ENV, OCI_ERROR, 0, 0, 0); } } } diff --git a/odb/oracle/error.hxx b/odb/oracle/error.hxx index fdb0537..accfc3f 100644 --- a/odb/oracle/error.hxx +++ b/odb/oracle/error.hxx @@ -7,20 +7,24 @@ #include +#include // std::size_t + #include -#include +#include // connection, multiple_exceptions #include + #include namespace odb { namespace oracle { - // Translate OCI error given an error handle and throw an appropriate - // exception. + // Translate OCI error given an error handle and throw (or return, + // in case multiple_exceptions is not NULL) an appropriate exception. // LIBODB_ORACLE_EXPORT void - translate_error (OCIError*, sword result); + translate_error (OCIError*, sword result, connection* = 0, + std::size_t pos = 0, multiple_exceptions* = 0); LIBODB_ORACLE_EXPORT void translate_error (connection&, sword result); diff --git a/odb/oracle/exceptions.cxx b/odb/oracle/exceptions.cxx index b6375e7..4676843 100644 --- a/odb/oracle/exceptions.cxx +++ b/odb/oracle/exceptions.cxx @@ -57,6 +57,12 @@ namespace odb return what_.c_str (); } + database_exception* database_exception:: + clone () const + { + return new database_exception (*this); + } + // // lob_comparison // @@ -67,6 +73,12 @@ namespace odb return "comparison of LOB values in queries not supported"; } + lob_comparison* lob_comparison:: + clone () const + { + return new lob_comparison (*this); + } + // // cli_exception // @@ -88,6 +100,12 @@ namespace odb return what_.c_str (); } + cli_exception* cli_exception:: + clone () const + { + return new cli_exception (*this); + } + // // invalid_oci_handle // @@ -97,5 +115,11 @@ namespace odb { return "invalid oci handle passed or unable to allocate handle"; } + + invalid_oci_handle* invalid_oci_handle:: + clone () const + { + return new invalid_oci_handle (*this); + } } } diff --git a/odb/oracle/exceptions.hxx b/odb/oracle/exceptions.hxx index d8ca14a..87327b9 100644 --- a/odb/oracle/exceptions.hxx +++ b/odb/oracle/exceptions.hxx @@ -76,6 +76,9 @@ namespace odb virtual const char* what () const throw (); + virtual database_exception* + clone () const; + void append (sb4 error, const std::string& message); @@ -88,6 +91,9 @@ namespace odb { virtual const char* what () const throw (); + + virtual lob_comparison* + clone () const; }; struct LIBODB_ORACLE_EXPORT cli_exception: odb::exception @@ -98,6 +104,9 @@ namespace odb virtual const char* what () const throw (); + virtual cli_exception* + clone () const; + private: std::string what_; }; @@ -106,6 +115,9 @@ namespace odb { virtual const char* what () const throw (); + + virtual invalid_oci_handle* + clone () const; }; namespace core diff --git a/odb/oracle/oracle-types.hxx b/odb/oracle/oracle-types.hxx index ec53425..ffc974a 100644 --- a/odb/oracle/oracle-types.hxx +++ b/odb/oracle/oracle-types.hxx @@ -115,7 +115,8 @@ namespace odb // bindings, this is interpreted as the OCIDefine // handle associated with the LOB result parameter. ub4 capacity; // The maximum number of bytes that can be stored in - // the buffer. + // the buffer. For LOBs, it used to store array skip + // size. sb2* indicator; // Pointer to an OCI indicator variable. lob_callback* callback; diff --git a/odb/oracle/simple-object-statements.hxx b/odb/oracle/simple-object-statements.hxx index ff76e91..2b550e5 100644 --- a/odb/oracle/simple-object-statements.hxx +++ b/odb/oracle/simple-object-statements.hxx @@ -276,9 +276,9 @@ namespace odb // Object image. // image_type& - image () + image (std::size_t i = 0) { - return image_; + return image_[i]; } // Insert binding. @@ -323,7 +323,7 @@ namespace odb // Object id image and binding. // id_image_type& - id_image () {return id_image_;} + id_image (std::size_t i = 0) {return id_image_[i];} std::size_t id_image_version () const {return id_image_version_;} @@ -353,7 +353,7 @@ namespace odb object_traits::persist_statement, object_traits::versioned, // Process if versioned. insert_image_binding_, - object_traits::auto_id)); + object_traits::auto_id ? &id_image_binding_ : 0)); return *persist_; } @@ -473,7 +473,8 @@ namespace odb extra_statement_cache_ptr extra_statement_cache_; - image_type image_; + image_type image_[object_traits::batch]; + auto_handle status_[object_traits::batch]; // Select binding. // @@ -502,10 +503,10 @@ namespace odb bind update_image_bind_[update_column_count + id_column_count + managed_optimistic_column_count]; - // Id image binding (only used as a parameter). Uses the suffix in - // the update bind. + // Id image binding (only used as a parameter or in RETURNING for + // auto ids). Uses the suffix in the update bind. // - id_image_type id_image_; + id_image_type id_image_[object_traits::batch]; std::size_t id_image_version_; binding id_image_binding_; diff --git a/odb/oracle/simple-object-statements.txx b/odb/oracle/simple-object-statements.txx index 61bbc5f..32be8bd 100644 --- a/odb/oracle/simple-object-statements.txx +++ b/odb/oracle/simple-object-statements.txx @@ -43,24 +43,31 @@ namespace odb object_statements (connection_type& conn) : object_statements_base (conn), select_image_binding_ (select_image_bind_, select_column_count), - insert_image_binding_ (insert_image_bind_, insert_column_count), + insert_image_binding_ (insert_image_bind_, + insert_column_count, + object_traits::batch, + sizeof (image_type), + status_), update_image_binding_ (update_image_bind_, update_column_count + id_column_count + managed_optimistic_column_count), id_image_binding_ (update_image_bind_ + update_column_count, - id_column_count), + id_column_count, + object_traits::batch, + sizeof (id_image_type), + 0), od_ (update_image_bind_ + update_column_count) { - image_.version = 0; + image_[0].version = 0; // @@ TODO select_image_version_ = 0; insert_image_version_ = 0; update_image_version_ = 0; update_id_image_version_ = 0; - id_image_.version = 0; + id_image_[0].version = 0; // @@ TODO id_image_version_ = 0; - select_image_binding_.change_callback = image_.change_callback (); + select_image_binding_.change_callback = image_[0].change_callback (); std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_)); std::memset (update_image_bind_, 0, sizeof (update_image_bind_)); diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index 4032138..10fa3a6 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -4,8 +4,9 @@ #include -#include // std::strlen +#include // std::strlen, std::memset #include +#include // @@ tmp #include #include // object_not_persistent @@ -73,7 +74,7 @@ namespace odb extern "C" sb4 odb_oracle_param_callback_proxy (void* context, OCIBind*, - ub4, // iteration + ub4 it, // iteration ub4, // index void** buffer, ub4* size, @@ -81,15 +82,27 @@ namespace odb void** indicator) { bind& b (*static_cast (context)); - lob* l (static_cast (b.buffer)); + + // Offset the data based on the current iteration and skip size (stored + // in capacity). + // + sb2* ind (reinterpret_cast ( + reinterpret_cast (b.indicator) + it * b.capacity)); // Only call the callback if the parameter is not NULL. // - if (*b.indicator != -1) + if (*ind != -1) { + lob* l (reinterpret_cast ( + static_cast (b.buffer) + it * b.capacity)); + + lob_callback* cb ( + reinterpret_cast ( + reinterpret_cast (b.callback) + it * b.capacity)); + chunk_position pos; - if (!(*b.callback->callback.param) ( - b.callback->context.param, + if (!(*cb->callback.param) ( + cb->context.param, &l->position, const_cast (buffer), size, @@ -125,7 +138,7 @@ namespace odb else *piece = OCI_ONE_PIECE; - *indicator = b.indicator; + *indicator = ind; return OCI_CONTINUE; } @@ -321,7 +334,7 @@ namespace odb } ub4 statement:: - bind_param (bind* b, size_t n) + bind_param (bind* b, size_t n, size_t batch, size_t skip) { // Figure out how many unbind elements we will need and allocate them. // @@ -589,7 +602,12 @@ namespace odb // However, in Oracle, LOBs cannot be used in queries so we can // make an exception here. // - l->buffer = &lob_buffer; + for (size_t i (0); i != batch;) + { + l->buffer = &lob_buffer; + l = reinterpret_cast ( + static_cast (b->buffer) + ++i * skip); + } } assert (callback); @@ -603,6 +621,11 @@ namespace odb // capacity = 4096; + // Store skip in capacity so that the callback can offset the + // values based on the iteration number. + // + b->capacity = static_cast (skip); + break; } default: @@ -686,6 +709,23 @@ namespace odb if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); } + + // Set array information if we have a batch. + // + if (skip != 0) + { + ub4 s (static_cast (skip)); + + r = OCIBindArrayOfStruct (h, + err, + (value != 0 ? s : 0), // value + (b->indicator != 0 ? s : 0), // indicator + (size != 0 ? s : 0), // length + 0); // return code + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } } return i; @@ -1559,22 +1599,27 @@ namespace odb extern "C" sb4 odb_oracle_returning_in (void* context, OCIBind*, // bind - ub4, // iter + ub4 it, // iter ub4, // index void** buffer, ub4* size, ub1* piece, void** indicator) { - typedef insert_statement::id_bind_type bind; + binding& ret (*static_cast (context)->ret_); - bind& b (*static_cast (context)); + // Offset the data based on the current iteration and skip size. + // The id is always first. + // + sb2* ind (reinterpret_cast ( + reinterpret_cast ( + ret.bind[0].indicator) + it * ret.skip)); *buffer = 0; *size = 0; *piece = OCI_ONE_PIECE; - b.indicator = -1; - *indicator = &b.indicator; + *ind = -1; + *indicator = ind; return OCI_CONTINUE; } @@ -1582,30 +1627,61 @@ namespace odb extern "C" sb4 odb_oracle_returning_out (void* context, OCIBind*, // bind - ub4, // iter + ub4 it, // iter ub4, // index void** buffer, ub4** size, - ub1*, // piece + ub1* piece, void** indicator, ub2** rcode) { - typedef insert_statement::id_bind_type bind; + insert_statement& st (*static_cast (context)); + bind& b (st.ret_->bind[0]); // The id is always first. + size_t skip (st.ret_->skip); - bind& b (*static_cast (context)); + // Offset the data based on the current iteration and skip size. + // + *buffer = static_cast (b.buffer) + it * skip; -#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \ - || OCI_MAJOR_VERSION > 11 - *buffer = &b.id.integer; - **size = sizeof (unsigned long long); -#else - *buffer = b.id.number.buffer; - *size = &b.id.number.size; - b.id.number.size = 21; -#endif + if (b.type == bind::number) + { + // So the straightforward way to handle this would have been to + // set size to the properly offset pointer to b.size, just like + // we do for the buffer and indicator. The problem is that in + // OCI size is ub2 everywhere except in the *Dynamic() callbacks. + // Here it is expected to be ub4 and, as a result, we cannot use + // our ub2 size that we use throughout (I know you are tempted + // to just cast ub2* to ub4* and forget about this mess, but, + // trust me, this won't end up well). + // + // So what we will do instead is this: have a temporary ub4 buffer + // that we return to OCI so that it can store the size for us. But + // the callback can be called multiple times (batch operations) so + // on each subsequent call we will have to save the size from the + // previous call into our ub2 array. We will also have to handle + // the last extracted size after OCIStmtExecute() below. Thanks, + // Oracle! + // + if (it != 0) + { + *reinterpret_cast ( + reinterpret_cast (b.size) + (it - 1) * skip) = + static_cast (st.ret_size_); + } + + *size = &st.ret_size_; + } + + // For some reason we have to set the out size to the (presumably) + // maximum buffer size. + // + **size = b.capacity; + + *indicator = reinterpret_cast ( + reinterpret_cast (b.indicator) + it * skip); - *indicator = &b.indicator; *rcode = 0; + *piece = OCI_ONE_PIECE; return OCI_CONTINUE; } @@ -1620,12 +1696,13 @@ namespace odb const string& text, bool process, binding& param, - bool returning) + binding* returning) : statement (conn, text, statement_insert, - (process ? ¶m : 0), false) + (process ? ¶m : 0), false), + ret_ (returning), status_ (param.status) { - init (param, returning); + init (param); } insert_statement:: @@ -1633,37 +1710,43 @@ namespace odb const char* text, bool process, binding& param, - bool returning) + binding* returning) : statement (conn, text, statement_insert, - (process ? ¶m : 0), false) + (process ? ¶m : 0), false), + ret_ (returning), status_ (param.status) { - init (param, returning); + init (param); } void insert_statement:: - init (binding& param, bool returning) + init (binding& param) { - ub4 param_count (bind_param (param.bind, param.count)); - - if (returning) + ub4 param_count (bind_param (param.bind, param.count, + param.batch, param.skip)); + if (ret_ != 0) { OCIError* err (conn_.error_handle ()); OCIBind* h (0); + bind* b (ret_->bind); + +#if OCI_MAJOR_VERSION < 11 || \ + (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION < 2) + // Assert if a 64 bit integer buffer type is provided and the OCI + // version is unable to implicitly convert the NUMBER binary data + // to the relevant type. + // + assert ((b->type != bind::integer && b->type != bind::uinteger) || + b->capacity <= 4); +#endif sword r (OCIBindByPos (stmt_, &h, err, param_count + 1, 0, -#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \ - || OCI_MAJOR_VERSION > 11 - sizeof (unsigned long long), - SQLT_UIN, -#else - 21, - SQLT_NUM, -#endif + b->capacity, + param_sqlt_lookup[b->type], 0, 0, 0, @@ -1676,9 +1759,9 @@ namespace odb r = OCIBindDynamic (h, err, - &id_bind_, + this, &odb_oracle_returning_in, - &id_bind_, + this, &odb_oracle_returning_out); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) @@ -1686,8 +1769,8 @@ namespace odb } } - bool insert_statement:: - execute () + size_t insert_statement:: + execute (size_t n, multiple_exceptions* mex) { { odb::tracer* t; @@ -1697,60 +1780,192 @@ namespace odb t->execute (conn_, *this); } + mex_ = mex; OCIError* err (conn_.error_handle ()); sword r (OCIStmtExecute (conn_.handle (), stmt_, err, - 1, + static_cast (n), 0, 0, 0, - OCI_DEFAULT)); + n == 1 ? OCI_DEFAULT : OCI_BATCH_ERRORS)); + + // If the statement failed as a whole, assume only the first row + // was attempted (and failed). Otherwise, in the batch errors mode, + // all the rows are always attempted (let's hope this is true). + // + i_ = 0; + n_ = (r == OCI_ERROR || r == OCI_INVALID_HANDLE ? 1 : n); + + if (mex_ != 0) + { + mex_->current (i_); + mex_->attempted (n_); + } if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) { - sb4 e; - OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR); + if (mex_ != 0) + mex_->fatal (true); // An incomplete batch is always fatal. + + fetch (r, err); + return n_; + } - // The Oracle error code ORA-00001 indicates unique constraint - // violation, which covers more than just a duplicate primary key. - // Unfortunately, there is nothing more precise that we can use. + // Initialize the batch status array. + // + if (n_ != 1) + { + // Clear the status array. // - if (e == 1) - return false; - else - translate_error (conn_, r); + for (size_t i (0); i != n_; ++i) + status_[i].reset (); + + // @@ TODO allocate per batch stmt (maybe lazily here if NULL). + // + auto_handle err1; + { + OCIError* e (0); + r = OCIHandleAlloc (conn_.database ().environment (), + reinterpret_cast (&e), + OCI_HTYPE_ERROR, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + err1.reset (e); + } + + ub4 en; + r = OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &en, + 0, + OCI_ATTR_NUM_DML_ERRORS, + err1); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err1, r); + + cerr << "_NUM_DML_ERRORS: " << en << endl; + + for (ub4 i (0); i != en; ++i) + { + auto_handle err2; + + { + OCIError* e (0); + r = OCIHandleAlloc (conn_.database ().environment (), + reinterpret_cast (&e), + OCI_HTYPE_ERROR, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + err2.reset (e); + } + + OCIError* tmp (err2); + r = OCIParamGet (err, // from + OCI_HTYPE_ERROR, + err1, // diagnostics + reinterpret_cast (&tmp), // to + i); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err1, r); + + ub4 row; + r = OCIAttrGet (err2, + OCI_HTYPE_ERROR, + &row, + 0, + OCI_ATTR_DML_ROW_OFFSET, + err1); + + status_[row].reset (err2.release ()); + + cerr << "[" << row << "]" << endl; + } } - ub4 row_count (0); - r = OCIAttrGet (stmt_, - OCI_HTYPE_STMT, - &row_count, - 0, - OCI_ATTR_ROW_COUNT, - err); + // Store the last returned id size (see odb_oracle_returning_out() + // for details). + // + if (ret_ != 0) + { + size_t n (n_); + + // Find the index of the last sucessefully processed row. + // + if (n != 1) + for (; n != 0 && status_[n - 1] != 0; --n) ; + + if (n != 0) // They all might have failed. + { + *reinterpret_cast ( + reinterpret_cast ( + ret_->bind[0].size) + (n - 1) * ret_->skip) = + static_cast (ret_size_); + } + } + + if (n_ == 1) + fetch (OCI_SUCCESS, 0); + else + fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); + + return n_; + } + + void insert_statement:: + fetch (sword r, OCIError* err) + { + result_ = true; if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + { + // An auto-assigned object id should never cause a duplicate primary + // key. + // + if (ret_ == 0) + { + sb4 e; + OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR); - // The value of the OCI_ATTR_ROW_COUNT attribute associated with an - // INSERT statment represents the number of rows inserted. - // - return row_count != 0; + // The Oracle error code ORA-00001 indicates unique constraint + // violation, which covers more than just a duplicate primary key. + // Unfortunately, there is nothing more precise that we can use. + // + if (e == 1) + result_ = false; + } + + if (result_) + translate_error (err, r, &conn_, i_, mex_); // Can return. + } } - unsigned long long insert_statement:: - id () + bool insert_statement:: + result (std::size_t i) { -#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \ - || OCI_MAJOR_VERSION > 11 - return id_bind_.id.integer; -#else - return details::number_to_uint64 ( - id_bind_.id.number.buffer, - static_cast (id_bind_.id.number.size)); -#endif + assert ((i_ == i || i_ + 1 == i) && i < n_); + + // Get to the next row if necessary. + // + if (i != i_) + { + mex_->current (++i_); // It cannot be NULL since this is a batch. + fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); + } + + return result_; } // diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx index cf336c4..93213d1 100644 --- a/odb/oracle/statement.hxx +++ b/odb/oracle/statement.hxx @@ -11,6 +11,7 @@ #include // std::size_t #include +#include #include #include @@ -96,7 +97,8 @@ namespace odb // of columns bound. // ub4 - bind_param (bind*, std::size_t count); + bind_param (bind*, std::size_t count, + size_t batch = 1, std::size_t skip = 0); // Bind results for this statement. This function must only be // called once. Multiple calls to it will result in memory leaks @@ -261,27 +263,30 @@ namespace odb const std::string& text, bool process_text, binding& param, - bool returning); + binding* returning); insert_statement (connection_type& conn, const char* text, bool process_text, binding& param, - bool returning); + binding* returning); - // Return true if successful and false if the row is a duplicate. All - // other errors are reported by throwing exceptions. + // Return the number of rows (out of n) that were attempted. // - bool - execute (); + std::size_t + execute (std::size_t n = 1, multiple_exceptions* = 0); - unsigned long long - id (); + // Return true if successful and false if this row is a duplicate. + // All other errors are reported via exceptions. + // + bool + result (std::size_t i = 0); private: insert_statement (const insert_statement&); insert_statement& operator= (const insert_statement&); + /* // Only OCI versions 11.2 and greater support conversion of the internal // Oracle type NUMBER to an external 64-bit integer type. If we detect // version 11.2 or greater we provide an unsigned long long image. @@ -304,13 +309,25 @@ namespace odb sb2 indicator; }; + */ private: void - init (binding& param, bool returning); + init (binding& param); + + void + fetch (sword r, OCIError*); + + public: // For odb_oracle_returning_*(). + binding* ret_; + ub4 ret_size_; // You don't want to know (see statement.cxx). private: - id_bind_type id_bind_; + multiple_exceptions* mex_; + std::size_t n_; + std::size_t i_; + auto_handle* status_; + bool result_; }; class LIBODB_ORACLE_EXPORT update_statement: public statement -- cgit v1.1