aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-03-26 13:03:13 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-04-10 18:46:44 +0200
commite999b1e7295acd8cdb646c2db7db1f5059f10c7d (patch)
treeacf67d764a65cdcd54d10585b927993eca64a15f
parent7cc50b230deb58703cd2d4df77fadcbb83fb14b3 (diff)
Add changelog support for add, drop, and later column
-rw-r--r--odb/relational/changelog.cxx201
-rw-r--r--odb/semantics/relational/column.cxx116
-rw-r--r--odb/semantics/relational/column.hxx81
-rw-r--r--odb/semantics/relational/table.cxx49
-rw-r--r--odb/semantics/relational/table.hxx20
-rw-r--r--odb/traversal/relational/column.hxx3
-rw-r--r--odb/traversal/relational/table.hxx1
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<column> (c.name ()))
+ {
+ if (c.type () != oc->type ())
+ diagnose_unsupported (c, "type");
+
+ if (c.null () != oc->null ())
+ {
+ alter_column& ac (g.new_node<alter_column> (c.id ()));
+ ac.null (c.null ());
+ g.new_edge<unames> (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<add_column> (c, at, g));
+ g.new_edge<unames> (at, ac, c.name ());
+ }
+ }
+ else
+ {
+ if (other.find<sema_rel::column> (c.name ()) == 0)
+ {
+ drop_column& dc (g.new_node<drop_column> (c.id ()));
+ g.new_edge<unames> (at, dc, c.name ());
+ }
+ }
+ }
+
+ void
+ diagnose_unsupported (sema_rel::column& c, char const* name)
+ {
+ table& t (c.table ());
+ location const& tl (t.get<location> ("cxx-location"));
+ location const& cl (c.get<location> ("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<sema_rel::table> (t.name ()) == 0)
+ if (table* ot = other.find<table> (t.name ()))
{
+ // See if there are any changes to the table.
+ //
+ alter_table& at (g.new_node<alter_table> (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<qnames> (cs, at, t.name ());
+ else
+ g.delete_node (at);
+ }
+ else
+ {
+ // New table.
+ //
add_table& at (g.new_node<add_table> (t, cs, g));
g.new_edge<qnames> (cs, at, t.name ());
}
}
else
{
- if (other.find<sema_rel::table> (t.name ()) == 0)
+ if (other.find<table> (t.name ()) == 0)
{
drop_table& dt (g.new_node<drop_table> (t.id ()));
g.new_edge<qnames> (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<column> (ac, t, g));
+ g.new_edge<unames> (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<column> ())
+ {
+ 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<column> (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<table> (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<add_column> (*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<drop_column> (*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<bool> ("null") : false)
+ {
+ p.content (xml::parser::empty);
+ }
+
+ alter_column& alter_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<alter_column> (*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<column>;
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["column"] = &unameable::parser_impl<column>;
+ m["add-column"] = &unameable::parser_impl<add_column>;
+ m["drop-column"] = &unameable::parser_impl<drop_column>;
+ m["alter-column"] = &unameable::parser_impl<alter_column>;
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<alter_table> (*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<table>;
- qnameable::parser_map_["add-table"] =
- &qnameable::parser_impl<add_table>;
- qnameable::parser_map_["drop-table"] =
- &qnameable::parser_impl<drop_table>;
+ qnameable::parser_map& m (qnameable::parser_map_);
+
+ m["table"] = &qnameable::parser_impl<table>;
+ m["add-table"] = &qnameable::parser_impl<add_table>;
+ m["drop-table"] = &qnameable::parser_impl<drop_table>;
+ m["alter-table"] = &qnameable::parser_impl<alter_table>;
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<semantics::relational::column> {};
+ struct add_column: node<semantics::relational::add_column> {};
+ struct drop_column: node<semantics::relational::drop_column> {};
+ struct alter_column: node<semantics::relational::alter_column> {};
}
}
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<semantics::relational::table> {};
struct add_table: scope_template<semantics::relational::add_table> {};
struct drop_table: node<semantics::relational::drop_table> {};
+ struct alter_table: scope_template<semantics::relational::alter_table> {};
}
}