From 232c3a7eb9c65c066a090e9b53c227e1c51ec4a5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 14 Nov 2014 16:24:50 +0200 Subject: Old interface compatibility and testing fixes Now all tests pass for both Oracle and SQL Server. --- odb/mssql/container-statements.hxx | 3 +- odb/mssql/error.cxx | 2 +- odb/mssql/no-id-object-statements.hxx | 3 +- odb/mssql/polymorphic-object-statements.hxx | 10 +- odb/mssql/section-statements.hxx | 9 +- odb/mssql/section-statements.txx | 3 +- odb/mssql/simple-object-statements.hxx | 51 +++--- odb/mssql/statement.cxx | 239 ++++++++++++++++++++-------- odb/mssql/statement.hxx | 72 ++++++--- odb/mssql/statement.ixx | 24 +-- 10 files changed, 274 insertions(+), 142 deletions(-) diff --git a/odb/mssql/container-statements.hxx b/odb/mssql/container-statements.hxx index 5a7e52c..adc09ac 100644 --- a/odb/mssql/container-statements.hxx +++ b/odb/mssql/container-statements.hxx @@ -124,6 +124,7 @@ namespace odb insert_image_binding_, false, false, + 0, false)); return *insert_; @@ -287,7 +288,7 @@ namespace odb update_text_, this->versioned_, // Process if versioned. update_image_binding_, - false, + 0, false)); return *update_; diff --git a/odb/mssql/error.cxx b/odb/mssql/error.cxx index 23d5be9..f947541 100644 --- a/odb/mssql/error.cxx +++ b/odb/mssql/error.cxx @@ -237,7 +237,7 @@ namespace odb 0, 0); - cerr << i << " "; + cerr << i << " " << sqlstate << " "; // check error if (n == SQL_NO_ROW_NUMBER) diff --git a/odb/mssql/no-id-object-statements.hxx b/odb/mssql/no-id-object-statements.hxx index 981bc9f..dc227f1 100644 --- a/odb/mssql/no-id-object-statements.hxx +++ b/odb/mssql/no-id-object-statements.hxx @@ -89,7 +89,8 @@ namespace odb object_traits::versioned, // Process if versioned. insert_image_binding_, false, - object_traits::rowversion, + false, + 0, false)); return *persist_; diff --git a/odb/mssql/polymorphic-object-statements.hxx b/odb/mssql/polymorphic-object-statements.hxx index 381c607..49b5438 100644 --- a/odb/mssql/polymorphic-object-statements.hxx +++ b/odb/mssql/polymorphic-object-statements.hxx @@ -310,7 +310,7 @@ namespace odb insert_image_binding_, false, false, - false)); + 0)); return *persist_; } @@ -345,7 +345,7 @@ namespace odb object_traits::update_statement, object_traits::versioned, // Process if versioned. update_image_binding_, - false, + 0, false)); return *update_; @@ -373,6 +373,7 @@ namespace odb return extra_statement_cache_.get ( conn_, image_, + id_image (), id_image_binding (), &id_image_binding ()); // Note, not id+version. } @@ -411,8 +412,9 @@ namespace odb root_statements_type& root_statements_; base_statements_type& base_statements_; - extra_statement_cache_ptr - extra_statement_cache_; + extra_statement_cache_ptr extra_statement_cache_; image_type image_; diff --git a/odb/mssql/section-statements.hxx b/odb/mssql/section-statements.hxx index 43919d5..8bb1650 100644 --- a/odb/mssql/section-statements.hxx +++ b/odb/mssql/section-statements.hxx @@ -36,6 +36,7 @@ namespace odb typedef ST traits; typedef typename traits::image_type image_type; + typedef typename traits::id_image_type id_image_type; typedef mssql::select_statement select_statement_type; typedef mssql::update_statement update_statement_type; @@ -43,7 +44,7 @@ namespace odb typedef mssql::connection connection_type; section_statements (connection_type&, - image_type&, + image_type&, id_image_type&, binding& id, binding& idv); connection_type& @@ -61,6 +62,9 @@ namespace odb image_type& image () {return image_;} + id_image_type& + id_image () {return id_image_;} + const binding& id_binding () {return id_binding_;} @@ -128,7 +132,7 @@ namespace odb traits::update_statement, traits::versioned, // Process if versioned. update_image_binding_, - traits::rowversion, + (traits::rowversion ? &idv_binding_ : 0), false)); return *update_; @@ -155,6 +159,7 @@ namespace odb // These come from object_statements. // image_type& image_; + id_image_type& id_image_; binding& id_binding_; binding& idv_binding_; diff --git a/odb/mssql/section-statements.txx b/odb/mssql/section-statements.txx index 358c216..319b0b4 100644 --- a/odb/mssql/section-statements.txx +++ b/odb/mssql/section-statements.txx @@ -11,11 +11,12 @@ namespace odb template section_statements:: section_statements (connection_type& conn, - image_type& im, + image_type& im, id_image_type& idim, binding& id, binding& idv) : conn_ (conn), svm_ (0), image_ (im), + id_image_ (idim), id_binding_ (id), idv_binding_ (idv), select_image_binding_ (select_image_bind_, diff --git a/odb/mssql/simple-object-statements.hxx b/odb/mssql/simple-object-statements.hxx index dbe1f30..2e20bb8 100644 --- a/odb/mssql/simple-object-statements.hxx +++ b/odb/mssql/simple-object-statements.hxx @@ -39,49 +39,56 @@ namespace odb // deleter function which will be initialized during allocation // (at that point we know that the cache class is defined). // - template + template struct extra_statement_cache_ptr { typedef I image_type; + typedef ID id_image_type; typedef mssql::connection connection_type; extra_statement_cache_ptr (): p_ (0) {} ~extra_statement_cache_ptr () { if (p_ != 0) - (this->*deleter_) (0, 0, 0, 0); + (this->*deleter_) (0, 0, 0, 0, 0); } T& - get (connection_type& c, image_type& im, binding& id, binding* idv) + get (connection_type& c, + image_type& im, id_image_type& idim, + binding& id, binding* idv) { if (p_ == 0) - allocate (&c, &im, &id, (idv != 0 ? idv : &id)); + allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id)); return *p_; } private: void - allocate (connection_type*, image_type*, binding*, binding*); + allocate (connection_type*, + image_type*, id_image_type*, + binding*, binding*); private: T* p_; void (extra_statement_cache_ptr::*deleter_) ( - connection_type*, image_type*, binding*, binding*); + connection_type*, image_type*, id_image_type*, binding*, binding*); }; - template - void extra_statement_cache_ptr:: - allocate (connection_type* c, image_type* im, binding* id, binding* idv) + template + void extra_statement_cache_ptr:: + allocate (connection_type* c, + image_type* im, id_image_type* idim, + binding* id, binding* idv) { // To reduce object code size, this function acts as both allocator // and deleter. // if (p_ == 0) { - p_ = new T (*c, *im, *id, *idv); - deleter_ = &extra_statement_cache_ptr::allocate; + p_ = new T (*c, *im, *idim, *id, *idv); + deleter_ = &extra_statement_cache_ptr::allocate; } else delete p_; @@ -339,7 +346,7 @@ namespace odb // at the same time. // binding& - optimistic_id_image_binding () {return od_.id_image_binding_;} + optimistic_id_image_binding () {return *od_.id_image_binding ();} // Statements. // @@ -355,9 +362,9 @@ namespace odb insert_image_binding_, object_traits::auto_id, object_traits::rowversion, - (object_traits::auto_id || object_traits::rowversion - ? &id_image_binding_ - : 0), + (object_traits::rowversion + ? &optimistic_id_image_binding () + : (object_traits::auto_id ? &id_image_binding () : 0)), false)); return *persist_; @@ -391,7 +398,7 @@ namespace odb true, // Unique (0 or 1). object_traits::versioned, // Process if versioned. update_image_binding_, - object_traits::rowversion, + object_traits::rowversion ? &optimistic_id_image_binding () : 0, false)); return *update_; @@ -405,8 +412,8 @@ namespace odb new (details::shared) delete_statement_type ( conn_, object_traits::erase_statement, - id_image_binding_, true, // Unique (0 or 1 affected rows). + id_image_binding_, false)); return *erase_; @@ -422,7 +429,6 @@ namespace odb conn_, object_traits::optimistic_erase_statement, od_.id_image_binding_, - true, // Unique (0 or 1 affected rows). false)); } @@ -435,7 +441,9 @@ namespace odb extra_statement_cache () { return extra_statement_cache_.get ( - conn_, images_[0].obj, id_image_binding_, od_.id_image_binding ()); + conn_, + images_[0].obj, images_[0].id, + id_image_binding_, od_.id_image_binding ()); } public: @@ -482,8 +490,9 @@ namespace odb template friend class polymorphic_derived_object_statements; - extra_statement_cache_ptr - extra_statement_cache_; + extra_statement_cache_ptr extra_statement_cache_; // The UPDATE statement uses both the object and id image. Keep // them next to each other so that the same skip distance can diff --git a/odb/mssql/statement.cxx b/odb/mssql/statement.cxx index 22b1de9..f81bd17 100644 --- a/odb/mssql/statement.cxx +++ b/odb/mssql/statement.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC // license : ODB NCUEL; see accompanying LICENSE file -#include // std::strlen, std::strstr +#include // std::strlen, std::strstr, std::memset #include #include @@ -782,6 +782,44 @@ namespace odb SQLRETURN r (statement::execute ()); bool ok (SQL_SUCCEEDED (r) || r == SQL_NO_DATA); + //cerr << "processed: " << processed_ << endl; + + // If we have a batch of 1 parameter set, SQL Server ODBC driver + // returns the error via SQLExecute() rather than via the status + // array even if we set all the attributes necessary for row-wise + // binding. So what we are going to do here is convert this case + // to the batch way of reporting errors (not that we also check + // processed_ so that we only do this is the parameter set was + // actually attempted). + // + if (!ok && status_ != 0 && n == 1 && processed_ == 1) + { + status_[0] = SQL_PARAM_ERROR; + r = SQL_SUCCESS; + ok = true; + } + + /* + for (unsigned i (0); i != processed_; ++i) + { + cerr << "[" << i << "] " << status_[i] << " "; + + if (status_[i] == SQL_PARAM_ERROR) + cerr << "error "; + else if (status_[i] == SQL_PARAM_SUCCESS || + status_[i] == SQL_PARAM_SUCCESS_WITH_INFO) + cerr << "ok"; + else if (status_[i] == SQL_PARAM_UNUSED) + cerr << "unused"; + else if (status_[i] == SQL_PARAM_DIAG_UNAVAILABLE) + cerr << "unavailable"; + else + cerr << "?"; + + cerr << endl; + } + */ + // If the statement failed as a whole, assume no parameter sets // were attempted in case of a batch. Otherwise, the documentation // says that the native client driver keeps processing remaining @@ -804,27 +842,6 @@ namespace odb return r; } - cerr << "processed: " << processed_ << endl; - - for (unsigned i (0); i != processed_; ++i) - { - cerr << "[" << i << "] " << status_[i] << " "; - - if (status_[i] == SQL_PARAM_ERROR) - cerr << "error "; - else if (status_[i] == SQL_PARAM_SUCCESS || - status_[i] == SQL_PARAM_SUCCESS_WITH_INFO) - cerr << "ok"; - else if (status_[i] == SQL_PARAM_UNUSED) - cerr << "unused"; - else if (status_[i] == SQL_PARAM_DIAG_UNAVAILABLE) - cerr << "unavailable"; - else - cerr << "?"; - - cerr << endl; - } - return r; } @@ -861,13 +878,15 @@ namespace odb if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); + //cerr << "raw total: " << n << endl; + // If all the parameter sets failed, then the returned count is -1, // which means "not available" according to the documentation. // rows = (n != -1 ? static_cast (n) : 0); } - cerr << "total: " << rows << endl; + //cerr << "total: " << rows << endl; if (n_ > 1) // Batch. { @@ -1094,8 +1113,8 @@ namespace odb // for one of the inserted columns is supplied at execution // (long data). // - text_batch_ = strstr (text_, "OUTPUT INSERTED.") == 0 && - strstr (text_, "output inserted.") == 0; + text_batch_ = (strstr (text_, "OUTPUT INSERTED.") == 0 && + strstr (text_, "output inserted.") == 0); SQLRETURN r; SQLUSMALLINT col (1); @@ -1236,6 +1255,7 @@ namespace odb else if (!SQL_SUCCEEDED (r)) continue; + /* // check error if (n == SQL_NO_ROW_NUMBER) cerr << "not associated with any row" << endl; @@ -1243,6 +1263,7 @@ namespace odb cerr << "unable to determine row association" << endl; else cerr << "associated with " << n << endl; + */ if (n == SQL_NO_ROW_NUMBER || n == SQL_ROW_NUMBER_UNKNOWN || @@ -1291,7 +1312,7 @@ namespace odb if (result_) { translate_error (r, conn_, stmt_, i_, mex_); // Can return. - result_ = false; // Prevent id/version extraction below. + result_ = false; // Prevent id/version extraction below or. } } @@ -1320,7 +1341,8 @@ namespace odb if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); - cerr << "fetch [" << i_ << "] " << status_[i_] << " " << endl; + //if (status_ != 0) + // cerr << "fetch [" << i_ << "] " << status_[i_] << " " << endl; if (r == SQL_NO_DATA) throw database_exception ( @@ -1376,7 +1398,12 @@ namespace odb // if ((returning_id_ || returning_version_) && i_ + 1 == n_) { - r = SQLCloseCursor (stmt_); + // Use SQLFreeStmt(SQL_CLOSE) instead of SQLCloseCursor() to avoid + // an error if a cursor is not open. This seem to happen if the + // statement failure was translated to a parameter set failure in + // bulk_statement for batches of one. + // + r = SQLFreeStmt (stmt_, SQL_CLOSE); if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); @@ -1399,24 +1426,54 @@ namespace odb update_statement:: update_statement (connection_type& conn, const string& text, + bool process, + binding& param, + binding* returning) + : bulk_statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + param.batch, param.skip, param.status), + unique_ (false), + returning_ (returning != 0) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + init (param, returning); + } + + update_statement:: + update_statement (connection_type& conn, + const string& text, bool unique, bool process, binding& param, - bool returning_version) + binding* returning) : bulk_statement (conn, text, statement_update, (process ? ¶m : 0), false, param.batch, param.skip, param.status), unique_ (unique), - returning_version_ (returning_version) + returning_ (returning != 0) { - if (!empty ()) - { - bind_param (param.bind, param.count); + init (param, returning); + } - if (returning_version_) - init_result (); - } + update_statement:: + update_statement (connection_type& conn, + const char* text, + bool process, + binding& param, + binding* returning, + bool copy_text) + : bulk_statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + param.batch, param.skip, param.status, + copy_text), + unique_ (false), + returning_ (returning != 0) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + init (param, returning); } update_statement:: @@ -1425,7 +1482,7 @@ namespace odb bool unique, bool process, binding& param, - bool returning_version, + binding* returning, bool copy_text) : bulk_statement (conn, text, statement_update, @@ -1433,30 +1490,33 @@ namespace odb param.batch, param.skip, param.status, copy_text), unique_ (unique), - returning_version_ (returning_version) + returning_ (returning != 0) + { + init (param, returning); + } + + void update_statement:: + init (binding& param, binding* ret) { if (!empty ()) { bind_param (param.bind, param.count); - if (returning_version_) - init_result (); - } - } + if (ret != 0) + { + bind& b (ret->bind[ret->count - 1]); // Version is the last element. - void update_statement:: - init_result () - { - SQLRETURN r ( - SQLBindCol (stmt_, - 1, - SQL_C_BINARY, - (SQLPOINTER) &version_, - sizeof (version_), - &version_size_ind_)); + SQLRETURN r (SQLBindCol (stmt_, + 1, + c_type_lookup[b.type], + (SQLPOINTER) b.buffer, + capacity_lookup[b.type], + b.size_ind)); - if (!SQL_SUCCEEDED (r)) - translate_error (r, conn_, stmt_); + if (!SQL_SUCCEEDED (r)) + translate_error (r, conn_, stmt_); + } + } } size_t update_statement:: @@ -1467,7 +1527,7 @@ namespace odb // are processed inside SQLExecute() and the total count of // affected rows is available after it returns. // - assert (!returning_version_ || n == 1); + assert (!returning_ || status_ == 0); SQLRETURN r (bulk_statement::execute (n, mex)); @@ -1480,29 +1540,24 @@ namespace odb return n_; } - // Extract error information for failed parameter sets. If we do - // this after calling SQLRowCount(), all the diagnostics records - // that we need will be gone. - // - size_t errors (n_ > 1 ? extract_errors () : 0); - - // Figure out the affected row count. - // - result_ = affected (r, errors, unique_); - if (status_ == 0) // Non-batch case. { - // Fetch the row containing the version if this statement is + // Fetch the row containing the data if this statement is // returning. We still need to close the cursor even if we // haven't updated any rows. // - if (returning_version_) + if (returning_) { r = SQLFetch (stmt_); if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); + // We have to get the result after fetching the OUTPUT data + // but before closing the cursor. + // + result_ = affected (SQL_SUCCESS, 0, unique_); + { SQLRETURN r (SQLCloseCursor (stmt_)); // Don't overwrite r. @@ -1516,6 +1571,20 @@ namespace odb "?????", "result set expected from a statement with the OUTPUT clause"); } + else + result_ = affected (r, 0, unique_); + } + else + { + // Extract error information for failed parameter sets. If we do + // this after calling SQLRowCount(), all the diagnostics records + // that we need will be gone. + // + size_t errors (extract_errors ()); + + // Figure out the affected row count. + // + result_ = affected (r, errors, unique_); } return n_; @@ -1544,8 +1613,22 @@ namespace odb delete_statement:: delete_statement (connection_type& conn, const string& text, - binding& param, - bool unique) + binding& param) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.skip, param.status), + unique_ (false) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + bind_param (param.bind, param.count); + } + + delete_statement:: + delete_statement (connection_type& conn, + const string& text, + bool unique, + binding& param) : bulk_statement (conn, text, statement_delete, 0, false, @@ -1559,7 +1642,23 @@ namespace odb delete_statement (connection_type& conn, const char* text, binding& param, + bool copy_text) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.skip, param.status, + copy_text), + unique_ (false) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + bind_param (param.bind, param.count); + } + + delete_statement:: + delete_statement (connection_type& conn, + const char* text, bool unique, + binding& param, bool copy_text) : bulk_statement (conn, text, statement_delete, @@ -1595,7 +1694,7 @@ namespace odb // this after calling SQLRowCount(), all the diagnostics records // that we need will be gone. // - size_t errors (n_ > 1 ? extract_errors () : 0); + size_t errors (status_ != 0 ? extract_errors () : 0); // Figure out the affected row count. // diff --git a/odb/mssql/statement.hxx b/odb/mssql/statement.hxx index 158964d..4bce464 100644 --- a/odb/mssql/statement.hxx +++ b/odb/mssql/statement.hxx @@ -319,13 +319,20 @@ namespace odb // Return the number of parameter sets (out of n) that were attempted. // std::size_t - execute (std::size_t n = 1, multiple_exceptions* = 0); + execute (std::size_t n, multiple_exceptions*); // Return true if successful and false if this row is a duplicate. // All other errors are reported by throwing exceptions. // bool - result (std::size_t i = 0); + result (std::size_t i); + + bool + execute () + { + execute (1, 0); + return result (0); + } private: insert_statement (const insert_statement&); @@ -371,23 +378,36 @@ namespace odb // update_statement (connection_type& conn, const std::string& text, + bool process, + binding& param, + binding* returning); + + update_statement (connection_type& conn, + const std::string& text, bool unique_hint, bool process, binding& param, - bool returning_version); + binding* returning); + + update_statement (connection_type& conn, + const char* text, + bool process, + binding& param, + binding* returning, + bool copy_text = true); update_statement (connection_type& conn, const char* text, bool unique_hint, bool process, binding& param, - bool returning_version, + binding* returning, bool copy_text = true); // Return the number of parameter sets (out of n) that were attempted. // std::size_t - execute (std::size_t n = 1, multiple_exceptions* = 0); + execute (std::size_t n, multiple_exceptions*); // Return the number of rows affected (deleted) by the parameter // set. If this is a batch (n > 1 in execute() call above) and it @@ -398,13 +418,14 @@ namespace odb using bulk_statement::result_unknown; unsigned long long - result (std::size_t i = 0); + result (std::size_t i); - // Note that currently the implementation does not support batch - // with the OUTPUT clause. - // unsigned long long - version (); + execute () + { + execute (1, 0); + return result (0); + } private: update_statement (const update_statement&); @@ -412,16 +433,13 @@ namespace odb private: void - init_result (); + init (binding& param, binding* ret); private: bool unique_; - bool returning_version_; + bool returning_; unsigned long long result_; - - unsigned char version_[8]; - SQLLEN version_size_ind_; }; class LIBODB_MSSQL_EXPORT delete_statement: public bulk_statement @@ -449,19 +467,28 @@ namespace odb // delete_statement (connection_type& conn, const std::string& text, + binding& param); + + delete_statement (connection_type& conn, + const std::string& text, + bool unique_hint, + binding& param); + + delete_statement (connection_type& conn, + const char* text, binding& param, - bool unique_hint = false); + bool copy_text = true); delete_statement (connection_type& conn, const char* text, + bool unique_hint, binding& param, - bool unique_hint = false, bool copy_text = true); // Return the number of parameter sets (out of n) that were attempted. // std::size_t - execute (std::size_t n = 1, multiple_exceptions* = 0); + execute (std::size_t n, multiple_exceptions*); // Return the number of rows affected (deleted) by the parameter // set. If this is a batch (n > 1 in execute() call above) and it @@ -472,7 +499,14 @@ namespace odb using bulk_statement::result_unknown; unsigned long long - result (std::size_t i = 0); + result (std::size_t i); + + unsigned long long + execute () + { + execute (1, 0); + return result (0); + } private: delete_statement (const delete_statement&); diff --git a/odb/mssql/statement.ixx b/odb/mssql/statement.ixx index 642a5d7..da9fbd8 100644 --- a/odb/mssql/statement.ixx +++ b/odb/mssql/statement.ixx @@ -18,7 +18,7 @@ namespace odb : statement (c, text, k, process, optimize), status_ (batch == 1 ? 0 : status) { - if (batch != 1 && !empty ()) + if (status_ != 0 && !empty ()) init (skip); } @@ -35,28 +35,8 @@ namespace odb : statement (c, text, k, process, optimize, copy_text), status_ (batch == 1 ? 0 : status) { - if (batch != 1 && !empty ()) + if (status_ != 0 && !empty ()) init (skip); } - - inline unsigned long long update_statement:: - version () - { - unsigned long long r; - - // The value is in the big-endian format. - // - unsigned char* p (reinterpret_cast (&r)); - p[0] = version_[7]; - p[1] = version_[6]; - p[2] = version_[5]; - p[3] = version_[4]; - p[4] = version_[3]; - p[5] = version_[2]; - p[6] = version_[1]; - p[7] = version_[0]; - - return r; - } } } -- cgit v1.1