From 5e113195591a9f8dccbb8f5f743cdd7776906b08 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 14 Jan 2013 16:01:06 +0200 Subject: Add support for MSSQL ROWVERSION ODB can now use ROWVERSION column as an optimistic concurrency version. --- odb/mssql/container-statements.hxx | 2 +- odb/mssql/no-id-object-statements.hxx | 3 +- odb/mssql/polymorphic-object-statements.hxx | 6 +- odb/mssql/simple-object-statements.hxx | 6 +- odb/mssql/statement.cxx | 116 +++++++++++++++++++++++----- odb/mssql/statement.hxx | 35 ++++++++- odb/mssql/statement.ixx | 49 ++++++++++++ 7 files changed, 188 insertions(+), 29 deletions(-) create mode 100644 odb/mssql/statement.ixx diff --git a/odb/mssql/container-statements.hxx b/odb/mssql/container-statements.hxx index 62ee3a2..dcf8e5c 100644 --- a/odb/mssql/container-statements.hxx +++ b/odb/mssql/container-statements.hxx @@ -169,7 +169,7 @@ namespace odb if (insert_one_ == 0) insert_one_.reset ( new (details::shared) insert_statement_type ( - conn_, insert_one_text_, data_image_binding_, false)); + conn_, insert_one_text_, data_image_binding_, false, false)); return *insert_one_; } diff --git a/odb/mssql/no-id-object-statements.hxx b/odb/mssql/no-id-object-statements.hxx index 8eae7c7..30ba999 100644 --- a/odb/mssql/no-id-object-statements.hxx +++ b/odb/mssql/no-id-object-statements.hxx @@ -87,7 +87,8 @@ namespace odb conn_, object_traits::persist_statement, insert_image_binding_, - false)); + false, + object_traits::rowversion)); return *persist_; } diff --git a/odb/mssql/polymorphic-object-statements.hxx b/odb/mssql/polymorphic-object-statements.hxx index bc58f6f..c121168 100644 --- a/odb/mssql/polymorphic-object-statements.hxx +++ b/odb/mssql/polymorphic-object-statements.hxx @@ -277,12 +277,15 @@ namespace odb insert_statement_type& persist_statement () { + // Auto id and version are in the root. + // if (persist_ == 0) persist_.reset ( new (details::shared) insert_statement_type ( conn_, object_traits::persist_statement, insert_image_binding_, + false, false)); return *persist_; @@ -313,7 +316,8 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, - update_image_binding_)); + update_image_binding_, + false)); return *update_; } diff --git a/odb/mssql/simple-object-statements.hxx b/odb/mssql/simple-object-statements.hxx index fc46f5b..a1f4503 100644 --- a/odb/mssql/simple-object-statements.hxx +++ b/odb/mssql/simple-object-statements.hxx @@ -343,7 +343,8 @@ namespace odb conn_, object_traits::persist_statement, insert_image_binding_, - object_traits::auto_id)); + object_traits::auto_id, + object_traits::rowversion)); return *persist_; } @@ -370,7 +371,8 @@ namespace odb new (details::shared) update_statement_type ( conn_, object_traits::update_statement, - update_image_binding_)); + update_image_binding_, + object_traits::rowversion)); return *update_; } diff --git a/odb/mssql/statement.cxx b/odb/mssql/statement.cxx index 524fc06..a0371b5 100644 --- a/odb/mssql/statement.cxx +++ b/odb/mssql/statement.cxx @@ -795,12 +795,15 @@ namespace odb insert_statement (connection_type& conn, const string& t, binding& param, - bool returning) - : statement (conn, t), returning_ (returning) + bool returning_id, + bool returning_version) + : statement (conn, t), + returning_id_ (returning_id), + returning_version_ (returning_version) { bind_param (param.bind, param.count); - if (returning) + if (returning_id_ || returning_version_) init_result (); } @@ -808,13 +811,16 @@ namespace odb insert_statement (connection_type& conn, const char* t, binding& param, - bool returning, + bool returning_id, + bool returning_version, bool ct) - : statement (conn, t, ct), returning_ (returning) + : statement (conn, t, ct), + returning_id_ (returning_id), + returning_version_ (returning_version) { bind_param (param.bind, param.count); - if (returning) + if (returning_id_ || returning_version_) init_result (); } @@ -831,16 +837,35 @@ namespace odb batch_ = strstr (text_, "OUTPUT INSERTED.") == 0 && strstr (text_, "output inserted.") == 0; - SQLRETURN r ( - SQLBindCol (stmt_, - 1, - SQL_C_SBIGINT, - (SQLPOINTER) &id_, - sizeof (id_), - &id_size_ind_)); + SQLUSMALLINT col (1); - if (!SQL_SUCCEEDED (r)) - translate_error (r, conn_, stmt_); + if (returning_id_) + { + SQLRETURN r ( + SQLBindCol (stmt_, + col++, + SQL_C_SBIGINT, + (SQLPOINTER) &id_, + sizeof (id_), + &id_size_ind_)); + + if (!SQL_SUCCEEDED (r)) + translate_error (r, conn_, stmt_); + } + + if (returning_version_) + { + SQLRETURN r ( + SQLBindCol (stmt_, + col++, + SQL_C_BINARY, + (SQLPOINTER) &version_, + sizeof (version_), + &version_size_ind_)); + + if (!SQL_SUCCEEDED (r)) + translate_error (r, conn_, stmt_); + } } bool insert_statement:: @@ -902,9 +927,10 @@ namespace odb translate_error (r, conn_, stmt_); } - // Fetch the row containing the id if this statement is returning. + // Fetch the row containing the id/version if this statement is + // returning. // - if (returning_) + if (returning_id_ || returning_version_) { if (batch_) { @@ -953,20 +979,46 @@ namespace odb } update_statement:: - update_statement (connection_type& conn, const string& t, binding& param) - : statement (conn, t) + update_statement (connection_type& conn, + const string& t, + binding& param, + bool returning_version) + : statement (conn, t), returning_version_ (returning_version) { bind_param (param.bind, param.count); + + if (returning_version_) + init_result (); } update_statement:: update_statement (connection_type& conn, const char* t, binding& param, + bool returning_version, bool ct) - : statement (conn, t, ct) + : statement (conn, t, ct), + returning_version_ (returning_version) { bind_param (param.bind, param.count); + + if (returning_version_) + init_result (); + } + + void update_statement:: + init_result () + { + SQLRETURN r ( + SQLBindCol (stmt_, + 1, + SQL_C_BINARY, + (SQLPOINTER) &version_, + sizeof (version_), + &version_size_ind_)); + + if (!SQL_SUCCEEDED (r)) + translate_error (r, conn_, stmt_); } unsigned long long update_statement:: @@ -990,6 +1042,30 @@ namespace odb if (!SQL_SUCCEEDED (r)) translate_error (r, conn_, stmt_); + // Fetch the row containing the id/version if this statement is + // returning. + // + if (returning_version_) + { + r = SQLFetch (stmt_); + + if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r)) + translate_error (r, conn_, stmt_); + + { + SQLRETURN r (SQLCloseCursor (stmt_)); // Don't overwrite r. + + if (!SQL_SUCCEEDED (r)) + translate_error (r, conn_, stmt_); + } + + if (r == SQL_NO_DATA) + throw database_exception ( + 0, + "?????", + "result set expected from a statement with the OUTPUT clause"); + } + return static_cast (rows); } diff --git a/odb/mssql/statement.hxx b/odb/mssql/statement.hxx index 4b54097..e51cd3c 100644 --- a/odb/mssql/statement.hxx +++ b/odb/mssql/statement.hxx @@ -200,12 +200,14 @@ namespace odb insert_statement (connection_type& conn, const std::string& text, binding& param, - bool returning); + bool returning_id, + bool returning_version); insert_statement (connection_type& conn, const char* text, binding& param, - bool returning, + bool returning_id, + bool returning_version, bool copy_text = true); // Return true if successful and false if the row is a duplicate. @@ -220,6 +222,9 @@ namespace odb return id_; } + unsigned long long + version (); + private: insert_statement (const insert_statement&); insert_statement& operator= (const insert_statement&); @@ -229,10 +234,15 @@ namespace odb init_result (); private: - bool returning_; + bool returning_id_; + bool returning_version_; bool batch_; + unsigned long long id_; SQLLEN id_size_ind_; + + unsigned char version_[8]; + SQLLEN version_size_ind_; }; class LIBODB_MSSQL_EXPORT update_statement: public statement @@ -243,19 +253,34 @@ namespace odb update_statement (connection_type& conn, const std::string& text, - binding& param); + binding& param, + bool returning_version); update_statement (connection_type& conn, const char* text, binding& param, + bool returning_version, bool copy_text = true); unsigned long long execute (); + unsigned long long + version (); + private: update_statement (const update_statement&); update_statement& operator= (const update_statement&); + + private: + void + init_result (); + + private: + bool returning_version_; + + unsigned char version_[8]; + SQLLEN version_size_ind_; }; class LIBODB_MSSQL_EXPORT delete_statement: public statement @@ -283,6 +308,8 @@ namespace odb } } +#include + #include #endif // ODB_MSSQL_STATEMENT_HXX diff --git a/odb/mssql/statement.ixx b/odb/mssql/statement.ixx new file mode 100644 index 0000000..d27e961 --- /dev/null +++ b/odb/mssql/statement.ixx @@ -0,0 +1,49 @@ +// file : odb/mssql/statement.ixx +// copyright : Copyright (c) 2005-2012 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace mssql + { + inline unsigned long long insert_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; + } + + 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