From e999b1e7295acd8cdb646c2db7db1f5059f10c7d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 26 Mar 2013 13:03:13 +0200 Subject: Add changelog support for add, drop, and later column --- odb/relational/changelog.cxx | 201 +++++++++++++++++++++++++++++++++++- odb/semantics/relational/column.cxx | 116 ++++++++++++++++++++- odb/semantics/relational/column.hxx | 81 +++++++++++++++ odb/semantics/relational/table.cxx | 49 ++++++++- odb/semantics/relational/table.hxx | 20 ++++ odb/traversal/relational/column.hxx | 3 + odb/traversal/relational/table.hxx | 1 + 7 files changed, 462 insertions(+), 9 deletions(-) diff --git a/odb/relational/changelog.cxx b/odb/relational/changelog.cxx index 7435f05..530c3a0 100644 --- a/odb/relational/changelog.cxx +++ b/odb/relational/changelog.cxx @@ -22,6 +22,86 @@ namespace relational namespace { + // + // diff + // + + struct diff_table: trav_rel::column + { + 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) {} + + virtual void + traverse (sema_rel::column& c) + { + using sema_rel::column; + + if (mode == mode_add) + { + if (column* oc = other.find (c.name ())) + { + if (c.type () != oc->type ()) + diagnose_unsupported (c, "type"); + + if (c.null () != oc->null ()) + { + alter_column& ac (g.new_node (c.id ())); + ac.null (c.null ()); + g.new_edge (at, ac, c.name ()); + } + + if (c.default_ () != oc->default_ ()) + diagnose_unsupported (c, "default value"); + + if (c.options () != oc->options ()) + diagnose_unsupported (c, "options"); + } + else + { + add_column& ac (g.new_node (c, at, g)); + g.new_edge (at, ac, c.name ()); + } + } + else + { + if (other.find (c.name ()) == 0) + { + drop_column& dc (g.new_node (c.id ())); + g.new_edge (at, dc, c.name ()); + } + } + } + + void + diagnose_unsupported (sema_rel::column& c, char const* name) + { + 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; + 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 " << + "a new data member with the desired " << name << ", migrating " << + "the data, and deleting the old data member" << endl; + + throw operation_failed (); + } + + protected: + table& other; + mode_type mode; + alter_table& at; + graph& g; + }; + struct diff_model: trav_rel::table { enum mode_type {mode_add, mode_drop}; @@ -32,17 +112,48 @@ namespace relational virtual void traverse (sema_rel::table& t) { + using sema_rel::table; + if (mode == mode_add) { - if (other.find (t.name ()) == 0) + if (table* ot = other.find (t.name ())) { + // See if there are any changes to the table. + // + alter_table& at (g.new_node (t.id ())); + + { + trav_rel::table table; + trav_rel::unames names; + diff_table dtable (*ot, diff_table::mode_add, at, g); + table >> names >> dtable; + table.traverse (t); + } + + { + trav_rel::table table; + trav_rel::unames names; + diff_table dtable (t, diff_table::mode_drop, at, g); + table >> names >> dtable; + table.traverse (*ot); + } + + if (!at.names_empty ()) + g.new_edge (cs, at, t.name ()); + else + g.delete_node (at); + } + else + { + // New table. + // add_table& at (g.new_node (t, cs, g)); g.new_edge (cs, at, t.name ()); } } else { - if (other.find (t.name ()) == 0) + if (other.find
(t.name ()) == 0) { drop_table& dt (g.new_node (t.id ())); g.new_edge (cs, dt, t.name ()); @@ -57,6 +168,8 @@ namespace relational graph& g; }; + // Assumes the new model has cxx-location set. + // changeset& diff (model& o, model& n, graph& g) { @@ -81,8 +194,71 @@ namespace relational return r; } + // + // patch + // + + struct patch_table: trav_rel::add_column, + trav_rel::drop_column, + trav_rel::alter_column + { + patch_table (table& tl, graph& gr): t (tl), g (gr) {} + + virtual void + traverse (sema_rel::add_column& ac) + { + try + { + column& c (g.new_node (ac, t, g)); + g.new_edge (t, c, ac.name ()); + } + catch (duplicate_name const&) + { + cerr << "error: invalid changelog: column '" << ac.name () << + "' already exists in table '" << t.name () << "'" << endl; + throw operation_failed (); + } + } + + virtual void + traverse (sema_rel::drop_column& dc) + { + table::names_iterator i (t.find (dc.name ())); + + if (i == t.names_end () || !i->nameable ().is_a ()) + { + cerr << "error: invalid changelog: column '" << dc.name () << + "' does not exist in table '" << t.name () << "'" << endl; + throw operation_failed (); + } + + g.delete_edge (t, i->nameable (), *i); + } + + virtual void + traverse (sema_rel::alter_column& ac) + { + if (column* c = t.find (ac.name ())) + { + if (ac.null_altered ()) + c->null (ac.null ()); + } + else + { + cerr << "error: invalid changelog: column '" << ac.name () << + "' does not exist in table '" << t.name () << "'" << endl; + throw operation_failed (); + } + } + + protected: + table& t; + graph& g; + }; + struct patch_model: trav_rel::add_table, - trav_rel::drop_table + trav_rel::drop_table, + trav_rel::alter_table { patch_model (model& ml, graph& gr): m (ml), g (gr) {} @@ -117,6 +293,25 @@ namespace relational g.delete_edge (m, i->nameable (), *i); } + virtual void + traverse (sema_rel::alter_table& at) + { + if (table* t = m.find
(at.name ())) + { + trav_rel::alter_table atable; + trav_rel::unames names; + patch_table ptable (*t, g); + atable >> names >> ptable; + atable.traverse (at); + } + else + { + cerr << "error: invalid changelog: table '" << at.name () << + "' does not exist in model version " << m.version () << endl; + throw operation_failed (); + } + } + protected: model& m; graph& g; diff --git a/odb/semantics/relational/column.cxx b/odb/semantics/relational/column.cxx index 05b8eff..f90b94e 100644 --- a/odb/semantics/relational/column.cxx +++ b/odb/semantics/relational/column.cxx @@ -10,6 +10,8 @@ namespace semantics { namespace relational { + // column + // column:: column (column const& c, uscope&, graph& g) : unameable (c, g), @@ -41,6 +43,13 @@ namespace semantics serialize (xml::serializer& s) const { s.start_element (xmlns, "column"); + serialize_attributes (s); + s.end_element (); + } + + void column:: + serialize_attributes (xml::serializer& s) const + { unameable::serialize_attributes (s); s.attribute ("type", type ()); @@ -51,6 +60,80 @@ namespace semantics if (!options ().empty ()) s.attribute ("options", options ()); + } + + // add_column + // + add_column& add_column:: + clone (uscope& s, graph& g) const + { + return g.new_node (*this, s, g); + } + + void add_column:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "add-column"); + column::serialize_attributes (s); + s.end_element (); + } + + // drop_column + // + drop_column:: + drop_column (xml::parser& p, uscope&, graph& g) + : unameable (p, g) + { + p.content (xml::parser::empty); + } + + drop_column& drop_column:: + clone (uscope& s, graph& g) const + { + return g.new_node (*this, s, g); + } + + void drop_column:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "drop-column"); + unameable::serialize_attributes (s); + s.end_element (); + } + + // 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 (xml::parser& p, uscope&, graph& g) + : unameable (p, g), + null_altered_ (p.attribute_present ("null")), + null_ (null_altered_ ? p.attribute ("null") : false) + { + p.content (xml::parser::empty); + } + + alter_column& alter_column:: + clone (uscope& s, graph& g) const + { + return g.new_node (*this, s, g); + } + + void alter_column:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "alter-column"); + unameable::serialize_attributes (s); + + if (null_altered_) + s.attribute ("null", null_); s.end_element (); } @@ -63,15 +146,46 @@ namespace semantics { init () { - unameable::parser_map_["column"] = &unameable::parser_impl; + unameable::parser_map& m (unameable::parser_map_); + + m["column"] = &unameable::parser_impl; + m["add-column"] = &unameable::parser_impl; + m["drop-column"] = &unameable::parser_impl; + m["alter-column"] = &unameable::parser_impl; using compiler::type_info; + // column + // { type_info ti (typeid (column)); ti.add_base (typeid (unameable)); insert (ti); } + + // add_column + // + { + type_info ti (typeid (add_column)); + ti.add_base (typeid (column)); + insert (ti); + } + + // drop_column + // + { + type_info ti (typeid (drop_column)); + ti.add_base (typeid (unameable)); + insert (ti); + } + + // alter_column + // + { + type_info ti (typeid (alter_column)); + ti.add_base (typeid (unameable)); + insert (ti); + } } } init_; } diff --git a/odb/semantics/relational/column.hxx b/odb/semantics/relational/column.hxx index 07d61bf..7702a13 100644 --- a/odb/semantics/relational/column.hxx +++ b/odb/semantics/relational/column.hxx @@ -25,6 +25,9 @@ namespace semantics bool null () const {return null_;} + void + null (bool n) {null_ = n;} + string const& default_ () const {return default__;} @@ -88,6 +91,10 @@ namespace semantics virtual void serialize (xml::serializer&) const; + protected: + void + serialize_attributes (xml::serializer&) const; + private: string type_; bool null_; @@ -96,6 +103,80 @@ namespace semantics contained_list contained_; }; + + class add_column: public column + { + public: + add_column (string const& id, string const& type, bool null) + : column (id, type, null) {} + add_column (column const& c, uscope& s, graph& g): column (c, s, g) {} + add_column (xml::parser& p, uscope& s, graph& g): column (p, s, g) {} + + virtual add_column& + clone (uscope&, graph&) const; + + virtual string + kind () const {return "add column";} + + virtual void + serialize (xml::serializer&) const; + }; + + class drop_column: public unameable + { + public: + drop_column (string const& id): unameable (id) {} + drop_column (drop_column const& c, uscope&, graph& g) + : unameable (c, g) {} + drop_column (xml::parser&, uscope&, graph&); + + virtual drop_column& + clone (uscope&, graph&) const; + + virtual string + kind () const {return "drop column";} + + virtual void + serialize (xml::serializer&) const; + }; + + class alter_column: public unameable + { + public: + bool + null_altered () const {return null_altered_;} + + bool + null () const {return null_;} + + void + null (bool n) {null_ = n; null_altered_ = true;} + + public: + alter_column (string const& id) + : unameable (id), null_altered_ (false) + { + } + + alter_column (alter_column const&, uscope&, graph&); + alter_column (xml::parser&, uscope&, graph&); + + virtual alter_column& + clone (uscope&, graph&) const; + + virtual string + kind () const + { + return "alter column"; + } + + virtual void + serialize (xml::serializer&) const; + + private: + bool null_altered_; + bool null_; + }; } } diff --git a/odb/semantics/relational/table.cxx b/odb/semantics/relational/table.cxx index 6d1c3a2..682196c 100644 --- a/odb/semantics/relational/table.cxx +++ b/odb/semantics/relational/table.cxx @@ -79,6 +79,35 @@ namespace semantics s.end_element (); } + // 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 + { + return g.new_node (*this, s, g); + } + + void alter_table:: + serialize (xml::serializer& s) const + { + s.start_element (xmlns, "alter-table"); + qnameable::serialize_attributes (s); + uscope::serialize_content (s); + s.end_element (); + } + // type info // namespace @@ -87,11 +116,12 @@ namespace semantics { init () { - qnameable::parser_map_["table"] = &qnameable::parser_impl
; - qnameable::parser_map_["add-table"] = - &qnameable::parser_impl; - qnameable::parser_map_["drop-table"] = - &qnameable::parser_impl; + qnameable::parser_map& m (qnameable::parser_map_); + + m["table"] = &qnameable::parser_impl
; + m["add-table"] = &qnameable::parser_impl; + m["drop-table"] = &qnameable::parser_impl; + m["alter-table"] = &qnameable::parser_impl; using compiler::type_info; @@ -119,6 +149,15 @@ namespace semantics ti.add_base (typeid (qnameable)); insert (ti); } + + // alter_table + // + { + type_info ti (typeid (alter_table)); + ti.add_base (typeid (qnameable)); + ti.add_base (typeid (uscope)); + insert (ti); + } } } init_; } diff --git a/odb/semantics/relational/table.hxx b/odb/semantics/relational/table.hxx index 3eb1b73..effd552 100644 --- a/odb/semantics/relational/table.hxx +++ b/odb/semantics/relational/table.hxx @@ -66,6 +66,26 @@ namespace semantics serialize (xml::serializer&) const; }; + class alter_table: public qnameable, public uscope + { + public: + alter_table (string const& id): qnameable (id) {} + alter_table (alter_table const&, qscope&, graph&); + alter_table (xml::parser&, qscope&, graph&); + + virtual alter_table& + clone (qscope&, graph&) const; + + virtual string + kind () const {return "alter table";} + + virtual void + serialize (xml::serializer&) const; + + // Resolve ambiguity. + // + using qnameable::scope; + }; } } diff --git a/odb/traversal/relational/column.hxx b/odb/traversal/relational/column.hxx index 40a6619..d9db303 100644 --- a/odb/traversal/relational/column.hxx +++ b/odb/traversal/relational/column.hxx @@ -13,6 +13,9 @@ namespace traversal namespace relational { struct column: node {}; + struct add_column: node {}; + struct drop_column: node {}; + struct alter_column: node {}; } } diff --git a/odb/traversal/relational/table.hxx b/odb/traversal/relational/table.hxx index e700e43..83149dc 100644 --- a/odb/traversal/relational/table.hxx +++ b/odb/traversal/relational/table.hxx @@ -15,6 +15,7 @@ namespace traversal struct table: scope_template {}; struct add_table: scope_template {}; struct drop_table: node {}; + struct alter_table: scope_template {}; } } -- cgit v1.1