diff options
Diffstat (limited to 'odb/relational')
-rw-r--r-- | odb/relational/header.hxx | 61 | ||||
-rw-r--r-- | odb/relational/inline.hxx | 37 | ||||
-rw-r--r-- | odb/relational/mysql/source.cxx | 12 | ||||
-rw-r--r-- | odb/relational/oracle/source.cxx | 12 | ||||
-rw-r--r-- | odb/relational/pgsql/header.cxx | 41 | ||||
-rw-r--r-- | odb/relational/pgsql/source.cxx | 53 | ||||
-rw-r--r-- | odb/relational/source.hxx | 317 | ||||
-rw-r--r-- | odb/relational/sqlite/source.cxx | 12 |
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; |