From 35bed9571166e80903ecfd69eb445a1a8df7a974 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 12 Sep 2013 10:01:47 +0200 Subject: Add options to warn about hard additions and deletions --- odb/relational/changelog.cxx | 225 ++++++++++++++++++++++++++++++++++--------- odb/relational/model.hxx | 1 + 2 files changed, 183 insertions(+), 43 deletions(-) (limited to 'odb/relational') diff --git a/odb/relational/changelog.cxx b/odb/relational/changelog.cxx index 7235882..bb8399a 100644 --- a/odb/relational/changelog.cxx +++ b/odb/relational/changelog.cxx @@ -22,8 +22,8 @@ namespace relational using sema_rel::model; using sema_rel::changelog; - typedef std::map deleted_table_map; - typedef std::map deleted_column_map; + typedef map deleted_table_map; + typedef map deleted_column_map; namespace { @@ -42,8 +42,9 @@ namespace relational mode_type m, alter_table& a, graph& gr, - unsigned long long v) - : other (o), mode (m), at (a), g (gr), current (v) {} + options const& op, + model_version const* v) + : other (o), mode (m), at (a), g (gr), ops (op), version (v) {} virtual void traverse (sema_rel::column& c) @@ -80,20 +81,54 @@ namespace relational } else { - if (current != 0) + if (version != 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) + if (m != 0) + { + // Make sure the addition version is the current version. + // + if (context::added (*m) != version->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 (); + } + } + // Warn about hard additions. If the version is closed, then + // we have already seen these warnings so no need to repeat + // them. + // + else if (ops.warn_hard_add () && version->open) { - location l (m->get ("added-location")); - error (l) << "member addition version is not the same " << - "as the current model version" << endl; - throw operation_failed (); + // Issue nicer diagnostics for the direct data member case. + // + if (mp.size () == 1) + { + location l (mp.back ()->location ()); + warn (l) << "data member is hard-added" << endl; + } + else + { + semantics::class_& s ( + dynamic_cast ( + mp.front ()->scope ())); + + warn (s.location ()) << "column '" << c.name () << "' " << + "in class '" << s.name () << "' is hard-added" << endl; + + for (data_member_path::const_iterator i (mp.begin ()); + i != mp.end (); ++i) + { + info ((*i)->location ()) << "corresponding hard-" << + "added data member could be '" << (*i)->name () << + "'" << endl; + } + } } } @@ -105,7 +140,7 @@ namespace relational { if (other.find (c.name ()) == 0) { - if (current != 0) + if (version != 0) { // See if we have an entry for this column in the soft- // deleted map. @@ -114,12 +149,40 @@ namespace relational other.get ("deleted-map")); deleted_column_map::const_iterator i (dm.find (c.name ())); - if (i != dm.end () && context::deleted (*i->second) != current) + if (i != dm.end ()) { - 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 (); + if (context::deleted (*i->second) != version->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 (); + } + } + // Warn about hard deletions. If the version is closed, then + // we have already seen these warnings so no need to repeat + // them. + // + else if (ops.warn_hard_delete () && version->open) + { + // Here all we have is a column name and a class (object) + // or data member (container) corresponding to the table. + // + if (semantics::class_* s = + other.get ("class", 0)) + { + warn (s->location ()) << "column '" << c.name () << "' " << + "in class '" << s->name () << "' is hard-deleted" << endl; + } + else + { + data_member_path const& mp ( + other.get ("member-path")); + + warn (mp.back ()->location ()) << "column '" << + c.name () << "' in container '" << + mp.back ()->name () << "' is hard-deleted" << endl; + } } } @@ -390,7 +453,8 @@ namespace relational mode_type mode; alter_table& at; graph& g; - unsigned long long current; + options const& ops; + model_version const* version; }; struct diff_model: trav_rel::table @@ -401,8 +465,16 @@ namespace relational mode_type m, changeset& s, graph& gr, - unsigned long long c) - : other (o), mode (m), cs (s), g (gr), current (c) {} + string const& in, + options const& op, + model_version const* v) + : other (o), + mode (m), + cs (s), + g (gr), + in_name (in), + ops (op), + version (v) {} virtual void traverse (sema_rel::table& t) @@ -429,7 +501,8 @@ namespace relational { trav_rel::table table; trav_rel::unames names; - diff_table dtable (*ot, diff_table::mode_add, at, g, current); + diff_table dtable ( + *ot, diff_table::mode_add, at, g, ops, version); table >> names >> dtable; table.traverse (t); } @@ -437,7 +510,8 @@ namespace relational { trav_rel::table table; trav_rel::unames names; - diff_table dtable (t, diff_table::mode_drop, at, g, current); + diff_table dtable ( + t, diff_table::mode_drop, at, g, ops, version); table >> names >> dtable; table.traverse (*ot); } @@ -454,7 +528,7 @@ namespace relational { // Soft-add is only applicable to containers. // - if (current != 0 && t.count ("member-path")) + if (version != 0 && t.count ("member-path")) { data_member_path const& mp ( t.get ("member-path")); @@ -462,12 +536,47 @@ namespace relational // Make sure the addition version is the current version. // semantics::data_member* m (context::added_member (mp)); - if (m != 0 && context::added (*m) != current) + if (m != 0) { - location l (m->get ("added-location")); - error (l) << "member addition version is not the same " << - "as the current model version" << endl; - throw operation_failed (); + if (context::added (*m) != version->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 (); + } + } + // Warn about hard additions. If the version is closed, then + // we have already seen these warnings so no need to repeat + // them. + // + else if (ops.warn_hard_add () && version->open) + { + // Issue nicer diagnostics for the direct data member case. + // + if (mp.size () == 1) + { + location l (mp.back ()->location ()); + warn (l) << "data member is hard-added" << endl; + } + else + { + semantics::class_& s ( + dynamic_cast ( + mp.front ()->scope ())); + + warn (s.location ()) << "container table '" << + t.name () << "' in class '" << s.name () << "' is " << + "hard-added" << endl; + + for (data_member_path::const_iterator i (mp.begin ()); + i != mp.end (); ++i) + { + info ((*i)->location ()) << "corresponding hard-" << + "added data member could be '" << (*i)->name () << + "'" << endl; + } + } } } @@ -479,7 +588,7 @@ namespace relational { if (other.find (t.name ()) == 0) { - if (current != 0) + if (version != 0) { // See if we have an entry for this table in the soft- // deleted map. @@ -495,7 +604,7 @@ namespace relational // semantics::class_* c ( dynamic_cast (i->second)); - if (c != 0 && context::deleted (*c) != current) + if (c != 0 && context::deleted (*c) != version->current) { location l (c->get ("deleted-location")); error (l) << "class deletion version is not the same " << @@ -505,7 +614,7 @@ namespace relational semantics::data_member* m ( dynamic_cast (i->second)); - if (m != 0 && context::deleted (*m) != current) + if (m != 0 && context::deleted (*m) != version->current) { location l (m->get ("deleted-location")); error (l) << "member deletion version is not the same " << @@ -513,6 +622,27 @@ namespace relational throw operation_failed (); } } + // Warn about hard deletions. If the version is closed, then + // we have already seen these warnings so no need to repeat + // them. + // + // At first, it may seem like a good idea not to warn about + // class deletions since it is not possible to do anything + // useful with a class without referencing it from the code + // (in C++ sense). However, things get tricky one we consider + // polymorphism since the migration code could be "working" + // with the hard-deleted derived class via its base. So we + // going to warn about everything and let the user decide. + // + else if (ops.warn_hard_delete () && version->open) + { + // There is no way to distinguish between object and + // container tables. We also don't have any sensible + // location. + // + cerr << in_name << ": warning: object or container " << + "table '" << t.name () << "' is hard-deleted" << endl; + } } drop_table& dt (g.new_node (t.id ())); @@ -551,15 +681,22 @@ namespace relational mode_type mode; changeset& cs; graph& g; - unsigned long long current; + string in_name; + options const& ops; + model_version const* version; }; - // Assumes the new model has cxx-location set. If current is not 0, + // Assumes the new model has cxx-location set. If version 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, unsigned long long current) + diff (model& o, + model& n, + changelog& l, + string const& in_name, + options const& ops, + model_version const* version) { changeset& r (l.new_node (n.version ())); @@ -588,7 +725,8 @@ namespace relational { trav_rel::model model; trav_rel::qnames names; - diff_model dmodel (o, diff_model::mode_add, r, l, current); + diff_model dmodel ( + o, diff_model::mode_add, r, l, in_name, ops, version); model >> names >> dmodel; model.traverse (n); } @@ -596,7 +734,8 @@ namespace relational { trav_rel::model model; trav_rel::qnames names; - diff_model dmodel (n, diff_model::mode_drop, r, l, current); + diff_model dmodel ( + n, diff_model::mode_drop, r, l, in_name, ops, version); model >> names >> dmodel; model.traverse (o); } @@ -819,8 +958,8 @@ namespace relational generate (model& m, model_version const& mv, changelog* old, - std::string const& in_name, - std::string const& out_name, + string const& in_name, + string const& out_name, options const& ops) { cutl::shared_ptr cl ( @@ -858,7 +997,7 @@ namespace relational model& nm (g.new_node (mv.base)); g.new_edge (*cl, nm); - changeset& c (diff (nm, m, *cl, mv.current)); + changeset& c (diff (nm, m, *cl, in_name, ops, &mv)); if (!c.names_empty ()) { @@ -939,7 +1078,7 @@ namespace relational base = &g.new_node (mv.base); g.new_edge (*cl, *base); - changeset& c (diff (*base, *last, *cl, 0)); + changeset& c (diff (*base, *last, *cl, in_name, ops, 0)); if (!c.names_empty ()) { g.new_edge (c, *base); @@ -1000,7 +1139,7 @@ namespace relational om = &patch (*last, c, g); } - changeset& c (diff (*om, m, *cl, mv.current)); + changeset& c (diff (*om, m, *cl, in_name, ops, &mv)); if (!c.names_empty ()) { @@ -1019,7 +1158,7 @@ namespace relational // if (mv.base != mv.current) { - changeset& c (diff (*last, m, *cl, mv.current)); + changeset& c (diff (*last, m, *cl, in_name, ops, &mv)); if (!c.names_empty ()) { diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index 8920825..7b5fb78 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -755,6 +755,7 @@ namespace relational sema_rel::table& t (model_.new_node (id)); t.set ("cxx-location", c.location ()); + t.set ("class", &c); t.set ("deleted-map", deleted_column_map ()); model_.new_edge (model_, t, name); -- cgit v1.1