From c3c42b69ee9cda9634573497c4476a05c1f3d049 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 12 Sep 2013 06:56:02 +0200 Subject: Make sure that soft-add/delete version is current version --- odb/context.hxx | 47 ++++++++++++++- odb/relational/changelog.cxx | 135 ++++++++++++++++++++++++++++++++++++++----- odb/relational/model.cxx | 1 + odb/relational/model.hxx | 47 +++++++++++---- 4 files changed, 202 insertions(+), 28 deletions(-) diff --git a/odb/context.hxx b/odb/context.hxx index 416c7c6..f188a41 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -136,9 +136,10 @@ struct default_value }; }; -// Database potentially-qualified name. +// Database potentially-qualified and unqualifed names. // using semantics::relational::qname; +using semantics::relational::uname; // Object or table associated with the view. // @@ -771,6 +772,28 @@ public: return r; } + static semantics::data_member* + deleted_member (data_member_path const& mp) + { + semantics::data_member* m (0); + + // Find the earliest version since this member was deleted. + // + unsigned long long r (0); + for (data_member_path::const_reverse_iterator i (mp.rbegin ()); + i != mp.rend (); ++i) + { + unsigned long long v ((*i)->get ("deleted", 0)); + if (v != 0 && (r == 0 || v < r)) + { + r = v; + m = *i; + } + } + + return m; + } + // Return the addition version or 0 if not soft-added. // static unsigned long long @@ -797,6 +820,28 @@ public: return r; } + static semantics::data_member* + added_member (data_member_path const& mp) + { + semantics::data_member* m (0); + + // Find the latest version since this member was added. + // + unsigned long long r (0); + for (data_member_path::const_reverse_iterator i (mp.rbegin ()); + i != mp.rend (); ++i) + { + unsigned long long v ((*i)->get ("added", 0)); + if (v != 0 && v > r) + { + r = v; + m = *i; + } + } + + return m; + } + static bool id (semantics::data_member& m) { diff --git a/odb/relational/changelog.cxx b/odb/relational/changelog.cxx index 63ea44f..7235882 100644 --- a/odb/relational/changelog.cxx +++ b/odb/relational/changelog.cxx @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include + #include #include @@ -20,6 +22,9 @@ namespace relational using sema_rel::model; using sema_rel::changelog; + typedef std::map deleted_table_map; + typedef std::map deleted_column_map; + namespace { // @@ -33,8 +38,12 @@ namespace relational { enum mode_type {mode_add, mode_drop}; - diff_table (table& o, mode_type m, alter_table& a, graph& gr) - : other (o), mode (m), at (a), g (gr) {} + diff_table (table& o, + mode_type m, + alter_table& a, + graph& gr, + unsigned long long v) + : other (o), mode (m), at (a), g (gr), current (v) {} virtual void traverse (sema_rel::column& c) @@ -71,6 +80,23 @@ namespace relational } else { + if (current != 0) + { + data_member_path const& mp ( + c.get ("member-path")); + + // Make sure the addition version is the current version. + // + semantics::data_member* m (context::added_member (mp)); + if (m != 0 && context::added (*m) != current) + { + location l (m->get ("added-location")); + error (l) << "member addition version is not the same " << + "as the current model version" << endl; + throw operation_failed (); + } + } + add_column& ac (g.new_node (c, at, g)); g.new_edge (at, ac, c.name ()); } @@ -79,6 +105,24 @@ namespace relational { if (other.find (c.name ()) == 0) { + if (current != 0) + { + // See if we have an entry for this column in the soft- + // deleted map. + // + deleted_column_map const& dm ( + other.get ("deleted-map")); + deleted_column_map::const_iterator i (dm.find (c.name ())); + + if (i != dm.end () && context::deleted (*i->second) != current) + { + location l (i->second->get ("deleted-location")); + error (l) << "member deletion version is not the same " << + "as the current model version" << endl; + throw operation_failed (); + } + } + drop_column& dc (g.new_node (c.id ())); g.new_edge (at, dc, c.name ()); } @@ -346,14 +390,19 @@ namespace relational mode_type mode; alter_table& at; graph& g; + unsigned long long current; }; struct diff_model: trav_rel::table { enum mode_type {mode_add, mode_drop}; - diff_model (model& o, mode_type m, changeset& s, graph& gr) - : other (o), mode (m), cs (s), g (gr) {} + diff_model (model& o, + mode_type m, + changeset& s, + graph& gr, + unsigned long long c) + : other (o), mode (m), cs (s), g (gr), current (c) {} virtual void traverse (sema_rel::table& t) @@ -380,7 +429,7 @@ namespace relational { trav_rel::table table; trav_rel::unames names; - diff_table dtable (*ot, diff_table::mode_add, at, g); + diff_table dtable (*ot, diff_table::mode_add, at, g, current); table >> names >> dtable; table.traverse (t); } @@ -388,7 +437,7 @@ namespace relational { trav_rel::table table; trav_rel::unames names; - diff_table dtable (t, diff_table::mode_drop, at, g); + diff_table dtable (t, diff_table::mode_drop, at, g, current); table >> names >> dtable; table.traverse (*ot); } @@ -403,8 +452,25 @@ namespace relational } else { - // New table. + // Soft-add is only applicable to containers. // + if (current != 0 && t.count ("member-path")) + { + data_member_path const& mp ( + t.get ("member-path")); + + // Make sure the addition version is the current version. + // + semantics::data_member* m (context::added_member (mp)); + if (m != 0 && context::added (*m) != current) + { + location l (m->get ("added-location")); + error (l) << "member addition version is not the same " << + "as the current model version" << endl; + throw operation_failed (); + } + } + add_table& at (g.new_node (t, cs, g)); g.new_edge (cs, at, t.name ()); } @@ -413,6 +479,42 @@ namespace relational { if (other.find (t.name ()) == 0) { + if (current != 0) + { + // See if we have an entry for this table in the soft- + // deleted map. + // + deleted_table_map const& dm ( + other.get ("deleted-map")); + deleted_table_map::const_iterator i (dm.find (t.name ())); + + if (i != dm.end ()) + { + // This table could be derived either from a class (object) + // or data member (container). + // + semantics::class_* c ( + dynamic_cast (i->second)); + if (c != 0 && context::deleted (*c) != current) + { + location l (c->get ("deleted-location")); + error (l) << "class deletion version is not the same " << + "as the current model version" << endl; + throw operation_failed (); + } + + semantics::data_member* m ( + dynamic_cast (i->second)); + if (m != 0 && context::deleted (*m) != current) + { + location l (m->get ("deleted-location")); + error (l) << "member deletion version is not the same " << + "as the current model version" << endl; + throw operation_failed (); + } + } + } + drop_table& dt (g.new_node (t.id ())); g.new_edge (cs, dt, t.name ()); } @@ -449,12 +551,15 @@ namespace relational mode_type mode; changeset& cs; graph& g; + unsigned long long current; }; - // Assumes the new model has cxx-location set. + // Assumes the new model has cxx-location set. If current is not 0, + // then assume it is the current model version and the new model is + // the current model which has member paths and deleted maps set. // changeset& - diff (model& o, model& n, changelog& l) + diff (model& o, model& n, changelog& l, unsigned long long current) { changeset& r (l.new_node (n.version ())); @@ -483,7 +588,7 @@ namespace relational { trav_rel::model model; trav_rel::qnames names; - diff_model dmodel (o, diff_model::mode_add, r, l); + diff_model dmodel (o, diff_model::mode_add, r, l, current); model >> names >> dmodel; model.traverse (n); } @@ -491,7 +596,7 @@ namespace relational { trav_rel::model model; trav_rel::qnames names; - diff_model dmodel (n, diff_model::mode_drop, r, l); + diff_model dmodel (n, diff_model::mode_drop, r, l, current); model >> names >> dmodel; model.traverse (o); } @@ -753,7 +858,7 @@ namespace relational model& nm (g.new_node (mv.base)); g.new_edge (*cl, nm); - changeset& c (diff (nm, m, *cl)); + changeset& c (diff (nm, m, *cl, mv.current)); if (!c.names_empty ()) { @@ -834,7 +939,7 @@ namespace relational base = &g.new_node (mv.base); g.new_edge (*cl, *base); - changeset& c (diff (*base, *last, *cl)); + changeset& c (diff (*base, *last, *cl, 0)); if (!c.names_empty ()) { g.new_edge (c, *base); @@ -895,7 +1000,7 @@ namespace relational om = &patch (*last, c, g); } - changeset& c (diff (*om, m, *cl)); + changeset& c (diff (*om, m, *cl, mv.current)); if (!c.names_empty ()) { @@ -914,7 +1019,7 @@ namespace relational // if (mv.base != mv.current) { - changeset& c (diff (*last, m, *cl)); + changeset& c (diff (*last, m, *cl, mv.current)); if (!c.names_empty ()) { diff --git a/odb/relational/model.cxx b/odb/relational/model.cxx index 981aff2..6a65803 100644 --- a/odb/relational/model.cxx +++ b/odb/relational/model.cxx @@ -71,6 +71,7 @@ namespace relational cutl::shared_ptr m ( new (shared) sema_rel::model ( ctx.versioned () ? ctx.version ().current : 0)); + m->set ("deleted-map", deleted_table_map ()); traversal::unit unit; traversal::defines unit_defines; diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index e345f83..8920825 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -5,6 +5,7 @@ #ifndef ODB_RELATIONAL_MODEL_HXX #define ODB_RELATIONAL_MODEL_HXX +#include #include #include #include @@ -19,6 +20,8 @@ namespace relational namespace model { typedef std::set tables; + typedef std::map deleted_table_map; + typedef std::map deleted_column_map; struct object_columns: object_columns_base, virtual context { @@ -102,8 +105,11 @@ namespace relational virtual bool traverse_column (semantics::data_member& m, string const& name, bool) { - if (deleted (member_path_)) + if (semantics::data_member* m = deleted_member (member_path_)) + { + table_.get ("deleted-map")[name] = m; return false; + } string col_id (id_prefix_ + (key_prefix_.empty () ? m.name () : key_prefix_)); @@ -112,6 +118,7 @@ namespace relational model_.new_node ( col_id, column_type (), null (m))); c.set ("cxx-location", m.location ()); + c.set ("member-path", member_path_); model_.new_edge (table_, c, name); // An id member cannot have a default value. @@ -233,14 +240,20 @@ namespace relational using sema_rel::column; using sema_rel::foreign_key; - if (deleted (member_path_)) - return; - // Ignore inverse object pointers. // if (inverse (m, key_prefix_)) return; + if (deleted (member_path_)) + { + // Still traverse it as columns so that we can populate the + // deleted map. + // + object_columns_base::traverse_pointer (m, c); + return; + } + string id (id_prefix_ + (key_prefix_.empty () ? m.name () : key_prefix_)); @@ -494,18 +507,22 @@ namespace relational using sema_rel::column; - if (deleted (member_path_)) - return; - // Ignore inverse containers of object pointers. // if (inverse (m, "value")) return; container_kind_type ck (container_kind (ct)); - qname const& name (table_name (m, table_prefix_)); + // Ignore deleted container members. + // + if (semantics::data_member* m = deleted_member (member_path_)) + { + model_.get ("deleted-map")[name] = m; + return; + } + // Add the [] decorator to distinguish this id from non-container // ids (we don't want to ever end up comparing, for example, an // object table to a container table). @@ -514,6 +531,8 @@ namespace relational sema_rel::table& t (model_.new_node (id)); t.set ("cxx-location", m.location ()); + t.set ("member-path", member_path_); + t.set ("deleted-map", deleted_column_map ()); model_.new_edge (model_, t, name); t.options (table_options (m, ct)); @@ -718,21 +737,25 @@ namespace relational if (abstract (c) && !polymorphic (c)) return; - if (deleted (c)) - return; - qname const& name (table_name (c)); - // If the table with this name was already created, assume the + // If the table with this name was already seen, assume the // user knows what they are doing and skip it. // if (tables_.count (name)) return; + if (deleted (c)) + { + model_.get ("deleted-map")[name] = &c; + return; + } + string id (class_fq_name (c), 2); // Remove leading '::'. sema_rel::table& t (model_.new_node (id)); t.set ("cxx-location", c.location ()); + t.set ("deleted-map", deleted_column_map ()); model_.new_edge (model_, t, name); t.options (table_options (c)); -- cgit v1.1