From 2ea4bcebf8efa0a2f77d65c82e2df4b4f04b1f76 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 19 Nov 2014 11:51:17 +0200 Subject: Implement bulk API code generation --- odb/mssql/simple-object-statements.hxx | 4 +- odb/mssql/simple-object-statements.txx | 11 +++- odb/mssql/statement.cxx | 102 +++++++++++++++++++++++++-------- odb/mssql/statement.hxx | 28 +++++++-- 4 files changed, 112 insertions(+), 33 deletions(-) diff --git a/odb/mssql/simple-object-statements.hxx b/odb/mssql/simple-object-statements.hxx index 2e20bb8..77b1c34 100644 --- a/odb/mssql/simple-object-statements.hxx +++ b/odb/mssql/simple-object-statements.hxx @@ -162,7 +162,7 @@ namespace odb typedef T object_type; typedef object_traits_impl object_traits; - optimistic_data (bind*); + optimistic_data (bind*, std::size_t skip, SQLUSMALLINT* status); binding* id_image_binding () {return &id_image_binding_;} @@ -177,7 +177,7 @@ namespace odb template struct optimistic_data { - optimistic_data (bind*) {} + optimistic_data (bind*, std::size_t, SQLUSMALLINT*) {} binding* id_image_binding () {return 0;} diff --git a/odb/mssql/simple-object-statements.txx b/odb/mssql/simple-object-statements.txx index a0626fa..1cdffb2 100644 --- a/odb/mssql/simple-object-statements.txx +++ b/odb/mssql/simple-object-statements.txx @@ -20,11 +20,14 @@ namespace odb template optimistic_data:: - optimistic_data (bind* b) + optimistic_data (bind* b, std::size_t skip, SQLUSMALLINT* status) : id_image_binding_ ( b, object_traits::id_column_count + - object_traits::managed_optimistic_column_count) + object_traits::managed_optimistic_column_count, + object_traits::batch, + skip, + status) { } @@ -59,7 +62,9 @@ namespace odb object_traits::batch, sizeof (images), status_), - od_ (update_image_bind_ + update_column_count) + od_ (update_image_bind_ + update_column_count, + sizeof (images), + status_) { images_[0].obj.version = 0; // @@ TODO [0] images_[0].id.version = 0; // @@ TODO diff --git a/odb/mssql/statement.cxx b/odb/mssql/statement.cxx index 027b0c3..695d97a 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, std::memset +#include // std::strlen, std::strstr, std::memset, std::memcpy #include #include //@@ tmp @@ -1073,12 +1073,14 @@ namespace odb text, statement_insert, (process ? ¶m : 0), false, param.batch, param.skip, param.status), - returning_id_ (returning_id), returning_version_ (returning_version) + returning_id_ (returning_id), + returning_version_ (returning_version), + ret_ (returning) { bind_param (param.bind, param.count); - if (returning_id_ || returning_version_) - init_result (*returning); + if (ret_ != 0) + init_result (); } insert_statement:: @@ -1095,16 +1097,26 @@ namespace odb (process ? ¶m : 0), false, param.batch, param.skip, param.status, copy_text), - returning_id_ (returning_id), returning_version_ (returning_version) + returning_id_ (returning_id), + returning_version_ (returning_version), + ret_ (returning) { bind_param (param.bind, param.count); - if (returning_id_ || returning_version_) - init_result (*returning); + if (ret_ != 0) + init_result (); + } + + template + inline T* + offset (T* base, size_t count, size_t size) + { + return reinterpret_cast ( + reinterpret_cast (base) + count * size); } void insert_statement:: - init_result (binding& ret) + init_result () { // Figure out if we are using the OUTPUT clause or a batch of // INSERT and SELECT statements. The latter is used to work @@ -1116,19 +1128,36 @@ namespace odb text_batch_ = (strstr (text_, "OUTPUT INSERTED.") == 0 && strstr (text_, "output inserted.") == 0); + // It might seem logical to set up the array of results if this is a + // batch (i.e., the SQL_ATTR_ROW_BIND_TYPE, SQL_ATTR_ROW_ARRAY_SIZE). + // This won't work because what we are getting is multiple result + // sets (each containing a single row) and not multiple rows. As a + // result, the SQL Server ODBC driver will always store the data in + // the first element of our array. A bit counter-intuitive. + // + // At the same time it would be conceptually cleaner to have the + // returned data extracted into the batch array instead of always + // the first element. This is also how other database runtimes (e.g., + // Oracle) behave. So what we are going to do here is emulate this + // by making the ODBC driver store the data into the last element + // of the batch array and then copying it into the right place + // after processing each result set (see fetch() below). + // SQLRETURN r; SQLUSMALLINT col (1); + size_t last (ret_->batch - 1); + if (returning_id_) { - bind& b (ret.bind[0]); // Auto id is the first element. + bind& b (ret_->bind[0]); // Auto id is the first element. r = SQLBindCol (stmt_, col++, c_type_lookup[b.type], - (SQLPOINTER) b.buffer, + (SQLPOINTER) offset (b.buffer, last, ret_->skip), capacity_lookup[b.type], - b.size_ind); + offset (b.size_ind, last, ret_->skip)); if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); @@ -1136,25 +1165,18 @@ namespace odb if (returning_version_) { - bind& b (ret.bind[ret.count - 1]); // Version is the last element. + bind& b (ret_->bind[ret_->count - 1]); // Version is the last element. r = SQLBindCol (stmt_, col++, c_type_lookup[b.type], - (SQLPOINTER) b.buffer, + (SQLPOINTER) offset (b.buffer, last, ret_->skip), capacity_lookup[b.type], - b.size_ind); + offset (b.size_ind, last, ret_->skip)); if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); } - - // It might seem logical to set up the array of results if this is a - // batch (i.e., the SQL_ATTR_ROW_BIND_TYPE, SQL_ATTR_ROW_ARRAY_SIZE). - // This won't work because what we are getting is multiple result - // sets (each containing a single row) and not multiple rows. As a - // result, the SQL Server ODBC driver will always store the data in - // the first element of our array. A bit counter-intuitive. } size_t insert_statement:: @@ -1319,7 +1341,7 @@ namespace odb // Fetch the row containing the id/version if this statement is // returning. // - if (result_ && (returning_id_ || returning_version_)) + if (result_ && ret_ != 0) { if (text_batch_) { @@ -1349,6 +1371,38 @@ namespace odb 0, "?????", "result set expected from a statement with the OUTPUT clause"); + + // See init_result() for details on what's going here. + // + size_t last (ret_->batch - 1); + if (i_ != last) + { + if (returning_id_) + { + bind& b (ret_->bind[0]); // Auto id is the first element. + + memcpy (offset (b.buffer, i_, ret_->skip), + offset (b.buffer, last, ret_->skip), + capacity_lookup[b.type]); + + memcpy (offset (b.size_ind, i_, ret_->skip), + offset (b.size_ind, last, ret_->skip), + sizeof (*b.size_ind)); + } + + if (returning_version_) + { + bind& b (ret_->bind[ret_->count - 1]); // Version is the last. + + memcpy (offset (b.buffer, i_, ret_->skip), + offset (b.buffer, last, ret_->skip), + capacity_lookup[b.type]); + + memcpy (offset (b.size_ind, i_, ret_->skip), + offset (b.size_ind, last, ret_->skip), + sizeof (*b.size_ind)); + } + } } } @@ -1367,7 +1421,7 @@ namespace odb // Only in case of the OUTPUT clause do we have multiple result sets. // - if (returning_id_ || returning_version_) + if (ret_ != 0) { r = SQLMoreResults (stmt_); @@ -1396,7 +1450,7 @@ namespace odb // Close the cursor if we are done. // - if ((returning_id_ || returning_version_) && i_ + 1 == n_) + if (ret_ != 0 && i_ + 1 == n_) { // Use SQLFreeStmt(SQL_CLOSE) instead of SQLCloseCursor() to avoid // an error if a cursor is not open. This seem to happen if the diff --git a/odb/mssql/statement.hxx b/odb/mssql/statement.hxx index 4bce464..516baed 100644 --- a/odb/mssql/statement.hxx +++ b/odb/mssql/statement.hxx @@ -319,7 +319,10 @@ namespace odb // Return the number of parameter sets (out of n) that were attempted. // std::size_t - execute (std::size_t n, multiple_exceptions*); + execute (std::size_t n, multiple_exceptions& mex) + { + return execute (n, &mex); + } // Return true if successful and false if this row is a duplicate. // All other errors are reported by throwing exceptions. @@ -340,7 +343,10 @@ namespace odb private: void - init_result (binding&); + init_result (); + + std::size_t + execute (std::size_t, multiple_exceptions*); void fetch (SQLRETURN); @@ -348,6 +354,7 @@ namespace odb private: bool returning_id_; bool returning_version_; + binding* ret_; bool text_batch_; bool result_; @@ -407,7 +414,10 @@ namespace odb // Return the number of parameter sets (out of n) that were attempted. // std::size_t - execute (std::size_t n, multiple_exceptions*); + execute (std::size_t n, multiple_exceptions& mex) + { + return execute (n, &mex); + } // Return the number of rows affected (deleted) by the parameter // set. If this is a batch (n > 1 in execute() call above) and it @@ -435,6 +445,9 @@ namespace odb void init (binding& param, binding* ret); + std::size_t + execute (std::size_t, multiple_exceptions*); + private: bool unique_; bool returning_; @@ -488,7 +501,10 @@ namespace odb // Return the number of parameter sets (out of n) that were attempted. // std::size_t - execute (std::size_t n, multiple_exceptions*); + execute (std::size_t n, multiple_exceptions& mex) + { + return execute (n, &mex); + } // Return the number of rows affected (deleted) by the parameter // set. If this is a batch (n > 1 in execute() call above) and it @@ -513,6 +529,10 @@ namespace odb delete_statement& operator= (const delete_statement&); private: + std::size_t + execute (std::size_t, multiple_exceptions*); + + private: bool unique_; unsigned long long result_; }; -- cgit v1.1