From 2aa3cabf1b737e225178230882ee9aadfd817ce0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 28 Mar 2013 16:04:48 +0200 Subject: Add changelog support for add/drop index/foreign key Also diagnose changes to primary keys and establish the 'alters' association. --- odb/relational/changelog.cxx | 357 +++++++++++++++++++++++++++++-- odb/semantics/relational/changelog.cxx | 84 +++++++- odb/semantics/relational/changeset.cxx | 10 +- odb/semantics/relational/changeset.hxx | 4 +- odb/semantics/relational/column.cxx | 29 ++- odb/semantics/relational/column.hxx | 58 +++-- odb/semantics/relational/elements.cxx | 38 +++- odb/semantics/relational/elements.hxx | 95 +++++++- odb/semantics/relational/elements.txx | 39 +++- odb/semantics/relational/foreign-key.cxx | 81 ++++++- odb/semantics/relational/foreign-key.hxx | 48 +++++ odb/semantics/relational/index.cxx | 75 ++++++- odb/semantics/relational/index.hxx | 48 ++++- odb/semantics/relational/key.cxx | 4 +- odb/semantics/relational/key.hxx | 14 +- odb/semantics/relational/model.cxx | 4 +- odb/semantics/relational/name.hxx | 12 ++ odb/semantics/relational/table.cxx | 33 ++- odb/semantics/relational/table.hxx | 18 +- odb/traversal/relational/foreign-key.hxx | 3 + odb/traversal/relational/index.hxx | 2 + 21 files changed, 927 insertions(+), 129 deletions(-) diff --git a/odb/relational/changelog.cxx b/odb/relational/changelog.cxx index 530c3a0..2208078 100644 --- a/odb/relational/changelog.cxx +++ b/odb/relational/changelog.cxx @@ -26,7 +26,10 @@ namespace relational // diff // - struct diff_table: trav_rel::column + struct diff_table: trav_rel::column, + trav_rel::primary_key, + trav_rel::foreign_key, + trav_rel::index { enum mode_type {mode_add, mode_drop}; @@ -43,20 +46,28 @@ namespace relational if (column* oc = other.find (c.name ())) { if (c.type () != oc->type ()) - diagnose_unsupported (c, "type"); + diagnose_column (c, "type", oc->type (), c.type ()); if (c.null () != oc->null ()) { alter_column& ac (g.new_node (c.id ())); + + // Set the alters edge. + // + column* b (at.lookup (c.name ())); + assert (b != 0); + g.new_edge (ac, *b); + ac.null (c.null ()); g.new_edge (at, ac, c.name ()); } if (c.default_ () != oc->default_ ()) - diagnose_unsupported (c, "default value"); + diagnose_column ( + c, "default value", oc->default_ (), c.default_ ()); if (c.options () != oc->options ()) - diagnose_unsupported (c, "options"); + diagnose_column (c, "options", oc->options (), c.options ()); } else { @@ -66,7 +77,7 @@ namespace relational } else { - if (other.find (c.name ()) == 0) + if (other.find (c.name ()) == 0) { drop_column& dc (g.new_node (c.id ())); g.new_edge (at, dc, c.name ()); @@ -74,27 +85,244 @@ namespace relational } } + virtual void + traverse (sema_rel::primary_key& pk) + { + using sema_rel::primary_key; + + if (mode == mode_add) + { + if (primary_key* opk = other.find (pk.name ())) + { + if (pk.auto_ () != opk->auto_ ()) + diagnose_primary_key (pk, "auto kind"); + + if (pk.contains_size () != opk->contains_size ()) + diagnose_primary_key (pk, "member set"); + + for (primary_key::contains_size_type i (0); + i != pk.contains_size (); ++i) + { + sema_rel::contains& c (pk.contains_at (i)); + sema_rel::contains& oc (opk->contains_at (i)); + + if (c.column ().name () != oc.column ().name ()) + diagnose_primary_key (pk, "member set"); + } + } + else + { + location const& l (pk.get ("cxx-location")); + error (l) << "adding object id to an existing class is " << + "not supported" << endl; + info (l) << "consider re-implementing this change by adding " << + "a new class with the object id, migrating the data, and " << + "deleteing the old class" << endl; + throw operation_failed (); + } + } + else + { + if (other.find (pk.name ()) == 0) + { + location const& l (other.get ("cxx-location")); + error (l) << "deleting object id from an existing class is " << + "not supported" << endl; + info (l) << "consider re-implementing this change by adding " << + "a new class without the object id, migrating the data, " << + "and deleteing the old class" << endl; + throw operation_failed (); + } + } + } + + virtual void + traverse (sema_rel::foreign_key& fk) + { + using sema_rel::foreign_key; + + if (mode == mode_add) + { + if (foreign_key* ofk = other.find (fk.name ())) + { + if (fk.deferred () != ofk->deferred ()) + diagnose_foreign_key (fk, "deferred kind"); + + if (fk.on_delete () != ofk->on_delete ()) + diagnose_foreign_key (fk, "on delete action"); + + if (fk.referenced_table () != ofk->referenced_table ()) + diagnose_foreign_key (fk, "pointed-to class"); + + if (fk.referenced_columns () != ofk->referenced_columns ()) + diagnose_foreign_key (fk, "id member set"); + + if (fk.contains_size () != ofk->contains_size ()) + diagnose_foreign_key (fk, "id member set"); + + for (foreign_key::contains_size_type i (0); + i != fk.contains_size (); ++i) + { + sema_rel::contains& c (fk.contains_at (i)); + sema_rel::contains& oc (ofk->contains_at (i)); + + if (c.column ().name () != oc.column ().name ()) + diagnose_foreign_key (fk, "id member set"); + } + } + else + { + add_foreign_key& afk (g.new_node (fk, at, g)); + g.new_edge (at, afk, fk.name ()); + } + } + else + { + if (other.find (fk.name ()) == 0) + { + drop_foreign_key& dfk (g.new_node (fk.id ())); + g.new_edge (at, dfk, fk.name ()); + } + } + } + + virtual void + traverse (sema_rel::index& i) + { + using sema_rel::index; + + if (mode == mode_add) + { + if (index* oi = other.find (i.name ())) + { + if (i.type () != oi->type ()) + diagnose_index (i, "type", oi->type (), i.type ()); + + if (i.method () != oi->method ()) + diagnose_index (i, "method", oi->method (), i.method ()); + + if (i.options () != oi->options ()) + diagnose_index (i, "options", oi->options (), i.options ()); + + if (i.contains_size () != oi->contains_size ()) + diagnose_index (i, "member set", "", ""); + + for (index::contains_size_type j (0); + j != i.contains_size (); ++j) + { + sema_rel::contains& c (i.contains_at (j)); + sema_rel::contains& oc (oi->contains_at (j)); + + if (c.column ().name () != oc.column ().name ()) + diagnose_index (i, "member set", "", ""); + + if (c.options () != oc.options ()) + diagnose_index ( + i, "member options", oc.options (), c.options ()); + } + } + else + { + add_index& ai (g.new_node (i, at, g)); + g.new_edge (at, ai, i.name ()); + } + } + else + { + if (other.find (i.name ()) == 0) + { + drop_index& di (g.new_node (i.id ())); + g.new_edge (at, di, i.name ()); + } + } + } + void - diagnose_unsupported (sema_rel::column& c, char const* name) + diagnose_column (sema_rel::column& c, + char const* name, + string const& ov, + string const& nv) { table& t (c.table ()); location const& tl (t.get ("cxx-location")); location const& cl (c.get ("cxx-location")); error (cl) << "change to data member results in the change of " << - "the corresponding column " << name << endl; + "the corresponding column " << name; + + if (!ov.empty () || !nv.empty ()) + cerr << " (old: '" << ov << "', new: '" << nv << "')"; + + cerr << endl; + error (cl) << "this change is not yet handled automatically" << endl; info (cl) << "corresponding column '" << c.name () << "' " << "originates here" << endl; info (tl) << "corresponding table '" << t.name () << "' " << "originates here" << endl; - info (cl) << "consider re-implementing this change by creating " << + info (cl) << "consider re-implementing this change by adding " << "a new data member with the desired " << name << ", migrating " << "the data, and deleting the old data member" << endl; throw operation_failed (); } + void + diagnose_primary_key (sema_rel::primary_key& pk, char const* name) + { + location const& l (pk.get ("cxx-location")); + + error (l) << "changing object id " << name << " in an existing " << + "class is not supported" << endl; + info (l) << "consider re-implementing this change by adding " << + "a new class with the desired object id " << name << ", " << + "migrating the data, and deleteing the old class" << endl; + + throw operation_failed (); + } + + void + diagnose_foreign_key (sema_rel::foreign_key& fk, char const* name) + { + location const& l (fk.get ("cxx-location")); + + error (l) << "changing object pointer " << name << " is not " << + "supported" << endl; + info (l) << "consider re-implementing this change by adding " << + "a new object pointer with the desired " << name << ", " << + "migrating the data, and deleteing the old pointer" << endl; + + throw operation_failed (); + } + + void + diagnose_index (sema_rel::index& i, + char const* name, + string const& ov, + string const& nv) + { + table& t (i.table ()); + location const& tl (t.get ("cxx-location")); + location const& il (i.get ("cxx-location")); + + error (il) << "change to index " << name; + + if (!ov.empty () || !nv.empty ()) + cerr << " (old: '" << ov << "', new: '" << nv << "')"; + + cerr << " is not yet handled automatically" << endl; + + info (il) << "corresponding index '" << i.name () << "' " << + "originates here" << endl; + info (tl) << "corresponding table '" << t.name () << "' " << + "originates here" << endl; + info (il) << "consider re-implementing this change by adding " << + "a new index with the desired " << name << " and deleting the " << + "old one" << endl; + + throw operation_failed (); + } + protected: table& other; mode_type mode; @@ -122,6 +350,12 @@ namespace relational // alter_table& at (g.new_node (t.id ())); + // Set the alters edge for lookup. + // + table* bt (cs.lookup (t.name ())); + assert (bt != 0); + alters& ae (g.new_edge (at, *bt)); + { trav_rel::table table; trav_rel::unames names; @@ -141,7 +375,10 @@ namespace relational if (!at.names_empty ()) g.new_edge (cs, at, t.name ()); else + { + g.delete_edge (at, *bt, ae); g.delete_node (at); + } } else { @@ -171,14 +408,21 @@ namespace relational // Assumes the new model has cxx-location set. // changeset& - diff (model& o, model& n, graph& g) + diff (model& o, model& n, changelog& l) { - changeset& r (g.new_node (n.version ())); + changeset& r (l.new_node (n.version ())); + + // Set the alters edge for lookup. + // + l.new_edge (r, + l.contains_changeset_empty () + ? static_cast (l.model ()) + : l.contains_changeset_back ().changeset ()); { trav_rel::model model; trav_rel::qnames names; - diff_model dmodel (o, diff_model::mode_add, r, g); + diff_model dmodel (o, diff_model::mode_add, r, l); model >> names >> dmodel; model.traverse (n); } @@ -186,7 +430,7 @@ namespace relational { trav_rel::model model; trav_rel::qnames names; - diff_model dmodel (n, diff_model::mode_drop, r, g); + diff_model dmodel (n, diff_model::mode_drop, r, l); model >> names >> dmodel; model.traverse (o); } @@ -200,7 +444,11 @@ namespace relational struct patch_table: trav_rel::add_column, trav_rel::drop_column, - trav_rel::alter_column + trav_rel::alter_column, + trav_rel::add_index, + trav_rel::drop_index, + trav_rel::add_foreign_key, + trav_rel::drop_foreign_key { patch_table (table& tl, graph& gr): t (tl), g (gr) {} @@ -251,6 +499,74 @@ namespace relational } } + virtual void + traverse (sema_rel::add_index& ai) + { + using sema_rel::index; + + try + { + index& i (g.new_node (ai, t, g)); + g.new_edge (t, i, ai.name ()); + } + catch (duplicate_name const&) + { + cerr << "error: invalid changelog: index '" << ai.name () << + "' already exists in table '" << t.name () << "'" << endl; + throw operation_failed (); + } + } + + virtual void + traverse (sema_rel::drop_index& di) + { + using sema_rel::index; + table::names_iterator i (t.find (di.name ())); + + if (i == t.names_end () || !i->nameable ().is_a ()) + { + cerr << "error: invalid changelog: index '" << di.name () << + "' does not exist in table '" << t.name () << "'" << endl; + throw operation_failed (); + } + + g.delete_edge (t, i->nameable (), *i); + } + + virtual void + traverse (sema_rel::add_foreign_key& afk) + { + using sema_rel::foreign_key; + + try + { + foreign_key& fk (g.new_node (afk, t, g)); + g.new_edge (t, fk, afk.name ()); + } + catch (duplicate_name const&) + { + cerr << "error: invalid changelog: foreign key '" << afk.name () << + "' already exists in table '" << t.name () << "'" << endl; + throw operation_failed (); + } + } + + virtual void + traverse (sema_rel::drop_foreign_key& dfk) + { + using sema_rel::foreign_key; + table::names_iterator i (t.find (dfk.name ())); + + if (i == t.names_end () || !i->nameable ().is_a ()) + { + cerr << "error: invalid changelog: foreign key '" << dfk.name () << + "' does not exist in table '" << t.name () << "'" << endl; + throw operation_failed (); + } + + g.delete_edge (t, i->nameable (), *i); + } + protected: table& t; graph& g; @@ -389,7 +705,7 @@ namespace relational // if (!mv.open) { - changeset& cs (diff (oldm, m, g)); + changeset& cs (diff (oldm, m, *cl)); if (!cs.names_empty ()) { @@ -432,7 +748,7 @@ namespace relational if (!mv.open) { model& old (patch (*last, cs, g)); - changeset& cs (diff (old, m, g)); + changeset& cs (diff (old, m, *cl)); if (!cs.names_empty ()) { @@ -458,7 +774,14 @@ namespace relational if (last->version () <= mv.base) continue; - g.new_edge (*cl, g.new_node (cs, g)); + changeset& c ( + g.new_node ( + cs, + cl->contains_changeset_empty () + ? static_cast (*base) // Cannot be NULL. + : cl->contains_changeset_back ().changeset (), + g)); + g.new_edge (*cl, c); } // If we still haven't found the new base model, then take the @@ -473,7 +796,7 @@ namespace relational // Add a changeset for the current version. // - changeset& cs (diff (*last, m, g)); + changeset& cs (diff (*last, m, *cl)); if (!cs.names_empty ()) g.new_edge (*cl, cs); diff --git a/odb/semantics/relational/changelog.cxx b/odb/semantics/relational/changelog.cxx index cd94494..fb304ca 100644 --- a/odb/semantics/relational/changelog.cxx +++ b/odb/semantics/relational/changelog.cxx @@ -3,6 +3,7 @@ // license : GNU GPL v3; see accompanying LICENSE file #include +#include #include @@ -10,6 +11,8 @@ #include #include +using namespace std; + namespace semantics { namespace relational @@ -26,10 +29,12 @@ namespace semantics if (p.attribute ("version") != 1) throw parsing (p, "unsupported changelog format version"); - // Get the changesets. Because they are stored in the reverse order, - // first save them to the temporary vector. + // Because things are stored in the reverse order, first save the + // changesets as XML chunks and then re-parse them in the reverse + // order. We have to do it this way so that we can do lookups along + // the alters edges. // - typedef std::vector changesets; + typedef vector changesets; changesets cs; for (parser::event_type e (p.peek ()); @@ -39,22 +44,79 @@ namespace semantics if (p.qname () != xml::qname (xmlns, "changeset")) break; // Not our elements. - p.next (); - cs.push_back (&new_node (p, *this)); - p.next_expect (parser::end_element); - } + ostringstream os; + os.exceptions (ios_base::badbit | ios_base::failbit); + serializer s (os, "changeset", 0); // No pretty-printing. + size_t depth (0); - for (changesets::reverse_iterator i (cs.rbegin ()); i != cs.rend (); ++i) - new_edge (*this, **i); + do + { + switch (p.next ()) + { + case parser::start_element: + { + s.start_element (p.qname ()); + + if (depth == 0) + s.namespace_decl (xmlns, ""); + + typedef parser::attribute_map_type attr_map; + attr_map const& am (p.attribute_map ()); + + for (attr_map::const_iterator i (am.begin ()); + i != am.end (); ++i) + s.attribute (i->first, i->second.value); + + depth++; + break; + } + case parser::end_element: + { + depth--; + s.end_element (); + break; + } + case parser::characters: + { + s.characters (p.value ()); + break; + } + default: + { + depth = 0; + break; + } + } + } while (depth != 0); + + cs.push_back (os.str ()); + } // Get the model. // p.next_expect (parser::start_element, xmlns, "model"); - model_type& m (new_node (p, *this)); new_edge (*this, m); - p.next_expect (parser::end_element); + + // Re-parse the changesets in reverse order. + // + qscope* base (&m); + for (changesets::reverse_iterator i (cs.rbegin ()); i != cs.rend (); ++i) + { + istringstream is (*i); + is.exceptions (ios_base::badbit | ios_base::failbit); + parser ip (is, p.input_name ()); + + ip.next_expect (parser::start_element, xmlns, "changeset"); + + changeset& c (new_node (ip, *base, *this)); + new_edge (*this, c); + base = &c; + + ip.next_expect (parser::end_element); + } + p.next_expect (parser::end_element); } diff --git a/odb/semantics/relational/changeset.cxx b/odb/semantics/relational/changeset.cxx index 8004844..fd5a17f 100644 --- a/odb/semantics/relational/changeset.cxx +++ b/odb/semantics/relational/changeset.cxx @@ -11,15 +11,15 @@ namespace semantics namespace relational { changeset:: - changeset (changeset const& c, graph& g) - : qscope (c, g), - version_ (c.version_) + changeset (changeset const& c, qscope& b, graph& g) + : qscope (c, &b, g), + version_ (c.version_) { } changeset:: - changeset (xml::parser& p, graph& g) - : qscope (p, g), + changeset (xml::parser& p, qscope& b, graph& g) + : qscope (p, &b, g), version_ (p.attribute ("version")) { } diff --git a/odb/semantics/relational/changeset.hxx b/odb/semantics/relational/changeset.hxx index 575eb7b..76fd683 100644 --- a/odb/semantics/relational/changeset.hxx +++ b/odb/semantics/relational/changeset.hxx @@ -21,8 +21,8 @@ namespace semantics public: changeset (version_type v): version_ (v) {} - changeset (changeset const&, graph&); - changeset (xml::parser&, graph&); + changeset (changeset const&, qscope& base, graph&); + changeset (xml::parser&, qscope& base, graph&); virtual string kind () const {return "changeset";} diff --git a/odb/semantics/relational/column.cxx b/odb/semantics/relational/column.cxx index f90b94e..1a47ef7 100644 --- a/odb/semantics/relational/column.cxx +++ b/odb/semantics/relational/column.cxx @@ -104,20 +104,26 @@ namespace semantics // alter_column // alter_column:: - alter_column (alter_column const& ac, uscope&, graph& g) - : unameable (ac, g), - null_altered_ (ac.null_altered_), - null_ (ac.null_) + alter_column (alter_column const& ac, uscope& s, graph& g) + : column (ac, s, g), + alters_ (0), + null_altered_ (ac.null_altered_) { + column* b (s.lookup (ac.name ())); + assert (b != 0); + g.new_edge (*this, *b); } alter_column:: - alter_column (xml::parser& p, uscope&, graph& g) - : unameable (p, g), - null_altered_ (p.attribute_present ("null")), - null_ (null_altered_ ? p.attribute ("null") : false) + alter_column (xml::parser& p, uscope& s, graph& g) + : column (p, s, g), + alters_ (0), + null_altered_ (p.attribute_present ("null")) { - p.content (xml::parser::empty); + name_type n (p.attribute ("name")); + column* b (s.lookup (n)); + assert (b != 0); + g.new_edge (*this, *b); } alter_column& alter_column:: @@ -130,6 +136,9 @@ namespace semantics serialize (xml::serializer& s) const { s.start_element (xmlns, "alter-column"); + + // Here we override the standard column logic. + // unameable::serialize_attributes (s); if (null_altered_) @@ -183,7 +192,7 @@ namespace semantics // { type_info ti (typeid (alter_column)); - ti.add_base (typeid (unameable)); + ti.add_base (typeid (column)); insert (ti); } } diff --git a/odb/semantics/relational/column.hxx b/odb/semantics/relational/column.hxx index 7702a13..11a76e0 100644 --- a/odb/semantics/relational/column.hxx +++ b/odb/semantics/relational/column.hxx @@ -19,35 +19,32 @@ namespace semantics typedef std::vector contained_list; public: - string const& + virtual string const& type () const {return type_;} - bool + virtual bool null () const {return null_;} - void + virtual void null (bool n) {null_ = n;} - string const& + virtual string const& default_ () const {return default__;} - void + virtual void default_ (string const& d) {default__ = d;} - string const& + virtual string const& options () const {return options_;} - void + virtual void options (string const& o) {options_ = o;} public: typedef relational::table table_type; table_type& - table () const - { - return dynamic_cast (scope ()); - } + table () const {return dynamic_cast (scope ());} // Key containment. // @@ -95,12 +92,13 @@ namespace semantics void serialize_attributes (xml::serializer&) const; - private: + protected: string type_; bool null_; string default__; string options_; + private: contained_list contained_; }; @@ -140,24 +138,34 @@ namespace semantics serialize (xml::serializer&) const; }; - class alter_column: public unameable + class alter_column: public column { public: + virtual string const& + type () const {return base ().type ();} + bool null_altered () const {return null_altered_;} - bool - null () const {return null_;} + virtual bool + null () const {return null_altered_ ? null_ : base ().null ();} - void + virtual void null (bool n) {null_ = n; null_altered_ = true;} + virtual string const& + default_ () const {return base ().default_ ();} + + virtual string const& + options () const {return base ().options ();} + public: - alter_column (string const& id) - : unameable (id), null_altered_ (false) - { - } + column& + base () const {return dynamic_cast (alters_->base ());} + public: + alter_column (string const& id) + : column (id, "", false), alters_ (0), null_altered_ (false) {} alter_column (alter_column const&, uscope&, graph&); alter_column (xml::parser&, uscope&, graph&); @@ -173,9 +181,17 @@ namespace semantics virtual void serialize (xml::serializer&) const; + virtual void + add_edge_left (alters& a) + { + assert (alters_ == 0); + alters_ = &a; + } + private: + alters* alters_; + bool null_altered_; - bool null_; }; } } diff --git a/odb/semantics/relational/elements.cxx b/odb/semantics/relational/elements.cxx index e14bc00..d83bff7 100644 --- a/odb/semantics/relational/elements.cxx +++ b/odb/semantics/relational/elements.cxx @@ -45,23 +45,36 @@ namespace semantics { typename names_list::iterator i; - // We want the order to be columns first, then the primary key, - // and then the foreign keys. + // We want the order to be add/alter columns first, then the + // primary key, then other keys, and finnally drop columns. // - if (n.is_a ()) + if (n.is_a () || + n.is_a () || + n.is_a ()) + { i = names_.insert (first_key_, &e); - else + } + else if (!n.is_a ()) { if (n.is_a ()) - first_key_ = i = names_.insert (first_key_, &e); + first_key_ = i = names_.insert ( + first_key_ != names_.end () ? first_key_ : first_drop_column_, + &e); else { - i = names_.insert (names_.end (), &e); + i = names_.insert (first_drop_column_, &e); if (first_key_ == names_.end ()) first_key_ = i; } } + else + { + i = names_.insert (names_.end (), &e); + + if (first_drop_column_ == names_.end ()) + first_drop_column_ = i; + } names_map_[name] = i; iterator_map_[&e] = i; @@ -83,6 +96,11 @@ namespace semantics if (first_key_ == i->second) first_key_++; + // The same for the first drop column. + // + if (first_drop_column_ == i->second) + first_drop_column_++; + names_.erase (i->second); names_map_.erase (e.name ()); iterator_map_.erase (i); @@ -106,6 +124,14 @@ namespace semantics // insert (type_info (typeid (edge))); + // alters + // + { + type_info ti (typeid (alters)); + ti.add_base (typeid (edge)); + insert (ti); + } + // names // { diff --git a/odb/semantics/relational/elements.hxx b/odb/semantics/relational/elements.hxx index 381d7a8..11e7ea8 100644 --- a/odb/semantics/relational/elements.hxx +++ b/odb/semantics/relational/elements.hxx @@ -93,9 +93,57 @@ namespace semantics // public: void - add_edge_right (edge&) + add_edge_right (edge&) {} + + void + remove_edge_right (edge&) {} + }; + + // + // + class alters: public edge + { + public: + node& + base () const {return *base_;} + + node& + modifier () const {return *modifier_;} + + public: + alters () : base_ (0), modifier_ (0) {} + + void + set_left_node (node& m) + { + assert (modifier_ == 0); + modifier_ = &m; + } + + void + set_right_node (node& b) { + assert (base_ == 0); + base_ = &b; } + + void + clear_left_node (node& m) + { + assert (modifier_ == &m); + modifier_ = 0; + } + + void + clear_right_node (node& b) + { + assert (base_ == &b); + base_ = 0; + } + + protected: + node* base_; + node* modifier_; }; // @@ -224,6 +272,7 @@ namespace semantics } using node::add_edge_right; + using node::remove_edge_right; protected: nameable (nameable const&, graph& g); @@ -319,7 +368,7 @@ namespace semantics return names_.empty (); } - // Find. + // Find (this scope only). // template T* @@ -337,30 +386,64 @@ namespace semantics names_const_iterator find (names_type const&) const; + // Lookup in this and all altered scopes until we find what we are + // looking for or hit a stop node of type S (e.g., drop_*). + // + template + T* + lookup (name_type const&); + + public: + scope* + base () const + { + return alters_ != 0 ? &dynamic_cast (alters_->base ()) : 0; + } + public: - scope (): first_key_ (names_.end ()) {} + scope () + : first_key_ (names_.end ()), + first_drop_column_ (names_.end ()), + alters_ (0) {} // Virtual because we call it via scope interface (e.g., in copy). // virtual void + add_edge_left (alters& a) + { + assert (alters_ == 0); + alters_ = &a; + } + + virtual void + remove_edge_left (alters& a) + { + assert (alters_ == &a); + alters_ = 0; + } + + virtual void add_edge_left (names_type&); virtual void remove_edge_left (names_type&); protected: - scope (scope const&, graph&); - scope (xml::parser&, graph&); + scope (scope const&, scope* base, graph&); + scope (xml::parser&, scope* base, graph&); void serialize_content (xml::serializer&) const; - private: + protected: names_list names_; names_map names_map_; names_iterator_map iterator_map_; typename names_list::iterator first_key_; + typename names_list::iterator first_drop_column_; + + alters* alters_; }; template <> diff --git a/odb/semantics/relational/elements.txx b/odb/semantics/relational/elements.txx index e1fd6d5..725fc3f 100644 --- a/odb/semantics/relational/elements.txx +++ b/odb/semantics/relational/elements.txx @@ -54,6 +54,23 @@ namespace semantics // template + template + T* scope:: + lookup (name_type const& name) + { + if (T* r = find (name)) + return r; + + if (scope* b = base ()) + { + if (find (name) == 0) + return b->lookup (name); + } + + return 0; + } + + template template T* scope:: find (name_type const& name) @@ -106,9 +123,16 @@ namespace semantics template scope:: - scope (scope const& s, graph& g) - : first_key_ (names_.end ()) + scope (scope const& s, scope* base, graph& g) + : first_key_ (names_.end ()), + first_drop_column_ (names_.end ()), + alters_ (0) { + // Set the alters edge for lookup. + // + if (base != 0) + g.new_edge (*this, *base); + for (names_const_iterator i (s.names_begin ()); i != s.names_end (); ++i) { @@ -119,9 +143,16 @@ namespace semantics template scope:: - scope (xml::parser& p, graph& g) - : first_key_ (names_.end ()) + scope (xml::parser& p, scope* base, graph& g) + : first_key_ (names_.end ()), + first_drop_column_ (names_.end ()), + alters_ (0) { + // Set the alters edge for lookup. + // + if (base != 0) + g.new_edge (*this, *base); + using namespace xml; p.content (parser::complex); diff --git a/odb/semantics/relational/foreign-key.cxx b/odb/semantics/relational/foreign-key.cxx index 7ef88a6..0407858 100644 --- a/odb/semantics/relational/foreign-key.cxx +++ b/odb/semantics/relational/foreign-key.cxx @@ -89,11 +89,9 @@ namespace semantics return g.new_node (*this, s, g); } - void foreign_key:: - serialize (xml::serializer& s) const + serialize_attributes (xml::serializer& s) const { - s.start_element (xmlns, "foreign-key"); key::serialize_attributes (s); if (deferred ()) @@ -101,7 +99,11 @@ namespace semantics if (on_delete () != no_action) s.attribute ("on-delete", on_delete ()); + } + void foreign_key:: + serialize_content (xml::serializer& s) const + { key::serialize_content (s); // Referenced columns. @@ -118,9 +120,57 @@ namespace semantics } s.end_element (); // references + } + + void foreign_key:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "foreign-key"); + serialize_attributes (s); + serialize_content (s); s.end_element (); // foreign-key } + // add_foreign_key + // + add_foreign_key& add_foreign_key:: + clone (uscope& s, graph& g) const + { + return g.new_node (*this, s, g); + } + + void add_foreign_key:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "add-foreign-key"); + foreign_key::serialize_attributes (s); + foreign_key::serialize_content (s); + s.end_element (); + } + + // drop_foreign_key + // + drop_foreign_key:: + drop_foreign_key (xml::parser& p, uscope&, graph& g) + : unameable (p, g) + { + p.content (xml::parser::empty); + } + + drop_foreign_key& drop_foreign_key:: + clone (uscope& s, graph& g) const + { + return g.new_node (*this, s, g); + } + + void drop_foreign_key:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "drop-foreign-key"); + unameable::serialize_attributes (s); + s.end_element (); + } + // type info // namespace @@ -129,16 +179,37 @@ namespace semantics { init () { - unameable::parser_map_["foreign-key"] = - &unameable::parser_impl; + unameable::parser_map& m (unameable::parser_map_); + + m["foreign-key"] = &unameable::parser_impl; + m["add-foreign-key"] = &unameable::parser_impl; + m["drop-foreign-key"] = &unameable::parser_impl; using compiler::type_info; + // foreign_key + // { type_info ti (typeid (foreign_key)); ti.add_base (typeid (key)); insert (ti); } + + // add_foreign_key + // + { + type_info ti (typeid (add_foreign_key)); + ti.add_base (typeid (foreign_key)); + insert (ti); + } + + // drop_foreign_key + // + { + type_info ti (typeid (drop_foreign_key)); + ti.add_base (typeid (unameable)); + insert (ti); + } } } init_; } diff --git a/odb/semantics/relational/foreign-key.hxx b/odb/semantics/relational/foreign-key.hxx index 5d551fe..50a3fae 100644 --- a/odb/semantics/relational/foreign-key.hxx +++ b/odb/semantics/relational/foreign-key.hxx @@ -83,6 +83,13 @@ namespace semantics virtual void serialize (xml::serializer&) const; + protected: + void + serialize_attributes (xml::serializer&) const; + + void + serialize_content (xml::serializer&) const; + private: qname referenced_table_; columns referenced_columns_; @@ -95,6 +102,47 @@ namespace semantics std::istream& operator>> (std::istream&, foreign_key::action_type&); + + class add_foreign_key: public foreign_key + { + public: + add_foreign_key (string const& id, + qname const& rt, + bool d, + action_type od = no_action) + : foreign_key (id, rt, d, od) {} + add_foreign_key (foreign_key const& fk, uscope& s, graph& g) + : foreign_key (fk, s, g) {} + add_foreign_key (xml::parser& p, uscope& s, graph& g) + : foreign_key (p, s, g) {} + + virtual add_foreign_key& + clone (uscope&, graph&) const; + + virtual string + kind () const {return "add foreign key";} + + virtual void + serialize (xml::serializer&) const; + }; + + class drop_foreign_key: public unameable + { + public: + drop_foreign_key (string const& id): unameable (id) {} + drop_foreign_key (drop_foreign_key const& dfk, uscope&, graph& g) + : unameable (dfk, g) {} + drop_foreign_key (xml::parser&, uscope&, graph&); + + virtual drop_foreign_key& + clone (uscope&, graph&) const; + + virtual string + kind () const {return "drop foreign key";} + + virtual void + serialize (xml::serializer&) const; + }; } } diff --git a/odb/semantics/relational/index.cxx b/odb/semantics/relational/index.cxx index 5ff1a26..b38c6e4 100644 --- a/odb/semantics/relational/index.cxx +++ b/odb/semantics/relational/index.cxx @@ -10,6 +10,8 @@ namespace semantics { namespace relational { + // index + // index:: index (index const& i, uscope& s, graph& g) : key (i, s, g), @@ -35,9 +37,8 @@ namespace semantics } void index:: - serialize (xml::serializer& s) const + serialize_attributes (xml::serializer& s) const { - s.start_element (xmlns, "index"); key::serialize_attributes (s); if (!type ().empty ()) @@ -48,11 +49,57 @@ namespace semantics if (!options ().empty ()) s.attribute ("options", options ()); + } + void index:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "index"); + serialize_attributes (s); key::serialize_content (s); s.end_element (); } + // add_index + // + add_index& add_index:: + clone (uscope& s, graph& g) const + { + return g.new_node (*this, s, g); + } + + void add_index:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "add-index"); + index::serialize_attributes (s); + index::serialize_content (s); + s.end_element (); + } + + // drop_index + // + drop_index:: + drop_index (xml::parser& p, uscope&, graph& g) + : unameable (p, g) + { + p.content (xml::parser::empty); + } + + drop_index& drop_index:: + clone (uscope& s, graph& g) const + { + return g.new_node (*this, s, g); + } + + void drop_index:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "drop-index"); + unameable::serialize_attributes (s); + s.end_element (); + } + // type info // namespace @@ -61,15 +108,37 @@ namespace semantics { init () { - unameable::parser_map_["index"] = &unameable::parser_impl; + unameable::parser_map& m (unameable::parser_map_); + + m["index"] = &unameable::parser_impl; + m["add-index"] = &unameable::parser_impl; + m["drop-index"] = &unameable::parser_impl; using compiler::type_info; + // index + // { type_info ti (typeid (index)); ti.add_base (typeid (key)); insert (ti); } + + // add_index + // + { + type_info ti (typeid (add_index)); + ti.add_base (typeid (index)); + insert (ti); + } + + // drop_index + // + { + type_info ti (typeid (drop_index)); + ti.add_base (typeid (unameable)); + insert (ti); + } } } init_; } diff --git a/odb/semantics/relational/index.hxx b/odb/semantics/relational/index.hxx index 621f36a..500c341 100644 --- a/odb/semantics/relational/index.hxx +++ b/odb/semantics/relational/index.hxx @@ -31,10 +31,7 @@ namespace semantics string const& t = string (), string const& m = string (), string const& o = string ()) - : key (id), type_ (t), method_ (m), options_ (o) - { - } - + : key (id), type_ (t), method_ (m), options_ (o) {} index (index const&, uscope&, graph&); index (xml::parser&, uscope&, graph&); @@ -50,11 +47,54 @@ namespace semantics virtual void serialize (xml::serializer&) const; + protected: + void + serialize_attributes (xml::serializer&) const; + private: string type_; // E.g., "UNIQUE", etc. string method_; // E.g., "BTREE", etc. string options_; // Database-specific index options. }; + + class add_index: public index + { + public: + add_index (string const& id, + string const& t = string (), + string const& m = string (), + string const& o = string ()) + : index (id, t, m, o) {} + add_index (index const& i, uscope& s, graph& g): index (i, s, g) {} + add_index (xml::parser& p, uscope& s, graph& g): index (p, s, g) {} + + virtual add_index& + clone (uscope&, graph&) const; + + virtual string + kind () const {return "add index";} + + virtual void + serialize (xml::serializer&) const; + }; + + class drop_index: public unameable + { + public: + drop_index (string const& id): unameable (id) {} + drop_index (drop_index const& di, uscope&, graph& g) + : unameable (di, g) {} + drop_index (xml::parser&, uscope&, graph&); + + virtual drop_index& + clone (uscope&, graph&) const; + + virtual string + kind () const {return "drop index";} + + virtual void + serialize (xml::serializer&) const; + }; } } diff --git a/odb/semantics/relational/key.cxx b/odb/semantics/relational/key.cxx index e27253c..dc0fbe0 100644 --- a/odb/semantics/relational/key.cxx +++ b/odb/semantics/relational/key.cxx @@ -18,7 +18,7 @@ namespace semantics for (contains_iterator i (k.contains_begin ()); i != k.contains_end (); ++i) { - column* c (s.find (i->column ().name ())); + column* c (s.lookup (i->column ().name ())); assert (c != 0); g.new_edge (*this, *c, i->options ()); } @@ -42,7 +42,7 @@ namespace semantics p.content (parser::empty); uname n (p.attribute ("name")); - column* c (s.find (n)); + column* c (s.lookup (n)); if (c == 0) throw parsing (p, "invalid column name in the 'name' attribute"); diff --git a/odb/semantics/relational/key.hxx b/odb/semantics/relational/key.hxx index 8d64993..184433e 100644 --- a/odb/semantics/relational/key.hxx +++ b/odb/semantics/relational/key.hxx @@ -6,6 +6,7 @@ #define ODB_SEMANTICS_RELATIONAL_KEY_HXX #include +#include namespace semantics { @@ -55,6 +56,8 @@ namespace semantics typedef std::vector contains_list; public: + typedef contains_list::size_type contains_size_type; + typedef pointer_iterator contains_iterator; @@ -65,9 +68,18 @@ namespace semantics contains_iterator contains_end () const {return contains_.end ();} - contains_list::size_type + contains_size_type contains_size () const {return contains_.size ();} + contains& + contains_at (contains_size_type i) const {return *contains_[i];} + + public: + typedef relational::table table_type; + + table_type& + table () const {return dynamic_cast (scope ());} + public: key (std::string const& id): unameable (id) {} diff --git a/odb/semantics/relational/model.cxx b/odb/semantics/relational/model.cxx index 6515591..678debe 100644 --- a/odb/semantics/relational/model.cxx +++ b/odb/semantics/relational/model.cxx @@ -12,14 +12,14 @@ namespace semantics { model:: model (model const& m, graph& g) - : qscope (m, g), + : qscope (m, 0, g), version_ (m.version_) { } model:: model (xml::parser& p, graph& g) - : qscope (p, g), + : qscope (p, 0, g), version_ (p.attribute ("version")) { } diff --git a/odb/semantics/relational/name.hxx b/odb/semantics/relational/name.hxx index 18c7509..06794be 100644 --- a/odb/semantics/relational/name.hxx +++ b/odb/semantics/relational/name.hxx @@ -125,6 +125,18 @@ namespace semantics public: friend bool + operator== (qname const& x, qname const& y) + { + return x.components_ == y.components_; + } + + friend bool + operator!= (qname const& x, qname const& y) + { + return x.components_ != y.components_; + } + + friend bool operator< (qname const& x, qname const& y) { return x.components_ < y.components_; diff --git a/odb/semantics/relational/table.cxx b/odb/semantics/relational/table.cxx index 682196c..17265bd 100644 --- a/odb/semantics/relational/table.cxx +++ b/odb/semantics/relational/table.cxx @@ -13,14 +13,20 @@ namespace semantics // table // table:: - table (table const& t, qscope&, graph& g) - : qnameable (t, g), uscope (t, g) + table (table const& t, qscope& s, graph& g, bool b) + : qnameable (t, g), + uscope (t, (b ? s.lookup (t.name ()) : 0), g) { } table:: - table (xml::parser& p, qscope&, graph& g) - : qnameable (p, g), uscope (p, g) + table (xml::parser& p, qscope& s, graph& g, bool b) + : qnameable (p, g), + uscope ( + p, + (b ? s.lookup ( + p.attribute ("name")) : 0), + g) { } @@ -81,18 +87,6 @@ namespace semantics // alter_table // - alter_table:: - alter_table (alter_table const& t, qscope&, graph& g) - : qnameable (t, g), uscope (t, g) - { - } - - alter_table:: - alter_table (xml::parser& p, qscope&, graph& g) - : qnameable (p, g), uscope (p, g) - { - } - alter_table& alter_table:: clone (qscope& s, graph& g) const { @@ -103,8 +97,8 @@ namespace semantics serialize (xml::serializer& s) const { s.start_element (xmlns, "alter-table"); - qnameable::serialize_attributes (s); - uscope::serialize_content (s); + table::serialize_attributes (s); + table::serialize_content (s); s.end_element (); } @@ -154,8 +148,7 @@ namespace semantics // { type_info ti (typeid (alter_table)); - ti.add_base (typeid (qnameable)); - ti.add_base (typeid (uscope)); + ti.add_base (typeid (table)); insert (ti); } } diff --git a/odb/semantics/relational/table.hxx b/odb/semantics/relational/table.hxx index effd552..ae4c96c 100644 --- a/odb/semantics/relational/table.hxx +++ b/odb/semantics/relational/table.hxx @@ -15,8 +15,8 @@ namespace semantics { public: table (string const& id): qnameable (id) {} - table (table const&, qscope&, graph&); - table (xml::parser&, qscope&, graph&); + table (table const&, qscope&, graph&, bool base = false); + table (xml::parser&, qscope&, graph&, bool base = false); virtual table& clone (qscope&, graph&) const; @@ -66,12 +66,14 @@ namespace semantics serialize (xml::serializer&) const; }; - class alter_table: public qnameable, public uscope + class alter_table: public table { public: - alter_table (string const& id): qnameable (id) {} - alter_table (alter_table const&, qscope&, graph&); - alter_table (xml::parser&, qscope&, graph&); + alter_table (string const& id): table (id) {} + alter_table (alter_table const& at, qscope& s, graph& g) + : table (at, s, g, true) {} + alter_table (xml::parser& p, qscope& s, graph& g) + : table (p, s, g, true) {} virtual alter_table& clone (qscope&, graph&) const; @@ -81,10 +83,6 @@ namespace semantics virtual void serialize (xml::serializer&) const; - - // Resolve ambiguity. - // - using qnameable::scope; }; } } diff --git a/odb/traversal/relational/foreign-key.hxx b/odb/traversal/relational/foreign-key.hxx index d80df26..1dc54e9 100644 --- a/odb/traversal/relational/foreign-key.hxx +++ b/odb/traversal/relational/foreign-key.hxx @@ -13,6 +13,9 @@ namespace traversal namespace relational { struct foreign_key: key_template {}; + struct add_foreign_key: + key_template {}; + struct drop_foreign_key: node {}; } } diff --git a/odb/traversal/relational/index.hxx b/odb/traversal/relational/index.hxx index f45dda7..7426ab4 100644 --- a/odb/traversal/relational/index.hxx +++ b/odb/traversal/relational/index.hxx @@ -13,6 +13,8 @@ namespace traversal namespace relational { struct index: key_template {}; + struct add_index: key_template {}; + struct drop_index: node {}; } } -- cgit v1.1