From 58c6088dc3c75f0fd631714541349068f5986239 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 11 Nov 2014 10:19:23 +0200 Subject: Complete initial bulk erase() implementation --- odb/oracle/simple-object-statements.hxx | 6 +- odb/oracle/statement.cxx | 142 ++++++++++++++++---------------- odb/oracle/statement.hxx | 36 ++++++-- 3 files changed, 104 insertions(+), 80 deletions(-) diff --git a/odb/oracle/simple-object-statements.hxx b/odb/oracle/simple-object-statements.hxx index 2b550e5..de82c53 100644 --- a/odb/oracle/simple-object-statements.hxx +++ b/odb/oracle/simple-object-statements.hxx @@ -397,7 +397,8 @@ namespace odb new (details::shared) delete_statement_type ( conn_, object_traits::erase_statement, - id_image_binding_)); + id_image_binding_, + true)); // Unique (0 or 1 affected rows). return *erase_; } @@ -411,7 +412,8 @@ namespace odb new (details::shared) delete_statement_type ( conn_, object_traits::optimistic_erase_statement, - od_.id_image_binding_)); + od_.id_image_binding_, + true)); // Unique (0 or 1 affected rows). } return *od_.erase_; diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index e932dab..5ca1a98 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -1244,12 +1244,12 @@ namespace odb 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). + // If the statement failed as a whole, assume no parameter sets + // were attempted. Otherwise, in the batch errors mode, all the + // sets are always attempted (let's hope this is actually true). // i_ = 0; - n_ = (r == OCI_ERROR || r == OCI_INVALID_HANDLE ? 1 : n); + n_ = (r == OCI_ERROR || r == OCI_INVALID_HANDLE ? 0 : n); if (mex_ != 0) { @@ -1913,6 +1913,9 @@ namespace odb { sword r (bulk_statement::execute (n, mex)); + // Statement failed as a whole, assume no parameter sets were + // attempted. + // if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) { fetch (r, conn_.error_handle ()); @@ -1926,7 +1929,7 @@ namespace odb { size_t n (n_); - // Find the index of the last sucessefully processed row. + // Find the index of the last sucessefully processed parameter set. // if (n != 1) for (; n != 0 && status_[n - 1] != 0; --n) ; @@ -1981,11 +1984,11 @@ namespace odb { assert ((i_ == i || i_ + 1 == i) && i < n_); - // Get to the next row if necessary. + // Get to the next parameter set if necessary. // if (i != i_) { - mex_->current (++i_); // It cannot be NULL since this is a batch. + mex_->current (++i_); // mex cannot be NULL since this is a batch. fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); } @@ -2082,11 +2085,13 @@ namespace odb delete_statement:: delete_statement (connection_type& conn, const string& text, - binding& param) + binding& param, + bool unique) : bulk_statement (conn, text, statement_delete, 0, false, - param.batch, param.status) + param.batch, param.status), + unique_ (unique) { bind_param (param.bind, param.count, param.batch, param.skip); } @@ -2094,11 +2099,13 @@ namespace odb delete_statement:: delete_statement (connection_type& conn, const char* text, - binding& param) + binding& param, + bool unique) : bulk_statement (conn, text, statement_delete, 0, false, - param.batch, param.status) + param.batch, param.status), + unique_ (unique) { bind_param (param.bind, param.count, param.batch, param.skip); } @@ -2107,30 +2114,21 @@ namespace odb execute (size_t n, multiple_exceptions* mex) { sword r (bulk_statement::execute (n, mex)); + OCIError* err (conn_.error_handle ()); + // Statement failed as a whole, assume no parameter sets were + // attempted. + // if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) { - fetch (r, conn_.error_handle ()); + fetch (r, err); 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) - { - if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err1, r, &conn_, i_, mex_); // Can return. - else + // Figure out the affected row count. + // { ub4 rows (0); - OCIError* err (conn_.error_handle ()); r = OCIAttrGet (stmt_, OCI_HTYPE_STMT, &rows, @@ -2141,67 +2139,65 @@ namespace odb 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_); + cerr << "total: " << result_ << endl; - // Get to the next row if necessary. - // - if (i != i_) + if (n_ > 1) // Batch. { - mex_->current (++i_); // It cannot be NULL since this is a batch. - fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); - } + if (result_ != 0) // Some rows did get affected. + { + size_t p (n_); - return result_; - } + // Subtract the parameter sets that failed since they haven't + // affected any rows. + // + for (size_t i (0); i != n_; ++i) + if (status_[i_] != 0) + p--; - /* - unsigned long long delete_statement:: - execute () - { - { - odb::tracer* t; - if ((t = conn_.transaction_tracer ()) || - (t = conn_.tracer ()) || - (t = conn_.database ().tracer ())) - t->execute (conn_, *this); + if (p > 1) // True batch. + { + if (unique_) // Each can affect 0 or 1 row. + { + result_ = (p == static_cast (result_) + ? 1 + : result_unknown); + } + else + result_ = result_unknown; + } + } } - OCIError* err (conn_.error_handle ()); + 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_]); - sword r (OCIStmtExecute (conn_.handle (), - stmt_, - err, - 1, - 0, - 0, - 0, - OCI_DEFAULT)); + return n_; + } + void delete_statement:: + fetch (sword r, OCIError* err) + { if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (conn_, r); + translate_error (err, r, &conn_, i_, mex_); // Can return. + } - ub4 row_count (0); - r = OCIAttrGet (stmt_, - OCI_HTYPE_STMT, - &row_count, - 0, - OCI_ATTR_ROW_COUNT, - err); + unsigned long long delete_statement:: + result (std::size_t i) + { + assert ((i_ == i || i_ + 1 == i) && i < n_); - if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + if (i != i_) + { + mex_->current (++i_); // mex cannot be NULL since this is a batch. + fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); + } - return static_cast (row_count); + return result_; } - */ } } diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx index 5bbd3af..6520c31 100644 --- a/odb/oracle/statement.hxx +++ b/odb/oracle/statement.hxx @@ -165,6 +165,9 @@ namespace odb std::size_t batch, auto_handle* status); + // Call OCIStmtExecute() and set up the batch tracking variables (see + // below). + // sword execute (std::size_t n, multiple_exceptions*); @@ -303,7 +306,7 @@ namespace odb binding& param, binding* returning); - // Return the number of rows (out of n) that were attempted. + // Return the number of parameter sets (out of n) that were attempted. // std::size_t execute (std::size_t n = 1, multiple_exceptions* = 0); @@ -390,22 +393,44 @@ namespace odb virtual ~delete_statement (); + // OCI does not expose individual affected row counts for batch + // operations. Instead, it adds them all up and returns a single + // count. This is bad news for us. + // + // In case of deleting by primary key (the affected row count is + // either 1 or 0), we can recognize the presumably successful case + // where the total affected row count is equal to the batch size + // (we can also recognize the "all unsuccessful" case where the + // total affected row count is 0). The unique_hint argument in the + // constructors below indicates whether this is a "0 or 1" DELETE + // statement. + // + // In all other situations (provided this is a batch), the result() + // function below returns the special result_unknown value. + // delete_statement (connection_type& conn, const std::string& text, - binding& param); + binding& param, + bool unique_hint = false); delete_statement (connection_type& conn, const char* text, - binding& param); + binding& param, + bool unique_hint = false); - // Return the number of parameter rows (out of n) that were attempted. + // Return the number of parameter sets (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. + // set. If this is a batch (n > 1 in execute() call above) and it + // is impossible to determine the affected row count for each + // parameter set, then this function returns result_unknown. All + // other errors are reported by throwing exceptions. // + static const unsigned long long result_unknown = ~0ULL; + unsigned long long result (std::size_t i = 0); @@ -418,6 +443,7 @@ namespace odb fetch (sword r, OCIError*); private: + bool unique_; unsigned long long result_; }; } -- cgit v1.1