From 02bf57c837750856b7e02165b30b9055bb2596cc Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 9 Oct 2014 11:22:06 +0200 Subject: Initial bulk erase implementation --- odb/oracle/simple-object-statements.txx | 2 +- odb/oracle/statement.cxx | 360 ++++++++++++++++++++------------ odb/oracle/statement.hxx | 61 +++++- odb/oracle/statement.ixx | 37 ++++ 4 files changed, 316 insertions(+), 144 deletions(-) create mode 100644 odb/oracle/statement.ixx diff --git a/odb/oracle/simple-object-statements.txx b/odb/oracle/simple-object-statements.txx index 32be8bd..dc226ed 100644 --- a/odb/oracle/simple-object-statements.txx +++ b/odb/oracle/simple-object-statements.txx @@ -55,7 +55,7 @@ namespace odb id_column_count, object_traits::batch, sizeof (id_image_type), - 0), + status_), od_ (update_image_bind_ + update_column_count) { image_[0].version = 0; // @@ TODO diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index 10fa3a6..e932dab 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -334,7 +334,7 @@ namespace odb } ub4 statement:: - bind_param (bind* b, size_t n, size_t batch, size_t skip) + 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. // @@ -712,7 +712,7 @@ namespace odb // Set array information if we have a batch. // - if (skip != 0) + if (batch != 1) { ub4 s (static_cast (skip)); @@ -1215,6 +1215,143 @@ namespace odb } // + // bulk_statement + // + + bulk_statement:: + ~bulk_statement () {} + + sword bulk_statement:: + execute (size_t n, multiple_exceptions* mex) + { + { + odb::tracer* t; + if ((t = conn_.transaction_tracer ()) || + (t = conn_.tracer ()) || + (t = conn_.database ().tracer ())) + t->execute (conn_, *this); + } + + mex_ = mex; + + OCIError* err (conn_.error_handle ()); + sword r (OCIStmtExecute (conn_.handle (), + stmt_, + err, + static_cast (n), + 0, + 0, + 0, + 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) + { + if (mex_ != 0) + mex_->fatal (true); // An incomplete batch is always fatal. + + return r; + } + + // Initialize the batch status array. + // + if (n_ != 1) + { + sword r; // Our own return code. + + // Clear the status array. + // + 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; + } + } + + return r; + } + + // // generic_statement // @@ -1697,10 +1834,11 @@ namespace odb bool process, binding& param, binding* returning) - : statement (conn, - text, statement_insert, - (process ? ¶m : 0), false), - ret_ (returning), status_ (param.status) + : bulk_statement (conn, + text, statement_insert, + (process ? ¶m : 0), false, + param.batch, param.status), + ret_ (returning) { init (param); } @@ -1711,10 +1849,11 @@ namespace odb bool process, binding& param, binding* returning) - : statement (conn, - text, statement_insert, - (process ? ¶m : 0), false), - ret_ (returning), status_ (param.status) + : bulk_statement (conn, + text, statement_insert, + (process ? ¶m : 0), false, + param.batch, param.status), + ret_ (returning) { init (param); } @@ -1772,129 +1911,14 @@ namespace odb size_t insert_statement:: execute (size_t n, multiple_exceptions* mex) { - { - odb::tracer* t; - if ((t = conn_.transaction_tracer ()) || - (t = conn_.tracer ()) || - (t = conn_.database ().tracer ())) - t->execute (conn_, *this); - } - - mex_ = mex; - OCIError* err (conn_.error_handle ()); - - sword r (OCIStmtExecute (conn_.handle (), - stmt_, - err, - static_cast (n), - 0, - 0, - 0, - 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_); - } + sword r (bulk_statement::execute (n, mex)); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) { - if (mex_ != 0) - mex_->fatal (true); // An incomplete batch is always fatal. - - fetch (r, err); + fetch (r, conn_.error_handle ()); return n_; } - // Initialize the batch status array. - // - if (n_ != 1) - { - // Clear the status array. - // - 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; - } - } - // Store the last returned id size (see odb_oracle_returning_out() // for details). // @@ -1916,7 +1940,7 @@ namespace odb } } - if (n_ == 1) + if (n == 1) // n and n_ are really the same here. fetch (OCI_SUCCESS, 0); else fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); @@ -2059,24 +2083,87 @@ namespace odb delete_statement (connection_type& conn, const string& text, binding& param) - : statement (conn, - text, statement_delete, - 0, false) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.status) { - bind_param (param.bind, param.count); + bind_param (param.bind, param.count, param.batch, param.skip); } delete_statement:: delete_statement (connection_type& conn, const char* text, binding& param) - : statement (conn, - text, statement_delete, - 0, false) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.status) + { + bind_param (param.bind, param.count, param.batch, param.skip); + } + + size_t delete_statement:: + execute (size_t n, multiple_exceptions* mex) + { + sword r (bulk_statement::execute (n, mex)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + { + fetch (r, conn_.error_handle ()); + return n_; + } + + if (n == 1) // n and n_ are really the same here. + fetch (OCI_SUCCESS, 0); + else + fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); + + return n_; + } + + void delete_statement:: + fetch (sword r, OCIError* err1) { - bind_param (param.bind, param.count); + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err1, r, &conn_, i_, mex_); // Can return. + else + { + ub4 rows (0); + OCIError* err (conn_.error_handle ()); + r = OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &rows, + 0, + OCI_ATTR_ROW_COUNT, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + cerr << "fetch: " << rows << endl; + + result_ = static_cast (rows); + } + } + + unsigned long long delete_statement:: + result (std::size_t i) + { + 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_; } + /* unsigned long long delete_statement:: execute () { @@ -2115,5 +2202,6 @@ namespace odb return static_cast (row_count); } + */ } } diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx index 93213d1..5bbd3af 100644 --- a/odb/oracle/statement.hxx +++ b/odb/oracle/statement.hxx @@ -143,6 +143,38 @@ namespace odb std::size_t usize_; }; + class LIBODB_ORACLE_EXPORT bulk_statement: public statement + { + public: + virtual + ~bulk_statement () = 0; + + bulk_statement (connection_type&, + const std::string& text, + statement_kind, + const binding* process, + bool optimize, + std::size_t batch, + auto_handle* status); + + bulk_statement (connection_type&, + const char* text, + statement_kind, + const binding* process, + bool optimize, + std::size_t batch, + auto_handle* status); + + sword + execute (std::size_t n, multiple_exceptions*); + + protected: + auto_handle* status_; // Row status array. + std::size_t n_; // Actual batch size. + std::size_t i_; // Position in result. + multiple_exceptions* mex_; + }; + class LIBODB_ORACLE_EXPORT generic_statement: public statement { public: @@ -253,7 +285,7 @@ namespace odb select_statement& s_; }; - class LIBODB_ORACLE_EXPORT insert_statement: public statement + class LIBODB_ORACLE_EXPORT insert_statement: public bulk_statement { public: virtual @@ -287,6 +319,8 @@ namespace odb insert_statement& operator= (const insert_statement&); /* + @@ RM + // 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. @@ -323,10 +357,6 @@ namespace odb ub4 ret_size_; // You don't want to know (see statement.cxx). private: - multiple_exceptions* mex_; - std::size_t n_; - std::size_t i_; - auto_handle* status_; bool result_; }; @@ -354,7 +384,7 @@ namespace odb update_statement& operator= (const update_statement&); }; - class LIBODB_ORACLE_EXPORT delete_statement: public statement + class LIBODB_ORACLE_EXPORT delete_statement: public bulk_statement { public: virtual @@ -368,16 +398,33 @@ namespace odb const char* text, binding& param); + // Return the number of parameter rows (out of n) that were attempted. + // + std::size_t + execute (std::size_t n = 1, multiple_exceptions* = 0); + + // Return the number of rows affected (deleted) by the parameter + // row. Errors are reported by throwing exceptions. + // unsigned long long - execute (); + result (std::size_t i = 0); private: delete_statement (const delete_statement&); delete_statement& operator= (const delete_statement&); + + private: + void + fetch (sword r, OCIError*); + + private: + unsigned long long result_; }; } } +#include + #include #endif // ODB_ORACLE_STATEMENT_HXX diff --git a/odb/oracle/statement.ixx b/odb/oracle/statement.ixx new file mode 100644 index 0000000..a41da8a --- /dev/null +++ b/odb/oracle/statement.ixx @@ -0,0 +1,37 @@ +// file : odb/oracle/statement.ixx +// copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +using namespace std; + +namespace odb +{ + namespace oracle + { + inline bulk_statement:: + bulk_statement (connection_type& c, + const std::string& text, + statement_kind k, + const binding* process, + bool optimize, + std::size_t batch, + auto_handle* status) + : statement (c, text, k, process, optimize), + status_ (batch == 1 ? 0 : status) + { + } + + inline bulk_statement:: + bulk_statement (connection_type& c, + const char* text, + statement_kind k, + const binding* process, + bool optimize, + std::size_t batch, + auto_handle* status) + : statement (c, text, k, process, optimize), + status_ (batch == 1 ? 0 : status) + { + } + } +} -- cgit v1.1