aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2014-11-19 11:51:17 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2014-11-19 11:51:17 +0200
commit2ea4bcebf8efa0a2f77d65c82e2df4b4f04b1f76 (patch)
tree8d4003cd9c92f91bafe9a83f911a7557d27ad588
parent887039bafb6843fa82b800b351c34a479f36b51a (diff)
Implement bulk API code generation
-rw-r--r--odb/mssql/simple-object-statements.hxx4
-rw-r--r--odb/mssql/simple-object-statements.txx11
-rw-r--r--odb/mssql/statement.cxx102
-rw-r--r--odb/mssql/statement.hxx28
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_type, id_mssql> 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 <typename T>
struct optimistic_data<T, false>
{
- 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 <typename T>
optimistic_data<T, true>::
- 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 <cstring> // std::strlen, std::strstr, std::memset
+#include <cstring> // std::strlen, std::strstr, std::memset, std::memcpy
#include <cassert>
#include <iostream> //@@ tmp
@@ -1073,12 +1073,14 @@ namespace odb
text, statement_insert,
(process ? &param : 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 ? &param : 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 <typename T>
+ inline T*
+ offset (T* base, size_t count, size_t size)
+ {
+ return reinterpret_cast<T*> (
+ reinterpret_cast<char*> (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_;
};