aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-03-20 13:09:45 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-04-10 18:46:44 +0200
commitc67c06030fb1ac622c96211bffc054a85efe0aa8 (patch)
tree07a85e0c3b0f3a22b58405ff5582870f2b09f424
parent3b457daf6b252ef31ec0611e7375aa4badd8e63d (diff)
Add support for maintaining log of database model changes
-rw-r--r--odb/generator.cxx144
-rw-r--r--odb/makefile1
-rw-r--r--odb/relational/changelog.cxx288
-rw-r--r--odb/relational/generate.hxx14
-rw-r--r--odb/semantics/relational/changelog.cxx1
-rw-r--r--odb/semantics/relational/elements.cxx18
-rw-r--r--odb/semantics/relational/elements.hxx52
-rw-r--r--odb/semantics/relational/elements.txx12
-rw-r--r--odb/semantics/relational/model.hxx3
-rw-r--r--odb/semantics/relational/table.cxx35
-rw-r--r--odb/semantics/relational/table.hxx18
-rw-r--r--odb/traversal/relational/table.hxx1
12 files changed, 555 insertions, 32 deletions
diff --git a/odb/generator.cxx b/odb/generator.cxx
index 83f42b8..a2254e0 100644
--- a/odb/generator.cxx
+++ b/odb/generator.cxx
@@ -6,6 +6,7 @@
#include <string>
#include <memory> // std::auto_ptr
#include <fstream>
+#include <sstream>
#include <iostream>
#include <cutl/fs/auto-remove.hxx>
@@ -14,10 +15,16 @@
#include <cutl/compiler/cxx-indenter.hxx>
#include <cutl/compiler/sloc-counter.hxx>
+#include <cutl/xml/parser.hxx>
+#include <cutl/xml/serializer.hxx>
+
#include <odb/version.hxx>
#include <odb/context.hxx>
#include <odb/generator.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
#include <odb/generate.hxx>
#include <odb/relational/generate.hxx>
@@ -85,18 +92,15 @@ generate (options const& ops,
// First create the database model.
//
+ bool gen_schema (ops.generate_schema () && db != database::common);
cutl::shared_ptr<semantics::relational::model> model;
- if (ops.generate_schema ())
+ if (gen_schema)
{
auto_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0));
switch (db)
{
- case database::common:
- {
- break; // No schema for common.
- }
case database::mssql:
case database::mysql:
case database::oracle:
@@ -106,29 +110,34 @@ generate (options const& ops,
model = relational::model::generate ();
break;
}
+ case database::common:
+ break;
}
}
// Output files.
//
+ fs::auto_removes auto_rm;
+
path file (ops.output_name ().empty ()
? p.leaf ()
: path (ops.output_name ()).leaf ());
string base (file.base ().string ());
- fs::auto_removes auto_rm;
-
string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ());
string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ());
string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ());
string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ());
string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ());
+ string log_name (base + ops.changelog_file_suffix ()[db] +
+ ops.changelog_suffix ());
path hxx_path (hxx_name);
path ixx_path (ixx_name);
path cxx_path (cxx_name);
path sch_path (sch_name);
path sql_path (sql_name);
+ path log_path (p.directory () / path (log_name)); // Input directory.
if (!ops.output_dir ().empty ())
{
@@ -140,12 +149,72 @@ generate (options const& ops,
sql_path = dir / sql_path;
}
- bool gen_cxx (!ops.generate_schema_only ());
+ // Load the old changelog and generate a new one.
+ //
+ bool gen_log (gen_schema && unit.count ("model-version") != 0);
+ cutl::shared_ptr<semantics::relational::changelog> changelog;
+ cutl::shared_ptr<semantics::relational::changelog> old_changelog;
+ string old_changelog_xml;
+
+ if (gen_log)
+ {
+ ifstream log (log_path.string ().c_str (),
+ ios_base::in | ios_base::binary);
+
+ if (log.is_open ())
+ {
+ try
+ {
+ // Get the XML into a buffer. We use it to avoid modifying the
+ // file when the changelog hasn't changed.
+ //
+ for (bool first (true); !log.eof (); )
+ {
+ string line;
+ getline (log, line);
+
+ if (log.fail ())
+ ios_base::failure ("getline");
+
+ if (first)
+ first = false;
+ else
+ old_changelog_xml += '\n';
+
+ old_changelog_xml += line;
+ }
+
+ istringstream is (old_changelog_xml);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+
+ xml::parser p (is, log_path.string ());
+ old_changelog.reset (
+ new (shared) semantics::relational::changelog (p));
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << log_path << ": read failure" << endl;
+ throw failed ();
+ }
+ catch (const xml::parsing& e)
+ {
+ cerr << e.what () << endl;
+ throw failed ();
+ }
+ }
+
+ changelog = relational::changelog::generate (
+ *model,
+ unit.get<model_version> ("model-version"),
+ old_changelog.get (),
+ log_path.string ());
+ }
//
//
- ofstream hxx;
+ bool gen_cxx (!ops.generate_schema_only ());
+ ofstream hxx;
if (gen_cxx)
{
hxx.open (hxx_path.string ().c_str (), ios_base::out);
@@ -163,7 +232,6 @@ generate (options const& ops,
//
//
ofstream ixx;
-
if (gen_cxx)
{
ixx.open (ixx_path.string ().c_str (), ios_base::out);
@@ -181,7 +249,6 @@ generate (options const& ops,
//
//
ofstream cxx;
-
if (gen_cxx && (db != database::common || md == multi_database::dynamic))
{
cxx.open (cxx_path.string ().c_str (), ios_base::out);
@@ -198,11 +265,9 @@ generate (options const& ops,
//
//
- bool gen_sql_schema (ops.generate_schema () &&
- ops.schema_format ()[db].count (schema_format::sql) &&
- db != database::common);
+ bool gen_sql_schema (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::sql));
ofstream sql;
-
if (gen_sql_schema)
{
sql.open (sql_path.string ().c_str (), ios_base::out);
@@ -221,12 +286,10 @@ generate (options const& ops,
//
bool gen_sep_schema (
gen_cxx &&
- ops.generate_schema () &&
- ops.schema_format ()[db].count (schema_format::separate) &&
- db != database::common);
+ gen_schema &&
+ ops.schema_format ()[db].count (schema_format::separate));
ofstream sch;
-
if (gen_sep_schema)
{
sch.open (sch_path.string ().c_str (), ios_base::out);
@@ -770,6 +833,49 @@ generate (options const& ops,
}
}
+ // Save the changelog if it changed.
+ //
+ if (gen_log)
+ {
+ try
+ {
+ ostringstream os;
+ os.exceptions (ifstream::badbit | ifstream::failbit);
+ xml::serializer s (os, log_path.string ());
+ changelog->serialize (s);
+ string const& changelog_xml (os.str ());
+
+ if (changelog_xml != old_changelog_xml)
+ {
+ ofstream log (log_path.string ().c_str (),
+ ios_base::out | ios_base::binary);
+
+ if (!log.is_open ())
+ {
+ cerr << "error: unable to open '" << log_path << "' in write mode"
+ << endl;
+ throw failed ();
+ }
+
+ if (old_changelog == 0)
+ auto_rm.add (log_path);
+
+ log.exceptions (ifstream::badbit | ifstream::failbit);
+ log << changelog_xml;
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << log_path << ": write failure" << endl;
+ throw failed ();
+ }
+ catch (const xml::serialization& e)
+ {
+ cerr << e.what () << endl;
+ throw failed ();
+ }
+ }
+
// Communicate the sloc count to the driver. This is necessary to
// correctly handle the total if we are compiling multiple files in
// one invocation.
diff --git a/odb/makefile b/odb/makefile
index 5edd0ea..104bc4a 100644
--- a/odb/makefile
+++ b/odb/makefile
@@ -32,6 +32,7 @@ pragma.cxx
# Relational.
#
cxx_ptun += \
+relational/changelog.cxx \
relational/common.cxx \
relational/common-query.cxx \
relational/context.cxx \
diff --git a/odb/relational/changelog.cxx b/odb/relational/changelog.cxx
new file mode 100644
index 0000000..de5e6f7
--- /dev/null
+++ b/odb/relational/changelog.cxx
@@ -0,0 +1,288 @@
+// file : odb/relational/changelog.cxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/semantics/relational.hxx>
+#include <odb/traversal/relational.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace changelog
+ {
+ using namespace sema_rel;
+ using sema_rel::model;
+ using sema_rel::changelog;
+
+ namespace
+ {
+ 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) {}
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ if (mode == mode_add)
+ {
+ if (other.find<sema_rel::table> (t.name ()) == 0)
+ {
+ 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)
+ {
+ drop_table& dt (g.new_node<drop_table> (t.id ()));
+ g.new_edge<qnames> (cs, dt, t.name ());
+ }
+ }
+ }
+
+ protected:
+ model& other;
+ mode_type mode;
+ changeset& cs;
+ graph& g;
+ };
+
+ changeset&
+ diff (model& o, model& n, graph& g)
+ {
+ changeset& r (g.new_node<changeset> (n.version ()));
+
+ {
+ trav_rel::model model;
+ trav_rel::qnames names;
+ diff_model dmodel (o, diff_model::mode_add, r, g);
+ model >> names >> dmodel;
+ model.traverse (n);
+ }
+
+ {
+ trav_rel::model model;
+ trav_rel::qnames names;
+ diff_model dmodel (n, diff_model::mode_drop, r, g);
+ model >> names >> dmodel;
+ model.traverse (o);
+ }
+
+ return r;
+ }
+
+ struct patch_model: trav_rel::add_table,
+ trav_rel::drop_table
+ {
+ patch_model (model& ml, graph& gr): m (ml), g (gr) {}
+
+ virtual void
+ traverse (sema_rel::add_table& at)
+ {
+ try
+ {
+ table& t (g.new_node<table> (at, m, g));
+ g.new_edge<qnames> (m, t, at.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: table '" << at.name () <<
+ "' already exists in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_table& dt)
+ {
+ model::names_iterator i (m.find (dt.name ()));
+
+ if (i == m.names_end () || !i->nameable ().is_a<table> ())
+ {
+ cerr << "error: invalid changelog: table '" << dt.name () <<
+ "' does not exist in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (m, i->nameable (), *i);
+ }
+
+ protected:
+ model& m;
+ graph& g;
+ };
+
+ model&
+ patch (model& m, changeset& c, graph& g)
+ {
+ model& r (g.new_node<model> (m, g));
+
+ trav_rel::changeset changeset;
+ trav_rel::qnames names;
+ patch_model pmodel (r, g);
+ changeset >> names >> pmodel;
+ changeset.traverse (c);
+
+ r.version (c.version ());
+ return r;
+ }
+ }
+
+ cutl::shared_ptr<changelog>
+ generate (model& m,
+ model_version const& mv,
+ changelog* old,
+ string const& name)
+ {
+ cutl::shared_ptr<changelog> cl (new (shared) changelog);
+ graph& g (*cl);
+
+ if (old == 0)
+ {
+ if (!mv.open)
+ {
+ cerr << name << ": error: unable to initialize changelog because " <<
+ "current version is closed" << endl;
+ throw operation_failed ();
+ }
+
+ cerr << name << ": info: initializing changelog with base version " <<
+ m.version () << endl;
+
+ g.new_edge<contains_model> (*cl, g.new_node<model> (m, g));
+ return cl;
+ }
+
+ // Get the changelog base and current versions and do some sanity
+ // checks.
+ //
+ version bver (old->model ().version ());
+ version cver (
+ old->contains_changeset_empty ()
+ ? bver
+ : old->contains_changeset_back ().changeset ().version ());
+
+ if (mv.current < cver)
+ {
+ cerr << name << ": error: latest changelog version is greater " <<
+ "than current version" << endl;
+ throw operation_failed ();
+ }
+
+ // Build the new changelog.
+ //
+ model& oldm (old->model ());
+
+ // Handle the cases where we just override the log with the current
+ // model.
+ //
+ if (mv.base == mv.current || bver == mv.current || oldm.names_empty ())
+ {
+ // If the current version is closed, make sure the model hasn't
+ // changed.
+ //
+ if (!mv.open)
+ {
+ changeset& cs (diff (oldm, m, g));
+
+ if (!cs.names_empty ())
+ {
+ qnames& n (*cs.names_begin ());
+
+ cerr << name << ": error: current version is closed" << endl;
+ cerr << name << ": info: first new change is " <<
+ n.nameable ().kind () << " '" << n.name () << "'" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ g.new_edge<contains_model> (*cl, g.new_node<model> (m, g));
+ return cl;
+ }
+
+ // Now we have a case with a "real" old model (i.e., non-empty
+ // and with version older than current) as well as zero or more
+ // changeset.
+ //
+ //
+ model* base (bver >= mv.base ? &g.new_node<model> (oldm, g) : 0);
+ model* last (&oldm);
+
+ for (changelog::contains_changeset_iterator i (
+ old->contains_changeset_begin ());
+ i != old->contains_changeset_end (); ++i)
+ {
+ changeset& cs (i->changeset ());
+
+ // Don't copy the changeset for the current version. Instead, we
+ // will re-create it from scratch.
+ //
+ if (cs.version () == mv.current)
+ {
+ // If the current version is closed, make sure the model hasn't
+ // changed.
+ //
+ if (!mv.open)
+ {
+ model& old (patch (*last, cs, g));
+ changeset& cs (diff (old, m, g));
+
+ if (!cs.names_empty ())
+ {
+ qnames& n (*cs.names_begin ());
+
+ cerr << name << ": error: current version is closed" << endl;
+ cerr << name << ": info: first new change is " <<
+ n.nameable ().kind () << " '" << n.name () << "'" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ break;
+ }
+
+ last = &patch (*last, cs, g);
+
+ if (base == 0 && last->version () >= mv.base)
+ base = last;
+
+ // Copy the changeset unless it is below or at our base version.
+ //
+ if (last->version () <= mv.base)
+ continue;
+
+ g.new_edge<contains_changeset> (*cl, g.new_node<changeset> (cs, g));
+ }
+
+ // If we still haven't found the new base model, then take the
+ // latest and update its version.
+ //
+ if (base == 0)
+ {
+ base = last != &oldm ? last : &g.new_node<model> (oldm, g);
+ base->version (mv.base);
+ }
+ g.new_edge<contains_model> (*cl, *base);
+
+ // Add a changeset for the current version.
+ //
+ changeset& cs (diff (*last, m, g));
+
+ if (!cs.names_empty ())
+ g.new_edge<contains_changeset> (*cl, cs);
+
+ return cl;
+ }
+ }
+}
diff --git a/odb/relational/generate.hxx b/odb/relational/generate.hxx
index 401c93c..e5dc376 100644
--- a/odb/relational/generate.hxx
+++ b/odb/relational/generate.hxx
@@ -5,9 +5,12 @@
#ifndef ODB_RELATIONAL_GENERATE_HXX
#define ODB_RELATIONAL_GENERATE_HXX
+#include <string>
#include <cutl/shared-ptr.hxx>
+#include <odb/context.hxx>
#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changelog.hxx>
namespace relational
{
@@ -41,6 +44,17 @@ namespace relational
generate ();
}
+ namespace changelog
+ {
+ // Returns NULL if the changelog is unchanged.
+ //
+ cutl::shared_ptr<semantics::relational::changelog>
+ generate (semantics::relational::model&,
+ model_version const&,
+ semantics::relational::changelog* old, // Can be NULL.
+ std::string const& name);
+ }
+
namespace schema
{
void
diff --git a/odb/semantics/relational/changelog.cxx b/odb/semantics/relational/changelog.cxx
index 05dd529..cd94494 100644
--- a/odb/semantics/relational/changelog.cxx
+++ b/odb/semantics/relational/changelog.cxx
@@ -73,6 +73,7 @@ namespace semantics
i != contains_changeset_.rend (); ++i)
{
(*i)->changeset ().serialize (s);
+ s.characters ("\n");
}
model ().serialize (s);
diff --git a/odb/semantics/relational/elements.cxx b/odb/semantics/relational/elements.cxx
index 43c03c0..e14bc00 100644
--- a/odb/semantics/relational/elements.cxx
+++ b/odb/semantics/relational/elements.cxx
@@ -70,6 +70,24 @@ namespace semantics
throw duplicate_name (*this, (*i->second)->nameable (), n);
}
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ // If we are removing the first key, then move to the next key (or
+ // the end which means there are no keys).
+ //
+ if (first_key_ == i->second)
+ first_key_++;
+
+ names_.erase (i->second);
+ names_map_.erase (e.name ());
+ iterator_map_.erase (i);
+ }
+
// type info
//
namespace
diff --git a/odb/semantics/relational/elements.hxx b/odb/semantics/relational/elements.hxx
index 2321e1a..381d7a8 100644
--- a/odb/semantics/relational/elements.hxx
+++ b/odb/semantics/relational/elements.hxx
@@ -149,6 +149,20 @@ namespace semantics
nameable_ = &n;
}
+ void
+ clear_left_node (scope_type& n)
+ {
+ assert (scope_ == &n);
+ scope_ = 0;
+ }
+
+ void
+ clear_right_node (nameable_type& n)
+ {
+ assert (nameable_ == &n);
+ nameable_ = 0;
+ }
+
protected:
name_type name_;
scope_type* scope_;
@@ -169,22 +183,16 @@ namespace semantics
typedef relational::scope<N> scope_type;
name_type const&
- name () const
- {
- return named_->name ();
- }
+ name () const {return named_->name ();}
scope_type&
- scope () const
- {
- return named ().scope ();
- }
+ scope () const {return named ().scope ();}
names_type&
- named () const
- {
- return *named_;
- }
+ named () const {return *named_;}
+
+ string const&
+ id () const {return id_;}
public:
// Id identifies the C++ node (e.g., a class or a data member) that
@@ -208,6 +216,13 @@ namespace semantics
named_ = &e;
}
+ virtual void
+ remove_edge_right (names_type& e)
+ {
+ assert (named_ == &e);
+ named_ = 0;
+ }
+
using node::add_edge_right;
protected:
@@ -298,6 +313,12 @@ namespace semantics
return names_.end ();
}
+ bool
+ names_empty () const
+ {
+ return names_.empty ();
+ }
+
// Find.
//
template <typename T>
@@ -324,6 +345,9 @@ namespace semantics
virtual void
add_edge_left (names_type&);
+ virtual void
+ remove_edge_left (names_type&);
+
protected:
scope (scope const&, graph&);
scope (xml::parser&, graph&);
@@ -343,6 +367,10 @@ namespace semantics
void scope<uname>::
add_edge_left (names_type&);
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type&);
+
typedef scope<uname> uscope;
typedef scope<qname> qscope;
}
diff --git a/odb/semantics/relational/elements.txx b/odb/semantics/relational/elements.txx
index 53946a0..e1fd6d5 100644
--- a/odb/semantics/relational/elements.txx
+++ b/odb/semantics/relational/elements.txx
@@ -169,5 +169,17 @@ namespace semantics
else
throw duplicate_name (*this, (*i->second)->nameable (), e.nameable ());
}
+
+ template <typename N>
+ void scope<N>::
+ remove_edge_left (names_type& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ names_.erase (i->second);
+ names_map_.erase (e.name ());
+ iterator_map_.erase (i);
+ }
}
}
diff --git a/odb/semantics/relational/model.hxx b/odb/semantics/relational/model.hxx
index 626befd..575100f 100644
--- a/odb/semantics/relational/model.hxx
+++ b/odb/semantics/relational/model.hxx
@@ -19,6 +19,9 @@ namespace semantics
version_type
version () const {return version_;}
+ void
+ version (version_type v) {version_ = v;}
+
public:
model (version_type v): version_ (v) {}
model (model const&, graph&);
diff --git a/odb/semantics/relational/table.cxx b/odb/semantics/relational/table.cxx
index 1c6e784..6d1c3a2 100644
--- a/odb/semantics/relational/table.cxx
+++ b/odb/semantics/relational/table.cxx
@@ -13,7 +13,7 @@ namespace semantics
// table
//
table::
- table (table const& t, qscope& s, graph& g)
+ table (table const& t, qscope&, graph& g)
: qnameable (t, g), uscope (t, g)
{
}
@@ -56,6 +56,29 @@ namespace semantics
s.end_element ();
}
+ // drop_table
+ //
+ drop_table::
+ drop_table (xml::parser& p, qscope&, graph& g)
+ : qnameable (p, g)
+ {
+ p.content (xml::parser::empty);
+ }
+
+ drop_table& drop_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<drop_table> (*this, s, g);
+ }
+
+ void drop_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-table");
+ qnameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
// type info
//
namespace
@@ -67,6 +90,8 @@ namespace semantics
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>;
using compiler::type_info;
@@ -86,6 +111,14 @@ namespace semantics
ti.add_base (typeid (table));
insert (ti);
}
+
+ // drop_table
+ //
+ {
+ type_info ti (typeid (drop_table));
+ ti.add_base (typeid (qnameable));
+ insert (ti);
+ }
}
} init_;
}
diff --git a/odb/semantics/relational/table.hxx b/odb/semantics/relational/table.hxx
index 768d3d7..3eb1b73 100644
--- a/odb/semantics/relational/table.hxx
+++ b/odb/semantics/relational/table.hxx
@@ -48,6 +48,24 @@ namespace semantics
virtual void
serialize (xml::serializer&) const;
};
+
+ class drop_table: public qnameable
+ {
+ public:
+ drop_table (string const& id): qnameable (id) {}
+ drop_table (drop_table const& t, qscope&, graph& g): qnameable (t, g) {}
+ drop_table (xml::parser&, qscope&, graph&);
+
+ virtual drop_table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
}
}
diff --git a/odb/traversal/relational/table.hxx b/odb/traversal/relational/table.hxx
index b438f16..e700e43 100644
--- a/odb/traversal/relational/table.hxx
+++ b/odb/traversal/relational/table.hxx
@@ -14,6 +14,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> {};
}
}