aboutsummaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-01-14 16:01:06 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-01-14 16:01:06 +0200
commit0e6be616e40c86ff03df7c0806bcbd0cc3c4489f (patch)
tree410e7ab2e10bb3dd0df30aab2135ed4544555157 /odb
parent604517928701b3ef2783ede9a0b3b2f80c92ec92 (diff)
Add support for MSSQL ROWVERSION
ODB can now use ROWVERSION column as an optimistic concurrency version.
Diffstat (limited to 'odb')
-rw-r--r--odb/relational/mssql/header.cxx32
-rw-r--r--odb/relational/mssql/source.cxx143
-rw-r--r--odb/relational/mysql/source.cxx12
-rw-r--r--odb/relational/source.cxx26
-rw-r--r--odb/relational/source.hxx40
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> 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> 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> 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_> 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> 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> 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);