From 0e6be616e40c86ff03df7c0806bcbd0cc3c4489f 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/relational/mssql/header.cxx | 32 +++++++++ odb/relational/mssql/source.cxx | 143 +++++++++++++++++++++++++++++++++++----- odb/relational/mysql/source.cxx | 12 ++-- odb/relational/source.cxx | 26 ++++++-- odb/relational/source.hxx | 40 ++++++++--- 5 files changed, 214 insertions(+), 39 deletions(-) diff --git a/odb/relational/mssql/header.cxx b/odb/relational/mssql/header.cxx index 957f880..54bb6e1 100644 --- a/odb/relational/mssql/header.cxx +++ b/odb/relational/mssql/header.cxx @@ -15,6 +15,38 @@ namespace relational { namespace relational = relational::header; + struct class1: relational::class1, context + { + class1 (base const& x): base (x) {} + + virtual void + object_public_extra_pre (type& c) + { + bool abst (abstract (c)); + + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + + if (poly_derived || (abst && !poly)) + return; + + // rowvesion + // + bool rv (false); + if (semantics::data_member* m = optimistic (c)) + { + sql_type t (parse_sql_type (column_type (*m), *m)); + rv = (t.type == sql_type::ROWVERSION); + } + + os << "static const bool rowversion = " << + (rv ? "true" : "false") << ";" + << endl; + } + }; + entry class1_entry_; + struct image_type: relational::image_type, context { image_type (base const& x): base (x) {}; diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx index 5d0c594..5d29b27 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/relational/mssql/source.cxx @@ -37,24 +37,49 @@ namespace relational { object_columns (base const& x): base (x) {} - virtual void + virtual bool column (semantics::data_member& m, string const& table, string const& column) { // Don't add a column for auto id in the INSERT statement. // - if (!(sk_ == statement_insert && - key_prefix_.empty () && - context::id (m) && auto_(m))) // Only simple id can be auto. + if (sk_ == statement_insert && + key_prefix_.empty () && + context::id (m) && auto_(m)) // Only simple id can be auto. + return false; + + // Don't update the ROWVERSION column explicitly. + // + if (sk_ == statement_update) { - base::column (m, table, column); + sql_type t (parse_sql_type (column_type (), m)); + if (t.type == sql_type::ROWVERSION) + return false; } + + return base::column (m, table, column); } }; entry object_columns_; // + // + struct persist_statement_params: relational::persist_statement_params, + context + { + persist_statement_params (base const& x): base (x) {} + + virtual string + version_value (semantics::data_member& m) + { + sql_type t (parse_sql_type (column_type (), m)); + return t.type == sql_type::ROWVERSION ? "DEFAULT" : "1"; + } + }; + entry persist_statement_params_; + + // // bind // @@ -863,20 +888,32 @@ namespace relational relational::query_parameters&, persist_position p) { - semantics::data_member* id (id_member (c)); - type* poly_root (polymorphic (c)); bool poly_derived (poly_root != 0 && poly_root != &c); - if (id == 0 || poly_derived || !auto_ (*id)) + // If we are a derived type in a polymorphic hierarchy, then + // auto id/version are handled by the root. + // + if (poly_derived) return; - // If we are a derived type in a polymorphic hierarchy, then - // auto id is handled by the root. + // See if we have auto id or ROWVERSION version. // - if (type* root = polymorphic (c)) - if (root != &c) - return; + semantics::data_member* id (id_member (c)); + semantics::data_member* ver (optimistic (c)); + + if (id != 0 && !auto_ (*id)) + id = 0; + + if (ver != 0) + { + sql_type t (parse_sql_type (column_type (*ver), *ver)); + if (t.type != sql_type::ROWVERSION) + ver = 0; + } + + if (id == 0 && ver == 0) + return; // SQL Server 2005 has a bug that causes it to fail on an // INSERT statement with the OUTPUT clause if data for one @@ -900,9 +937,22 @@ namespace relational if (ld) { if (p == persist_after_values) + { + // SQL Server 2005 has no eqivalent of SCOPE_IDENTITY for + // ROWVERSION. + // + if (ver != 0) + { + error (c.location ()) << "in SQL Server 2005 ROWVERSION " << + "value cannot be retrieved for a persistent class " << + "containing long data" << endl; + throw operation_failed (); + } + os << endl << strlit ("; SELECT " + convert_from ("SCOPE_IDENTITY()", *id)); + } return; } @@ -910,21 +960,78 @@ namespace relational if (p == persist_after_columns) { - // Top-level auto id. + string s (" OUTPUT "); + + // Top-level auto id column. // - os << strlit ( - " OUTPUT " + convert_from ( - "INSERTED." + column_qname ( - *id, column_prefix ()), *id)) << endl; + if (id != 0) + s += "INSERTED." + convert_from ( + column_qname (*id, column_prefix ()), *id); + + // Top-level version column. + // + if (ver != 0) + { + if (id != 0) + s += ','; + + s += "INSERTED." + convert_from ( + column_qname (*ver, column_prefix ()), *ver); + } + + os << strlit (s) << endl; } } virtual void + update_statement_extra (type& c) + { + type* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + // If we are a derived type in a polymorphic hierarchy, then + // version is handled by the root. + // + if (poly_derived) + return; + + semantics::data_member* ver (optimistic (c)); + + if (ver == 0 || + parse_sql_type (column_type (*ver), *ver).type != + sql_type::ROWVERSION) + return; + + // Long data & SQL Server 2005 incompatibility is detected + // in persist_statement_extra. + // + os << strlit ( + " OUTPUT INSERTED." + convert_from ( + column_qname (*ver, column_prefix ()), *ver)) << endl; + } + + virtual void process_statement_columns (relational::statement_columns& cols, statement_kind sk) { statement_columns_common::process (cols, sk); } + + virtual string + optimimistic_version_init (semantics::data_member& m) + { + sql_type t (parse_sql_type (column_type (m), m)); + return t.type != sql_type::ROWVERSION ? "1" : "st.version ()"; + } + + virtual string + optimimistic_version_increment (semantics::data_member& m) + { + sql_type t (parse_sql_type (column_type (m), m)); + return t.type != sql_type::ROWVERSION + ? "1" + : "sts.update_statement ().version ()"; + } }; entry class_entry_; } diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 48ae1f2..78f7b79 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -66,7 +66,7 @@ namespace relational { object_columns (base const& x): base (x) {} - virtual void + virtual bool column (semantics::data_member& m, string const& table, string const& column) @@ -114,8 +114,7 @@ namespace relational if (sk_ != statement_select || parse_sql_type (type, m).type != sql_type::ENUM) { - base::column (m, table, column); - return; + return base::column (m, table, column); } // Qualified column and conversion expression. @@ -133,6 +132,7 @@ namespace relational sc_.push_back ( relational::statement_column (table, r, type, m, key_prefix_)); + return true; } }; entry object_columns_; @@ -141,7 +141,7 @@ namespace relational { view_columns (base const& x): base (x) {} - virtual void + virtual bool column (semantics::data_member& m, string const& table, string const& column) @@ -152,8 +152,7 @@ namespace relational if (parse_sql_type (type, m).type != sql_type::ENUM) { - base::column (m, table, column); - return; + return base::column (m, table, column); } // Column and conversion expression. @@ -162,6 +161,7 @@ namespace relational string r ("CONCAT(" + c + "+0,' '," + c + ")"); sc_.push_back (relational::statement_column (table, r, type, m)); + return true; } }; entry view_columns_; diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index f133750..0ec7cc4 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -687,6 +687,8 @@ traverse_object (type& c) os << strlit (c + (++i != e ? "," : "")) << endl; } + update_statement_extra (c); + for (object_columns_list::iterator b (id_cols->begin ()), i (b); i != id_cols->end (); ++i) { @@ -980,12 +982,13 @@ traverse_object (type& c) // If we don't have auto id, then obj is a const reference. // string obj (auto_id ? "obj" : "const_cast< object_type& > (obj)"); + string init (optimimistic_version_init (*opt)); if (!opt_ma_set->synthesized) os << "// From " << location_string (opt_ma_set->loc, true) << endl; if (opt_ma_set->placeholder ()) - os << opt_ma_set->translate (obj, "1") << ";" + os << opt_ma_set->translate (obj, init) << ";" << endl; else { @@ -1002,7 +1005,7 @@ traverse_object (type& c) if (cast) os << ")"; - os << " = 1;" + os << " = " << init << ";" << endl; } } @@ -1397,6 +1400,7 @@ traverse_object (type& c) // constness. // string obj ("const_cast< object_type& > (obj)"); + string inc (optimimistic_version_increment (*opt)); if (!opt_ma_set->synthesized) os << "// From " << location_string (opt_ma_set->loc, true) << endl; @@ -1407,9 +1411,13 @@ traverse_object (type& c) os << "// From " << location_string (opt_ma_get->loc, true) << endl; - os << opt_ma_set->translate ( - obj, opt_ma_get->translate ("obj") + " + 1") << ";" - << endl; + if (inc == "1") + os << opt_ma_set->translate ( + obj, opt_ma_get->translate ("obj") + " + 1") << ";"; + else + os << opt_ma_set->translate (obj, inc) << ";"; + + os << endl; } else { @@ -1426,8 +1434,12 @@ traverse_object (type& c) if (cast) os << ")"; - os << "++;" - << endl; + if (inc == "1") + os << "++;"; + else + os << " = " << inc << ";"; + + os << endl; } } diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 1e223af..dfa6ebf 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -220,12 +220,10 @@ namespace relational sk_ == statement_update) return false; - column (m, table_name_, quote_id (name)); - - return true; + return column (m, table_name_, quote_id (name)); } - virtual void + virtual bool column (semantics::data_member& m, string const& table, string const& column) @@ -259,6 +257,7 @@ namespace relational r = convert_from (r, sqlt, m); sc_.push_back (statement_column (table, r, sqlt, m, key_prefix_)); + return true; } protected: @@ -426,14 +425,13 @@ namespace relational throw operation_failed (); } - column (m, tbl, col); - return true; + return column (m, tbl, col); } // The column argument is a qualified and quoted column or // expression. // - virtual void + virtual bool column (semantics::data_member& m, string const& table, string const& column) @@ -442,6 +440,7 @@ namespace relational sc_.push_back ( statement_column ( table, convert_from (column, sqlt, m), sqlt, m)); + return true; } protected: @@ -3262,7 +3261,7 @@ namespace relational string p; if (version (m)) - p = "1"; + p = version_value (m); else if (context::id (m) && auto_ (m)) // Only simple id can be auto. p = qp_.auto_id (); else @@ -3279,6 +3278,12 @@ namespace relational return !p.empty (); } + virtual string + version_value (semantics::data_member&) + { + return "1"; + } + private: string& params_; query_parameters& qp_; @@ -3406,6 +3411,11 @@ namespace relational { } + virtual void + update_statement_extra (type&) + { + } + // // common // @@ -3448,6 +3458,20 @@ namespace relational << "q.parameters_binding ()"; } + virtual string + optimimistic_version_init (semantics::data_member&) + { + return "1"; + } + + // Returning "1" means incremenet by one. + // + virtual string + optimimistic_version_increment (semantics::data_member&) + { + return "1"; + } + virtual void traverse_object (type& c); -- cgit v1.1