summaryrefslogtreecommitdiff
path: root/odb/relational
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-11-01 12:41:02 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-11-01 14:30:22 +0200
commit7871bd9b681f449cc3938750ce70fa1ed5400dcd (patch)
tree3784716722430f06799016e3b7bdae7231fd9120 /odb/relational
parentc11ace0f4a665ac0dfb269860ef04dce284b75f5 (diff)
Implement support for optimistic concurrency
New pragmas: optimistic, version. New test: optimistic. New database function: reload().
Diffstat (limited to 'odb/relational')
-rw-r--r--odb/relational/header.hxx61
-rw-r--r--odb/relational/inline.hxx37
-rw-r--r--odb/relational/mysql/source.cxx12
-rw-r--r--odb/relational/oracle/source.cxx12
-rw-r--r--odb/relational/pgsql/header.cxx41
-rw-r--r--odb/relational/pgsql/source.cxx53
-rw-r--r--odb/relational/source.hxx317
-rw-r--r--odb/relational/sqlite/source.cxx12
8 files changed, 471 insertions, 74 deletions
diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx
index 355ec83..39f2bbf 100644
--- a/odb/relational/header.hxx
+++ b/odb/relational/header.hxx
@@ -892,6 +892,7 @@ namespace relational
class1 ()
: id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
query_columns_type_ (false),
pointer_query_columns_type_ (true)
{
@@ -901,6 +902,7 @@ namespace relational
: root_context (), //@@ -Wextra
context (),
id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
query_columns_type_ (false),
pointer_query_columns_type_ (true)
{
@@ -940,6 +942,8 @@ namespace relational
bool auto_id (id ? id->count ("auto") : false);
bool base_id (id ? &id->scope () != &c : false); // Comes from base.
+ semantics::data_member* optimistic (context::optimistic (c));
+
column_count_type const& cc (column_count (c));
os << "// " << c.name () << endl
@@ -992,8 +996,13 @@ namespace relational
{
string const& base (id->scope ().fq_name ());
- os << "typedef object_traits< " << base << " >::id_type id_type;"
- << endl
+ os << "typedef object_traits< " << base << " >::id_type id_type;";
+
+ if (optimistic != 0)
+ os << "typedef object_traits< " << base << " >::version_type " <<
+ "version_type;";
+
+ os << endl
<< "static const bool auto_id = object_traits< " << base <<
" >::auto_id;"
<< endl
@@ -1003,13 +1012,23 @@ namespace relational
}
else
{
- semantics::names* hint;
- semantics::type& t (utype (*id, hint));
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*id, hint));
- os << "typedef " << t.fq_name (hint) << " id_type;"
- << endl;
+ os << "typedef " << t.fq_name (hint) << " id_type;";
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*optimistic, hint));
+
+ os << "typedef " << t.fq_name (hint) << " version_type;";
+ }
- os << "static const bool auto_id = " <<
+ os << endl
+ << "static const bool auto_id = " <<
(auto_id ? "true" : "false") << ";"
<< endl;
@@ -1018,6 +1037,9 @@ namespace relational
id_image_member_->traverse (*id);
+ if (optimistic != 0)
+ version_image_member_->traverse (*optimistic);
+
os << "std::size_t version;"
<< "};";
}
@@ -1054,7 +1076,8 @@ namespace relational
if (id != 0 || !abstract)
// We want to generate a dummy void id() accessor even if this
// object has no id to help us in the runtime. This way we can
- // generic code that will both for both void and non-void ids.
+ // write generic code that will work for both void and non-void
+ // ids.
//
os << "static id_type" << endl
<< "id (const object_type&);"
@@ -1065,6 +1088,11 @@ namespace relational
<< "id (const image_type&);"
<< endl;
+ if (id != 0 && optimistic != 0)
+ os << "static version_type" << endl
+ << "version (const image_type&);"
+ << endl;
+
// grow ()
//
if (generate_grow)
@@ -1108,7 +1136,8 @@ namespace relational
if (id != 0)
{
os << "static void" << endl
- << "init (id_image_type&, const id_type&);"
+ << "init (id_image_type&, const id_type&" <<
+ (optimistic != 0 ? ", const version_type* = 0" : "") << ");"
<< endl;
}
@@ -1156,6 +1185,8 @@ namespace relational
cc.inverse << "UL;"
<< "static const std::size_t readonly_column_count = " <<
cc.readonly << "UL;"
+ << "static const std::size_t managed_optimistic_column_count = " <<
+ cc.optimistic_managed << "UL;"
<< endl;
// Statements.
@@ -1170,6 +1201,9 @@ namespace relational
os << "static const char update_statement[];";
os << "static const char erase_statement[];";
+
+ if (optimistic != 0)
+ os << "static const char optimistic_erase_statement[];";
}
if (options.generate_query ())
@@ -1216,6 +1250,10 @@ namespace relational
<< "find (database&, const id_type&, object_type&);"
<< endl;
+ os << "static bool" << endl
+ << "reload (database&, object_type&);"
+ << endl;
+
// update ()
//
if (!readonly (c))
@@ -1228,6 +1266,10 @@ namespace relational
os << "static void" << endl
<< "erase (database&, const id_type&);"
<< endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type&);"
+ << endl;
}
// query ()
@@ -1560,6 +1602,7 @@ namespace relational
private:
instance<image_type> image_type_;
instance<image_member> id_image_member_;
+ instance<image_member> version_image_member_;
instance<query_columns_type> query_columns_type_;
instance<query_columns_type> pointer_query_columns_type_;
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
index 3968dce..0c37d33 100644
--- a/odb/relational/inline.hxx
+++ b/odb/relational/inline.hxx
@@ -109,6 +109,8 @@ namespace relational
semantics::data_member* id (id_member (c));
bool base_id (id ? &id->scope () != &c : false); // Comes from base.
+ semantics::data_member* optimistic (context::optimistic (c));
+
os << "// " << c.name () << endl
<< "//" << endl
<< endl;
@@ -139,10 +141,10 @@ namespace relational
if (id != 0)
{
+ // id (image_type)
+ //
if (options.generate_query () && base_id)
{
- // id (image_type)
- //
os << "inline" << endl
<< traits << "::id_type" << endl
<< traits << "::" << endl
@@ -153,6 +155,20 @@ namespace relational
<< "}";
}
+ // version (image_type)
+ //
+ if (optimistic != 0 && base_id)
+ {
+ os << "inline" << endl
+ << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "return object_traits< " <<
+ optimistic->scope ().fq_name () << " >::version (i);"
+ << "}";
+ }
+
// bind (id_image_type)
//
if (base_id)
@@ -170,10 +186,11 @@ namespace relational
{
os << "inline" << endl
<< "void " << traits << "::" << endl
- << "init (id_image_type& i, const id_type& id)"
+ << "init (id_image_type& i, const id_type& id" <<
+ (optimistic != 0 ? ", const version_type* v" : "") << ")"
<< "{"
<< "object_traits< " << id->scope ().fq_name () <<
- " >::init (i, id);"
+ " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
<< "}";
}
}
@@ -184,6 +201,18 @@ namespace relational
if (abstract)
return;
+ // erase (object_type)
+ //
+ if (id != 0 && optimistic == 0)
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj)"
+ << "{"
+ << "erase (db, id (obj));"
+ << "}";
+ }
+
// callback ()
//
os << "inline" << endl
diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx
index d49910f..6a05878 100644
--- a/odb/relational/mysql/source.cxx
+++ b/odb/relational/mysql/source.cxx
@@ -197,7 +197,7 @@ namespace relational
os << "// " << mi.m.name () << endl
<< "//" << endl;
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
os << "if (sk == statement_select)"
<< "{";
// If the whole class is readonly, then we will never be
@@ -242,7 +242,7 @@ namespace relational
<< "sk == statement_select ? 0 : ";
if (cc.inverse != 0)
- os << cc.inverse << "UL" << endl;
+ os << cc.inverse << "UL";
if (!ro && cc.readonly != 0)
{
@@ -266,7 +266,7 @@ namespace relational
// The same logic as in pre().
//
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
block = true;
else if (!readonly (*context::top_object))
{
@@ -587,6 +587,12 @@ namespace relational
member = member_override_;
else
{
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
string const& name (mi.m.name ());
member = "o." + name;
diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx
index 62b417b..0c40bab 100644
--- a/odb/relational/oracle/source.cxx
+++ b/odb/relational/oracle/source.cxx
@@ -90,7 +90,7 @@ namespace relational
os << "// " << mi.m.name () << endl
<< "//" << endl;
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
os << "if (sk == statement_select)"
<< "{";
// If the whole class is readonly, then we will never be
@@ -135,7 +135,7 @@ namespace relational
<< "sk == statement_select ? 0 : ";
if (cc.inverse != 0)
- os << cc.inverse << "UL" << endl;
+ os << cc.inverse << "UL";
if (!ro && cc.readonly != 0)
{
@@ -159,7 +159,7 @@ namespace relational
// The same logic as in pre().
//
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
block = true;
else if (!readonly (*context::top_object))
{
@@ -335,6 +335,12 @@ namespace relational
member = member_override_;
else
{
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
string const& name (mi.m.name ());
member = "o." + name;
diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx
index 474b69c..db7a939 100644
--- a/odb/relational/pgsql/header.cxx
+++ b/odb/relational/pgsql/header.cxx
@@ -26,17 +26,27 @@ namespace relational
if (abstract (c))
return;
+ semantics::data_member* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
column_count_type const& cc (column_count (c));
// Statement names.
//
- os << "static const char persist_statement_name[];"
- << "static const char find_statement_name[];";
+ os << "static const char persist_statement_name[];";
+
+ if (id != 0)
+ {
+ os << "static const char find_statement_name[];";
- if (cc.total != cc.id + cc.inverse + cc.readonly)
- os << "static const char update_statement_name[];";
+ if (cc.total != cc.id + cc.inverse + cc.readonly)
+ os << "static const char update_statement_name[];";
- os << "static const char erase_statement_name[];";
+ os << "static const char erase_statement_name[];";
+
+ if (optimistic != 0)
+ os << "static const char optimistic_erase_statement_name[];";
+ }
// Query statement name.
//
@@ -48,14 +58,23 @@ namespace relational
// Statement types.
//
- os << "static const unsigned int persist_statement_types[];"
- << "static const unsigned int find_statement_types[];";
+ os << "static const unsigned int persist_statement_types[];";
- if (cc.total != cc.id + cc.inverse + cc.readonly)
- os << "static const unsigned int update_statement_types[];";
+ if (id != 0)
+ {
+ os << "static const unsigned int find_statement_types[];";
- os << "static const unsigned int erase_statement_types[];"
- << endl;
+ if (cc.total != cc.id + cc.inverse + cc.readonly)
+ os << "static const unsigned int update_statement_types[];";
+
+ os << "static const unsigned int erase_statement_types[];";
+
+ if (optimistic != 0)
+ os << "static const unsigned int " <<
+ "optimistic_erase_statement_types[];";
+ }
+
+ os << endl;
}
virtual void
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
index 6bfa3d9..0ff0bf1 100644
--- a/odb/relational/pgsql/source.cxx
+++ b/odb/relational/pgsql/source.cxx
@@ -122,6 +122,10 @@ namespace relational
sk_ == statement_update)
return false;
+ if ((sk_ == statement_insert || sk_ == statement_update) &&
+ version (m))
+ return false;
+
if (!first)
os << ',' << endl;
@@ -164,7 +168,7 @@ namespace relational
os << "// " << mi.m.name () << endl
<< "//" << endl;
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
os << "if (sk == statement_select)"
<< "{";
// If the whole class is readonly, then we will never be
@@ -209,7 +213,7 @@ namespace relational
<< "sk == statement_select ? 0 : ";
if (cc.inverse != 0)
- os << cc.inverse << "UL" << endl;
+ os << cc.inverse << "UL";
if (!ro && cc.readonly != 0)
{
@@ -233,7 +237,7 @@ namespace relational
// The same logic as in pre().
//
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
block = true;
else if (!readonly (*context::top_object))
{
@@ -493,6 +497,12 @@ namespace relational
member = member_override_;
else
{
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
string const& name (mi.m.name ());
member = "o." + name;
@@ -1001,6 +1011,7 @@ namespace relational
return;
semantics::data_member* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
column_count_type const& cc (column_count (c));
string const& n (c.fq_name ());
@@ -1028,6 +1039,12 @@ namespace relational
os << name_decl << endl
<< "erase_statement_name[] = " << strlit (fn + "_erase") << ";"
<< endl;
+
+ if (optimistic != 0)
+ os << name_decl << endl
+ << "optimistic_erase_statement_name[] = " <<
+ strlit (fn + "_optimistic_erase") << ";"
+ << endl;
}
// Query statement name.
@@ -1087,9 +1104,18 @@ namespace relational
st->traverse (c);
}
+ bool first (cc.total == cc.id + cc.inverse + cc.readonly +
+ cc.optimistic_managed);
+
{
instance<statement_oids> st (statement_where);
- st->traverse_column (*id, "", false);
+ st->traverse_column (*id, "", first);
+ }
+
+ if (optimistic != 0)
+ {
+ instance<statement_oids> st (statement_where);
+ st->traverse_column (*optimistic, "", false);
}
os << "};";
@@ -1108,6 +1134,25 @@ namespace relational
os << "};";
}
+
+ if (id != 0 && optimistic != 0)
+ {
+ os << oid_decl << endl
+ << "optimistic_erase_statement_types[] ="
+ << "{";
+
+ {
+ instance<statement_oids> st (statement_where);
+ st->traverse_column (*id, "", true);
+ }
+
+ {
+ instance<statement_oids> st (statement_where);
+ st->traverse_column (*optimistic, "", false);
+ }
+
+ os << "};";
+ }
}
virtual void
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index dcc9c23..cabb5c5 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -90,6 +90,17 @@ namespace relational
line_.clear ();
+ // Version column (optimistic concurrency) requires special
+ // handling in the UPDATE statement.
+ //
+ //
+ if (sk_ == statement_update && version (m))
+ {
+ string const& qname (quote_id (name));
+ line_ = qname + "=" + qname + "+1";
+ return true;
+ }
+
// Inverse object pointers come from a joined table.
//
if (im != 0)
@@ -565,20 +576,22 @@ namespace relational
os << "n += " << cc.total << "UL";
// select = total
- // insert = total - inverse
- // update = total - inverse - id - readonly
+ // insert = total - inverse - optimistic_managed
+ // update = total - inverse - optimistic_managed - id - readonly
//
- if (cc.inverse != 0 || (!ro && (cc.id != 0 || cc.readonly != 0)))
+ if (cc.inverse != 0 ||
+ cc.optimistic_managed != 0 ||
+ (!ro && (cc.id != 0 || cc.readonly != 0)))
{
os << " - (" << endl
<< "sk == statement_select ? 0 : ";
- if (cc.inverse != 0)
- os << cc.inverse << "UL" << endl;
+ if (cc.inverse != 0 || cc.optimistic_managed != 0)
+ os << (cc.inverse + cc.optimistic_managed) << "UL";
if (!ro && (cc.id != 0 || cc.readonly != 0))
{
- if (cc.inverse != 0)
+ if (cc.inverse != 0 || cc.optimistic_managed != 0)
os << " + ";
os << "(" << endl
@@ -2085,7 +2098,9 @@ namespace relational
if (count_++ != 0)
params_ += ',';
- if (m.count ("id") && m.count ("auto"))
+ if (version (m))
+ params_ += "1";
+ else if (m.count ("id") && m.count ("auto"))
params_ += qp_.auto_id ();
else
params_ += qp_.next ();
@@ -2112,8 +2127,11 @@ namespace relational
: grow_base_ (index_),
grow_member_ (index_),
bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
init_id_value_member_ ("id"),
+ init_version_value_member_ ("v"),
stream_ (emitter_),
drop_model_ (emitter_, stream_, format_embedded),
drop_table_ (emitter_, stream_, format_embedded),
@@ -2131,8 +2149,11 @@ namespace relational
grow_base_ (index_),
grow_member_ (index_),
bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
init_id_value_member_ ("id"),
+ init_version_value_member_ ("v"),
stream_ (emitter_),
drop_model_ (emitter_, stream_, format_embedded),
drop_table_ (emitter_, stream_, format_embedded),
@@ -2266,13 +2287,16 @@ namespace relational
bool auto_id (id ? id->count ("auto") : false);
bool base_id (id ? &id->scope () != &c : false); // Comes from base.
+ semantics::data_member* optimistic (context::optimistic (c));
+
bool grow (false);
bool grow_id (false);
if (generate_grow)
{
grow = context::grow (c);
- grow_id = id ? context::grow (*id) : false;
+ grow_id = (id ? context::grow (*id) : false) ||
+ (optimistic ? context::grow (*optimistic) : false);
}
column_count_type const& cc (column_count (c));
@@ -2345,6 +2369,18 @@ namespace relational
<< "}";
}
+ if (id != 0 && optimistic != 0 && !base_id)
+ {
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_->traverse (*optimistic);
+ os << "return v;"
+ << "}";
+ }
+
// grow ()
//
if (generate_grow)
@@ -2397,7 +2433,17 @@ namespace relational
<< "bind (" << bind_vector << " b, id_image_type& i)"
<< "{"
<< "std::size_t n (0);";
+
bind_id_member_->traverse (*id);
+
+ if (optimistic != 0)
+ {
+ os << "n++;" //@@ composite id
+ << endl;
+
+ bind_version_member_->traverse (*optimistic);
+ }
+
os << "}";
}
@@ -2449,17 +2495,27 @@ namespace relational
if (id != 0 && !base_id)
{
os << "void " << traits << "::" << endl
- << "init (id_image_type& i, const id_type& id)"
+ << "init (id_image_type& i, const id_type& id" <<
+ (optimistic != 0 ? ", const version_type* v" : "") << ")"
<< "{";
if (grow_id)
- os << "bool grew (false);";
+ os << "bool grew (false);"
+ << endl;
init_id_image_member_->traverse (*id);
+ if (optimistic != 0)
+ {
+ // Here we rely on the fact that init_image_member
+ // always wraps the statements in a block.
+ //
+ os << "if (v != 0)";
+ init_version_image_member_->traverse (*optimistic);
+ }
+
if (grow_id)
- os << endl
- << "if (grew)" << endl
+ os << "if (grew)" << endl
<< "i.version++;";
os << "}";
@@ -2564,7 +2620,13 @@ namespace relational
instance<object_columns> t (statement_update, true, qp.get ());
t->traverse (c);
- os << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";"
+ string where (" WHERE " + id_col + "=" + qp->next ());
+
+ if (optimistic != 0)
+ where += " AND " + column_qname (*optimistic) + "=" +
+ qp->next ();
+
+ os << strlit (where) << ";"
<< endl;
}
@@ -2577,6 +2639,20 @@ namespace relational
<< strlit (" WHERE " + id_col + "=" + qp->next ()) << ";"
<< endl;
}
+
+ if (optimistic != 0)
+ {
+ instance<query_parameters> qp;
+
+ string where (" WHERE " + id_col + "=" + qp->next ());
+ where += " AND " + column_qname (*optimistic) + "=" + qp->next ();
+
+ os << "const char " << traits <<
+ "::optimistic_erase_statement[] =" << endl
+ << strlit ("DELETE FROM " + table) << endl
+ << strlit (where) << ";"
+ << endl;
+ }
}
if (options.generate_query ())
@@ -2678,6 +2754,19 @@ namespace relational
<< endl;
}
+ if (optimistic != 0)
+ {
+ // Set the version in the object member.
+ //
+ if (!auto_id || const_type (optimistic->type ()))
+ os << "const_cast< version_type& > (" <<
+ "obj." << optimistic->name () << ") = 1;";
+ else
+ os << "obj." << optimistic->name () << " = 1;";
+
+ os << endl;
+ }
+
if (straight_containers)
{
// Initialize id_image and binding.
@@ -2718,9 +2807,15 @@ namespace relational
{
// Initialize object and id images.
//
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, obj." << id->name () << ");"
- << endl
+ os << "id_image_type& i (sts.id_image ());";
+
+ if (optimistic == 0)
+ os << "init (i, obj." << id->name () << ");";
+ else
+ os << "init (i, obj." << id->name () << ", &obj." <<
+ optimistic->name () << ");";
+
+ os << endl
<< "image_type& im (sts.image ());"
<< "if (init (im, obj, statement_update))" << endl
<< "im.version++;"
@@ -2748,18 +2843,33 @@ namespace relational
<< "if (i.version != sts.update_id_image_version () || " <<
"idb.version == 0)"
<< "{"
+ // If the id binding is up-to-date, then that means update
+ // binding is too and we just need to update the versions.
+ //
+ << "if (i.version != sts.id_image_version () || " <<
+ "idb.version == 0)"
+ << "{"
<< "bind (idb.bind, i);"
- << "sts.update_id_image_version (i.version);"
- << "if (!u)" << endl
- << "imb.version++;"
- << endl
- // Update the id binding versions since we rebound it.
+ // Update the id binding versions since we may use them later
+ // to update containers.
//
<< "sts.id_image_version (i.version);"
<< "idb.version++;"
+ << "}"
+ << "sts.update_id_image_version (i.version);"
+ << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
<< "}";
- os << "sts.update_statement ().execute ();";
+ os << "if (sts.update_statement ().execute () == 0)" << endl;
+
+ if (optimistic == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
}
else
{
@@ -2772,20 +2882,28 @@ namespace relational
if (straight_readwrite_containers)
os << endl
- << "binding& idb (sts.id_image_binding ());";
+ << "binding& idb (sts.id_image_binding ());"
+ << endl;
}
if (straight_readwrite_containers)
{
- os << endl;
instance<container_calls> t (container_calls::update_call);
t->traverse (c);
}
+ if (optimistic != 0)
+ {
+ // Update version in the object member.
+ //
+ os << "const_cast< version_type& > (" <<
+ "obj." << optimistic->name () << ")++;";
+ }
+
os << "}";
}
- // erase ()
+ // erase (id_type)
//
if (id != 0)
{
@@ -2823,7 +2941,6 @@ namespace relational
{
instance<container_calls> t (container_calls::erase_call);
t->traverse (c);
- os << endl;
}
os << "if (sts.erase_statement ().execute () != 1)" << endl
@@ -2832,6 +2949,91 @@ namespace relational
os << "}";
}
+ // erase (object_type)
+ //
+ if (id != 0 && optimistic != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database&, const object_type& obj)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << object_statements_type << "& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Initialize id + managed column image.
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, obj." << id->name () << ", &obj." <<
+ optimistic->name () << ");"
+ << endl;
+
+ // To update the id part of the optimistic id binding we have
+ // to do it indirectly via the id binding, since both id and
+ // optimistic id bindings just point to the suffix of the
+ // update bind array (see object_statements).
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "binding& oidb (sts.optimistic_id_image_binding ());"
+ << "if (i.version != sts.optimistic_id_image_version () || " <<
+ "oidb.version == 0)"
+ << "{"
+ // If the id binding is up-to-date, then that means optimistic
+ // id binding is too and we just need to update the versions.
+ //
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ // Update the id binding versions since we may use them later
+ // to delete containers.
+ //
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}"
+ << "sts.optimistic_id_image_version (i.version);"
+ << "oidb.version++;"
+ << "}";
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (straight_containers)
+ {
+ // Things get complicated here: we don't want to trash the
+ // containers and then find out that the versions don't match
+ // and we therefore cannot delete the object. After all, there
+ // is no guarantee that the user will abort the transaction.
+ // In fact, a perfectly reasonable scenario is to reload the
+ // object, re-apply the changes, and commit the transaction.
+ //
+ // There doesn't seem to be anything better than first making
+ // sure we can delete the object, then deleting the container
+ // data, and then deleting the object. To check that we can
+ // delete the object we are going to use find_() and then
+ // compare the versions. A special-purpose SELECT query would
+ // have been more efficient but it would complicated and bloat
+ // things significantly.
+ //
+ os << "if (!find_ (sts, obj." << id->name () << ") ||" << endl
+ << "version (sts.image ()) != obj." << optimistic->name () <<
+ ")" << endl
+ << "throw object_changed ();"
+ << endl;
+
+ instance<container_calls> t (container_calls::erase_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.optimistic_erase_statement ().execute () != 1)" << endl
+ << "throw object_changed ();"
+ << "}";
+ }
+
// find (id)
//
if (id != 0 && c.default_ctor ())
@@ -2895,18 +3097,18 @@ namespace relational
<< db << "::transaction::current ().connection ());"
<< object_statements_type << "& sts (" << endl
<< "conn.statement_cache ().find_object<object_type> ());"
- << object_statements_type << "::auto_lock l (sts);"
<< endl
- << "if (l.locked ())"
- << "{"
- << "if (!find_ (sts, id))" << endl
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ << object_statements_type << "::auto_lock l (sts);"
+ << endl;
+
+ os << "if (!find_ (sts, id))" << endl
<< "return false;"
- << "}"
+ << endl
<< "reference_cache_traits< object_type >::insert_guard ig (" << endl
<< "reference_cache_traits< object_type >::insert (db, id, obj));"
<< endl
- << "if (l.locked ())"
- << "{"
<< "callback (db, obj, callback_event::pre_load);"
<< "init (obj, sts.image (), db);";
@@ -2916,12 +3118,49 @@ namespace relational
<< "sts.load_delayed ();"
<< "l.unlock ();"
<< "callback (db, obj, callback_event::post_load);"
- << "}"
- << "else" << endl
- << "sts.delay_load (id, obj, ig.position ());"
+ << "ig.release ();"
+ << "return true;"
+ << "}";
+ }
+
+ // reload()
+ //
+ if (id != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "reload (database& db, object_type& obj)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << object_statements_type << "& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ << object_statements_type << "::auto_lock l (sts);"
<< endl;
- os << "ig.release ();"
+ os << "if (!find_ (sts, obj." << id->name () << "))" << endl
+ << "return false;"
+ << endl;
+
+ if (optimistic != 0)
+ os << "if (version (sts.image ()) == obj." <<
+ optimistic->name () << ")" << endl
+ << "return true;"
+ << endl;
+
+ os << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), db);";
+
+ init_value_extra ();
+
+ os << "load_ (sts, obj);"
+ << "sts.load_delayed ();"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
<< "return true;"
<< "}";
}
@@ -4126,6 +4365,7 @@ namespace relational
instance<bind_member> bind_member_;
traversal::names bind_member_names_;
instance<bind_member> bind_id_member_;
+ instance<bind_member> bind_version_member_;
instance<init_image_base> init_image_base_;
traversal::inherits init_image_base_inherits_;
@@ -4133,12 +4373,15 @@ namespace relational
traversal::names init_image_member_names_;
instance<init_image_member> init_id_image_member_;
+ instance<init_image_member> init_version_image_member_;
instance<init_value_base> init_value_base_;
traversal::inherits init_value_base_inherits_;
instance<init_value_member> init_value_member_;
traversal::names init_value_member_names_;
+
instance<init_value_member> init_id_value_member_;
+ instance<init_value_member> init_version_value_member_;
schema_emitter emitter_;
emitter_ostream stream_;
diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx
index 0516370..041f8aa 100644
--- a/odb/relational/sqlite/source.cxx
+++ b/odb/relational/sqlite/source.cxx
@@ -48,7 +48,7 @@ namespace relational
os << "// " << mi.m.name () << endl
<< "//" << endl;
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
os << "if (sk == statement_select)"
<< "{";
// If the whole class is readonly, then we will never be
@@ -93,7 +93,7 @@ namespace relational
<< "sk == statement_select ? 0 : ";
if (cc.inverse != 0)
- os << cc.inverse << "UL" << endl;
+ os << cc.inverse << "UL";
if (!ro && cc.readonly != 0)
{
@@ -117,7 +117,7 @@ namespace relational
// The same logic as in pre().
//
- if (inverse (mi.m, key_prefix_))
+ if (inverse (mi.m, key_prefix_) || version (mi.m))
block = true;
else if (!readonly (*context::top_object))
{
@@ -295,6 +295,12 @@ namespace relational
member = member_override_;
else
{
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
string const& name (mi.m.name ());
member = "o." + name;