From 08a47c70ad517b80b72914d47d547463f576bcd3 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 24 Oct 2011 16:32:51 +0200 Subject: Generate database schema from database model instead of C++ model We now first create the so-called database model from C++ model and then use that to generate the database schema. The new approach also adds more general support for primary/foreign keys, including multi- column keys. Finally, for MySQL we now generate out-of-line foreign key definitions. Because MySQL does not support deferred constraints checking, deferred foreign keys are written commented out, for documentation. --- odb/context.cxx | 13 +- odb/context.hxx | 6 +- odb/generator.cxx | 34 +- odb/makefile | 167 ++++---- odb/processor.cxx | 2 +- odb/relational/common.hxx | 16 + odb/relational/context.cxx | 10 +- odb/relational/context.hxx | 13 +- odb/relational/generate.hxx | 10 + odb/relational/model.cxx | 168 ++++++++ odb/relational/model.hxx | 528 +++++++++++++++++++++++ odb/relational/mysql/context.cxx | 96 ++--- odb/relational/mysql/context.hxx | 20 +- odb/relational/mysql/model.cxx | 111 +++++ odb/relational/mysql/schema.cxx | 171 +++----- odb/relational/oracle/context.cxx | 134 +++--- odb/relational/oracle/context.hxx | 21 +- odb/relational/oracle/model.cxx | 74 ++++ odb/relational/oracle/schema.cxx | 306 ++++++------- odb/relational/pgsql/context.cxx | 103 ++--- odb/relational/pgsql/context.hxx | 20 +- odb/relational/pgsql/model.cxx | 71 ++++ odb/relational/pgsql/schema.cxx | 265 ++++-------- odb/relational/schema.cxx | 157 ++----- odb/relational/schema.hxx | 708 ++++++++++++++++--------------- odb/relational/source.hxx | 86 +++- odb/relational/sqlite/context.cxx | 66 +-- odb/relational/sqlite/context.hxx | 20 +- odb/relational/sqlite/model.cxx | 58 +++ odb/relational/sqlite/schema.cxx | 60 +-- odb/semantics/relational.hxx | 18 + odb/semantics/relational/column.cxx | 35 ++ odb/semantics/relational/column.hxx | 118 ++++++ odb/semantics/relational/elements.cxx | 137 ++++++ odb/semantics/relational/elements.hxx | 277 ++++++++++++ odb/semantics/relational/foreign-key.cxx | 35 ++ odb/semantics/relational/foreign-key.hxx | 87 ++++ odb/semantics/relational/index.cxx | 35 ++ odb/semantics/relational/index.hxx | 44 ++ odb/semantics/relational/key.cxx | 43 ++ odb/semantics/relational/key.hxx | 100 +++++ odb/semantics/relational/model.cxx | 35 ++ odb/semantics/relational/model.hxx | 39 ++ odb/semantics/relational/primary-key.cxx | 35 ++ odb/semantics/relational/primary-key.hxx | 45 ++ odb/semantics/relational/table.cxx | 52 +++ odb/semantics/relational/table.hxx | 56 +++ odb/traversal/relational.hxx | 18 + odb/traversal/relational/column.hxx | 20 + odb/traversal/relational/elements.cxx | 18 + odb/traversal/relational/elements.hxx | 147 +++++++ odb/traversal/relational/foreign-key.hxx | 20 + odb/traversal/relational/index.hxx | 20 + odb/traversal/relational/key.hxx | 43 ++ odb/traversal/relational/model.hxx | 20 + odb/traversal/relational/primary-key.hxx | 20 + odb/traversal/relational/table.hxx | 22 + odb/validator.cxx | 2 +- 58 files changed, 3745 insertions(+), 1310 deletions(-) create mode 100644 odb/relational/model.cxx create mode 100644 odb/relational/model.hxx create mode 100644 odb/relational/mysql/model.cxx create mode 100644 odb/relational/oracle/model.cxx create mode 100644 odb/relational/pgsql/model.cxx create mode 100644 odb/relational/sqlite/model.cxx create mode 100644 odb/semantics/relational.hxx create mode 100644 odb/semantics/relational/column.cxx create mode 100644 odb/semantics/relational/column.hxx create mode 100644 odb/semantics/relational/elements.cxx create mode 100644 odb/semantics/relational/elements.hxx create mode 100644 odb/semantics/relational/foreign-key.cxx create mode 100644 odb/semantics/relational/foreign-key.hxx create mode 100644 odb/semantics/relational/index.cxx create mode 100644 odb/semantics/relational/index.hxx create mode 100644 odb/semantics/relational/key.cxx create mode 100644 odb/semantics/relational/key.hxx create mode 100644 odb/semantics/relational/model.cxx create mode 100644 odb/semantics/relational/model.hxx create mode 100644 odb/semantics/relational/primary-key.cxx create mode 100644 odb/semantics/relational/primary-key.hxx create mode 100644 odb/semantics/relational/table.cxx create mode 100644 odb/semantics/relational/table.hxx create mode 100644 odb/traversal/relational.hxx create mode 100644 odb/traversal/relational/column.hxx create mode 100644 odb/traversal/relational/elements.cxx create mode 100644 odb/traversal/relational/elements.hxx create mode 100644 odb/traversal/relational/foreign-key.hxx create mode 100644 odb/traversal/relational/index.hxx create mode 100644 odb/traversal/relational/key.hxx create mode 100644 odb/traversal/relational/model.hxx create mode 100644 odb/traversal/relational/primary-key.hxx create mode 100644 odb/traversal/relational/table.hxx diff --git a/odb/context.cxx b/odb/context.cxx index 9814dc2..f1386d2 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -99,7 +99,10 @@ namespace } auto_ptr -create_context (ostream& os, semantics::unit& unit, options const& ops) +create_context (ostream& os, + semantics::unit& unit, + options const& ops, + semantics::relational::model* m) { auto_ptr r; @@ -107,22 +110,22 @@ create_context (ostream& os, semantics::unit& unit, options const& ops) { case database::mysql: { - r.reset (new relational::mysql::context (os, unit, ops)); + r.reset (new relational::mysql::context (os, unit, ops, m)); break; } case database::oracle: { - r.reset (new relational::oracle::context (os, unit, ops)); + r.reset (new relational::oracle::context (os, unit, ops, m)); break; } case database::pgsql: { - r.reset (new relational::pgsql::context (os, unit, ops)); + r.reset (new relational::pgsql::context (os, unit, ops, m)); break; } case database::sqlite: { - r.reset (new relational::sqlite::context (os, unit, ops)); + r.reset (new relational::sqlite::context (os, unit, ops, m)); break; } case database::tracer: diff --git a/odb/context.hxx b/odb/context.hxx index b52ead3..d1a9073 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -24,6 +24,7 @@ #include #include #include +#include #include using std::endl; @@ -787,7 +788,10 @@ private: // Create concrete database context. // std::auto_ptr -create_context (std::ostream&, semantics::unit&, options const&); +create_context (std::ostream&, + semantics::unit&, + options const&, + semantics::relational::model*); // Checks if scope Y names any of X. // diff --git a/odb/generator.cxx b/odb/generator.cxx index aa56644..6c1fda4 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -93,6 +93,32 @@ generate (options const& ops, semantics::unit& unit, path const& p) { try { + // First create the database model. + // + cutl::shared_ptr model; + + if (ops.generate_schema ()) + { + auto_ptr ctx (create_context (cerr, unit, ops, 0)); + + switch (ops.database ()) + { + case database::mysql: + case database::oracle: + case database::pgsql: + case database::sqlite: + { + model = relational::model::generate (); + break; + } + case database::tracer: + { + cerr << "error: the tracer database does not have schema" << endl; + throw failed (); + } + } + } + // Output files. // path file (p.leaf ()); @@ -196,7 +222,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (hxx); - auto_ptr ctx (create_context (hxx, unit, ops)); + auto_ptr ctx (create_context (hxx, unit, ops, model.get ())); string guard (make_guard (gp + hxx_name, *ctx)); @@ -270,7 +296,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (ixx); - auto_ptr ctx (create_context (ixx, unit, ops)); + auto_ptr ctx (create_context (ixx, unit, ops, model.get ())); // Copy prologue. // @@ -313,7 +339,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (cxx); - auto_ptr ctx (create_context (cxx, unit, ops)); + auto_ptr ctx (create_context (cxx, unit, ops, model.get ())); cxx << "#include " << endl << endl; @@ -363,7 +389,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // if (sql_schema) { - auto_ptr ctx (create_context (sql, unit, ops)); + auto_ptr ctx (create_context (sql, unit, ops, model.get ())); // Copy prologue. // diff --git a/odb/makefile b/odb/makefile index bd47cc2..dd277de 100644 --- a/odb/makefile +++ b/odb/makefile @@ -7,101 +7,126 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make # Plugin units. # -cxx_ptun := \ -cxx-lexer.cxx \ -sql-token.cxx \ -sql-lexer.cxx \ -context.cxx \ -common.cxx \ -diagnostics.cxx \ -emitter.cxx \ -lookup.cxx \ -include.cxx \ -header.cxx \ -inline.cxx \ -validator.cxx \ -processor.cxx \ -generator.cxx \ -parser.cxx \ -plugin.cxx \ +cxx_ptun := \ +cxx-lexer.cxx \ +sql-token.cxx \ +sql-lexer.cxx \ +context.cxx \ +common.cxx \ +diagnostics.cxx \ +emitter.cxx \ +lookup.cxx \ +include.cxx \ +header.cxx \ +inline.cxx \ +validator.cxx \ +processor.cxx \ +generator.cxx \ +parser.cxx \ +plugin.cxx \ pragma.cxx -# Tracer +# Tracer. # -cxx_ptun += \ -tracer/header.cxx \ -tracer/inline.cxx \ +cxx_ptun += \ +tracer/header.cxx \ +tracer/inline.cxx \ tracer/source.cxx -# Relational +# Relational. # -cxx_ptun += \ -relational/common.cxx \ -relational/context.cxx \ -relational/processor.cxx \ -relational/header.cxx \ -relational/inline.cxx \ -relational/source.cxx \ +cxx_ptun += \ +relational/common.cxx \ +relational/context.cxx \ +relational/processor.cxx \ +relational/header.cxx \ +relational/inline.cxx \ +relational/source.cxx \ +relational/model.cxx \ relational/schema.cxx -# Relational/MySQL +# Relational/MySQL. # -cxx_ptun += \ -relational/mysql/common.cxx \ -relational/mysql/context.cxx \ -relational/mysql/header.cxx \ -relational/mysql/source.cxx \ +cxx_ptun += \ +relational/mysql/common.cxx \ +relational/mysql/context.cxx \ +relational/mysql/header.cxx \ +relational/mysql/source.cxx \ +relational/mysql/model.cxx \ relational/mysql/schema.cxx # Relational/Oracle # -cxx_ptun += \ -relational/oracle/common.cxx \ -relational/oracle/context.cxx \ -relational/oracle/header.cxx \ -relational/oracle/source.cxx \ +cxx_ptun += \ +relational/oracle/common.cxx \ +relational/oracle/context.cxx \ +relational/oracle/header.cxx \ +relational/oracle/source.cxx \ +relational/oracle/model.cxx \ relational/oracle/schema.cxx # Relational/PostgreSQL # -cxx_ptun += \ -relational/pgsql/common.cxx \ -relational/pgsql/context.cxx \ -relational/pgsql/header.cxx \ -relational/pgsql/schema.cxx \ -relational/pgsql/source.cxx - -# Relational/SQLite +cxx_ptun += \ +relational/pgsql/common.cxx \ +relational/pgsql/context.cxx \ +relational/pgsql/header.cxx \ +relational/pgsql/source.cxx \ +relational/pgsql/model.cxx \ +relational/pgsql/schema.cxx + +# Relational/SQLite. # -cxx_ptun += \ -relational/sqlite/common.cxx \ -relational/sqlite/context.cxx \ -relational/sqlite/header.cxx \ -relational/sqlite/source.cxx \ +cxx_ptun += \ +relational/sqlite/common.cxx \ +relational/sqlite/context.cxx \ +relational/sqlite/header.cxx \ +relational/sqlite/source.cxx \ +relational/sqlite/model.cxx \ relational/sqlite/schema.cxx -cxx_ptun += \ -semantics/class.cxx \ -semantics/class-template.cxx \ -semantics/derived.cxx \ -semantics/elements.cxx \ -semantics/enum.cxx \ -semantics/fundamental.cxx \ -semantics/namespace.cxx \ -semantics/template.cxx \ -semantics/union.cxx \ -semantics/union-template.cxx \ +# Semantics. +# +cxx_ptun += \ +semantics/class.cxx \ +semantics/class-template.cxx \ +semantics/derived.cxx \ +semantics/elements.cxx \ +semantics/enum.cxx \ +semantics/fundamental.cxx \ +semantics/namespace.cxx \ +semantics/template.cxx \ +semantics/union.cxx \ +semantics/union-template.cxx \ semantics/unit.cxx -cxx_ptun += \ -traversal/class.cxx \ -traversal/class-template.cxx \ -traversal/derived.cxx \ -traversal/elements.cxx \ -traversal/enum.cxx \ -traversal/template.cxx \ +# Semantics/Relational. +# +cxx_ptun += \ +semantics/relational/column.cxx \ +semantics/relational/elements.cxx \ +semantics/relational/foreign-key.cxx \ +semantics/relational/index.cxx \ +semantics/relational/key.cxx \ +semantics/relational/model.cxx \ +semantics/relational/primary-key.cxx \ +semantics/relational/table.cxx + +# Traversal. +# +cxx_ptun += \ +traversal/class.cxx \ +traversal/class-template.cxx \ +traversal/derived.cxx \ +traversal/elements.cxx \ +traversal/enum.cxx \ +traversal/template.cxx \ traversal/union-template.cxx +# Traversal. +# +cxx_ptun += \ +traversal/relational/elements.cxx # Driver units. # diff --git a/odb/processor.cxx b/odb/processor.cxx index 4ace44b..2779957 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -21,7 +21,7 @@ process (options const& ops, semantics::unit& unit, semantics::path const&) // if (ops.database () != database::tracer) { - auto_ptr ctx (create_context (cerr, unit, ops)); + auto_ptr ctx (create_context (cerr, unit, ops, 0)); relational::process (); } } diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index 92edba6..1627a74 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -318,12 +318,28 @@ namespace relational } template + inline traversal::relational::edge_base& + operator>> (instance& n, traversal::relational::edge_base& e) + { + n->edge_traverser (e); + return e; + } + + template inline traversal::node_base& operator>> (traversal::edge_base& e, instance& n) { e.node_traverser (*n); return *n; } + + template + inline traversal::relational::node_base& + operator>> (traversal::relational::edge_base& e, instance& n) + { + e.node_traverser (*n); + return *n; + } } #endif // ODB_RELATIONAL_COMMON_HXX diff --git a/odb/relational/context.cxx b/odb/relational/context.cxx index 363b3bf..f080563 100644 --- a/odb/relational/context.cxx +++ b/odb/relational/context.cxx @@ -23,18 +23,18 @@ namespace relational context:: context () : data_ (current ().data_), - generate_grow (data_->generate_grow_), - need_alias_as (data_->need_alias_as_), + model (current ().model), + generate_grow (current ().generate_grow), + need_alias_as (current ().need_alias_as), bind_vector (data_->bind_vector_), truncated_vector (data_->truncated_vector_) { } context:: - context (data* d) + context (data* d, sema_rel::model* m) : data_ (d), - generate_grow (data_->generate_grow_), - need_alias_as (data_->need_alias_as_), + model (m), bind_vector (data_->bind_vector_), truncated_vector (data_->truncated_vector_) { diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx index cd2bce5..96e8954 100644 --- a/odb/relational/context.hxx +++ b/odb/relational/context.hxx @@ -8,8 +8,14 @@ #include +#include +#include + namespace relational { + namespace sema_rel = semantics::relational; + namespace trav_rel = traversal::relational; + enum statement_kind { statement_select, @@ -129,7 +135,7 @@ namespace relational struct data; typedef context base_context; - context (data*); + context (data*, sema_rel::model*); private: static context* current_; @@ -139,15 +145,14 @@ namespace relational { data (std::ostream& os): root_context::data (os) {} - bool generate_grow_; - bool need_alias_as_; - string bind_vector_; string truncated_vector_; }; data* data_; public: + sema_rel::model* model; + bool generate_grow; bool need_alias_as; diff --git a/odb/relational/generate.hxx b/odb/relational/generate.hxx index c7c7837..bf4b4aa 100644 --- a/odb/relational/generate.hxx +++ b/odb/relational/generate.hxx @@ -6,6 +6,10 @@ #ifndef ODB_RELATIONAL_GENERATE_HXX #define ODB_RELATIONAL_GENERATE_HXX +#include + +#include + namespace relational { namespace header @@ -26,6 +30,12 @@ namespace relational generate (); } + namespace model + { + cutl::shared_ptr + generate (); + } + namespace schema { void diff --git a/odb/relational/model.cxx b/odb/relational/model.cxx new file mode 100644 index 0000000..e44ffec --- /dev/null +++ b/odb/relational/model.cxx @@ -0,0 +1,168 @@ +// file : odb/relational/model.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace model + { + // object_columns + // + string object_columns:: + default_ (semantics::data_member& m) + { + default_value* dv (0); + + semantics::type& t (utype (m)); + + if (m.count ("default")) + dv = &m.get ("default"); + else if (t.count ("default")) + dv = &t.get ("default"); + else + return ""; // No default value for this column. + + switch (dv->kind) + { + case default_value::reset: + { + // No default value. + return ""; + } + case default_value::null: + { + return default_null (m); + break; + } + case default_value::boolean: + { + return default_bool (m, dv->value == "true"); + break; + } + case default_value::number: + { + tree n (dv->node); + + switch (TREE_CODE (n)) + { + case INTEGER_CST: + { + HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n)); + HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n)); + + unsigned long long l (hwl); + unsigned long long h (hwh); + unsigned short width (HOST_BITS_PER_WIDE_INT); + + unsigned long long v ((h << width) + l); + + return default_integer (m, v, dv->value == "-"); + break; + } + case REAL_CST: + { + double v; + + REAL_VALUE_TYPE d (TREE_REAL_CST (n)); + + if (REAL_VALUE_ISINF (d)) + v = numeric_limits::infinity (); + else if (REAL_VALUE_ISNAN (d)) + v = numeric_limits::quiet_NaN (); + else + { + char tmp[256]; + real_to_decimal (tmp, &d, sizeof (tmp), 0, true); + istringstream is (tmp); + is >> v; + } + + if (dv->value == "-") + v = -v; + + return default_float (m, v); + break; + } + default: + assert (false); + } + break; + } + case default_value::string: + { + return default_string (m, dv->value); + break; + } + case default_value::enumerator: + { + return default_enum (m, dv->node, dv->value); + break; + } + } + + return ""; + } + + cutl::shared_ptr + generate () + { + context ctx; + cutl::shared_ptr m (new (shared) sema_rel::model); + + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + instance c (*m); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + try + { + unit.dispatch (ctx.unit); + } + catch (sema_rel::duplicate_name const& e) + { + semantics::node& n (*e.nameable.get ("cxx-node")); + semantics::node& d (*e.duplicate.get ("cxx-node")); + + cerr << d.file () << ":" << d.line () << ":" << d.column () + << ": error: " << e.duplicate.kind () << " name '" + << e.nameable.name () << "' conflicts with an already defined " + << e.nameable.kind () << " name" + << endl; + + cerr << n.file () << ":" << n.line () << ":" << n.column () + << ": info: conflicting " << e.nameable.kind () << " is " + << "defined here" + << endl; + + cerr << d.file () << ":" << d.line () << ":" << d.column () + << ": error: use '#pragma db column' or '#pragma db table' " + << "to change one of the names" + << endl; + + throw operation_failed (); + } + + return m; + } + } +} diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx new file mode 100644 index 0000000..3d8cc4b --- /dev/null +++ b/odb/relational/model.hxx @@ -0,0 +1,528 @@ +// file : odb/relational/model.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_RELATIONAL_MODEL_HXX +#define ODB_RELATIONAL_MODEL_HXX + +#include +#include +#include + +#include + +#include + +#include +#include + +namespace relational +{ + namespace model + { + typedef std::set tables; + + struct object_columns: object_columns_base, virtual context + { + typedef object_columns base; + + object_columns (sema_rel::model& model, + sema_rel::table& table, + string const& prefix = string ()) + : model_ (model), + table_ (table), + prefix_ (prefix), + id_override_ (false) + { + } + + virtual void + traverse_object (semantics::class_& c) + { + if (context::top_object != &c) + { + // We are in one of the bases. Set the id_prefix to its + // (unqualified) name. + // + string t (id_prefix_); + id_prefix_ = c.name () + "::"; + object_columns_base::traverse_object (c); + id_prefix_ = t; + } + else + object_columns_base::traverse_object (c); + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + string t (id_prefix_); + + if (m != 0) + // Member of a composite type. Add the data member to id_prefix. + // + if (!id_override_) + id_prefix_ += m->name () + "."; + else + id_override_ = false; + else + // Composite base. Add its unqualified name to id_prefix. + // + id_prefix_ += c.name () + "::"; + + object_columns_base::traverse_composite (m, c); + + id_prefix_ = t; + } + + virtual void + traverse (semantics::data_member& m, + semantics::class_& c, + std::string const& kp, + std::string const& dn) + { + // This overrides the member name for a composite container value + // or key. + // + if (!kp.empty ()) + { + id_prefix_ = kp + "."; + id_override_ = true; + } + + object_columns_base::traverse (m, c, kp, dn); + } + + using object_columns_base::traverse; + + virtual bool + traverse_column (semantics::data_member& m, string const& name, bool) + { + // Ignore inverse object pointers. + // + if (inverse (m)) + return false; + + string id (id_prefix_ + (prefix_.empty () ? m.name () : prefix_)); + + sema_rel::column& c ( + model_.new_node ( + id, column_type (m, prefix_), context::null (m, prefix_))); + c.set ("cxx-node", static_cast (&m)); + + model_.new_edge (table_, c, name); + + // An id member cannot have a default value. + // + if (!context::id (m)) + { + string const& d (default_ (m)); + + if (!d.empty ()) + c.default_ (d); + } + + // If we have options, add them. + // + string const& o (column_options (m, prefix_)); + + if (!o.empty ()) + c.options (o); + + constraints (m, name, id, c); + reference (m, name, id, c); + + return true; + } + + virtual string + default_null (semantics::data_member&) + { + return "NULL"; + } + + virtual string + default_bool (semantics::data_member&, bool v) + { + // Most databases do not support boolean literals. Those that + // do should override this. + // + return (v ? "1" : "0"); + } + + virtual string + default_integer (semantics::data_member&, unsigned long long v, bool neg) + { + std::ostringstream ostr; + ostr << (neg ? "-" : "") << v; + return ostr.str (); + } + + virtual string + default_float (semantics::data_member&, double v) + { + std::ostringstream ostr; + ostr << v; + return ostr.str (); + } + + virtual string + default_string (semantics::data_member&, string const& v) + { + return quote_string (v); + } + + virtual string + default_enum (semantics::data_member&, + tree /*enumerator*/, + string const& /*name*/) + { + // Has to be implemented by the database-specific override. + // + assert (false); + } + + virtual void + constraints (semantics::data_member& m, + string const& /* name */, + string const& /* id */, + sema_rel::column& c) + { + if (!id (m)) + return; + + sema_rel::primary_key& pk ( + model_.new_node (m.count ("auto"))); + pk.set ("cxx-node", static_cast (&m)); + + model_.new_edge (pk, c); + + // In most databases the primary key constraint can be manipulated + // without an explicit name. So we use the special empty name for + // primary keys in order not to clash with columns and other + // constraints. If the target database does not support unnamed + // primary key manipulation, then the database-specific code will + // have to come up with a suitable name. + // + model_.new_edge (table_, pk, ""); + } + + virtual void + reference (semantics::data_member& m, + string const& name, + string const& id, + sema_rel::column& c) + { + semantics::class_* p (object_pointer (member_utype (m, prefix_))); + + if (p == 0) + return; + + sema_rel::foreign_key& fk ( + model_.new_node ( + id, + table_name (*p), + true)); // deferred + + fk.set ("cxx-node", static_cast (&m)); + + fk.referenced_columns ().push_back (column_name (*id_member (*p))); + + model_.new_edge (fk, c); + + // Derive the constraint name. Generally, we want it to be based + // on the column name. This is straightforward for single column + // references. In case of the composite ids, we will need to use + // the column prefix which is based on the data member name, + // unless overridden (see how the column pragma works for members + // of composite value types). @@ This is a TODO. Perhaps use the + // up-to-and-including composite member prefix? Though it can be + // empty. + // + model_.new_edge (table_, fk, name + "_fk"); + } + + protected: + string + default_ (semantics::data_member&); + + protected: + sema_rel::model& model_; + sema_rel::table& table_; + string prefix_; + string id_prefix_; + bool id_override_; + }; + + struct member_create: object_members_base, virtual context + { + typedef member_create base; + + member_create (sema_rel::model& model) + : object_members_base (false, true, false), model_ (model) + { + } + + virtual void + traverse_object (semantics::class_& c) + { + if (context::top_object != &c) + { + // We are in one of the bases. Set the id_prefix to its + // (unqualified) name. + // + string t (id_prefix_); + id_prefix_ = c.name () + "::"; + object_members_base::traverse_object (c); + id_prefix_ = t; + } + else + { + // Top-level object. Set its id as a prefix. + // + id_prefix_ = string (c.fq_name (), 2) + "::"; + object_members_base::traverse_object (c); + } + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + string t (id_prefix_); + + if (m != 0) + // Member of a composite type. Add the data member to id_prefix. + // + id_prefix_ += m->name () + "."; + else + // Composite base. Add its unqualified name to id_prefix. + // + id_prefix_ += c.name () + "::"; + + object_members_base::traverse_composite (m, c); + + id_prefix_ = t; + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type& ct) + { + using semantics::type; + using semantics::data_member; + + // Ignore inverse containers of object pointers. + // + if (inverse (m, "value")) + return; + + container_kind_type ck (container_kind (ct)); + type& vt (container_vt (ct)); + + string const& name (table_name (m, table_prefix_)); + + // Add the [] decorator to distinguish this id from non-container + // ids (we don't want to ever end up comparing, for example, an + // object table to a container table). + // + string id (id_prefix_ + m.name () + "[]"); + + sema_rel::container_table& t ( + model_.new_node (id)); + t.set ("cxx-node", static_cast (&m)); + + model_.new_edge (model_, t, name); + + // object_id (simple value, for now) + // + string id_name (column_name (m, "id", "object_id")); + { + instance oc (model_, t, "id"); + oc->traverse_column (m, id_name, true); + } + + // Foreign key for the object id. + // + { + sema_rel::foreign_key& fk ( + model_.new_node ( + id + ".id", + table_name (*context::top_object), + false, // immediate + sema_rel::foreign_key::cascade)); + + fk.set ("cxx-node", static_cast (&m)); + + fk.referenced_columns ().push_back ( + column_name ( + *id_member (*context::top_object))); + + // All the columns we have in this table so far are for the + // object id. + // + for (sema_rel::table::names_iterator i (t.names_begin ()); + i != t.names_end (); + ++i) + model_.new_edge ( + fk, dynamic_cast (i->nameable ())); + + // Derive the constraint name. See the comment for the other + // foreign key code above. + // + model_.new_edge (t, fk, id_name + "_fk"); + } + + // index (simple value) + // + string index_name; + bool ordered (ck == ck_ordered && !unordered (m)); + if (ordered) + { + instance oc (model_, t, "index"); + index_name = column_name (m, "index", "index"); + oc->traverse_column (m, index_name, true); + } + + // key (simple or composite value) + // + if (ck == ck_map || ck == ck_multimap) + { + type& kt (container_kt (ct)); + + if (semantics::class_* ckt = composite_wrapper (kt)) + { + instance oc (model_, t); + oc->traverse (m, *ckt, "key", "key"); + } + else + { + instance oc (model_, t, "key"); + string const& name (column_name (m, "key", "key")); + oc->traverse_column (m, name, true); + } + } + + // value (simple or composite value) + // + { + if (semantics::class_* cvt = composite_wrapper (vt)) + { + instance oc (model_, t); + oc->traverse (m, *cvt, "value", "value"); + } + else + { + instance oc (model_, t, "value"); + string const& name (column_name (m, "value", "value")); + oc->traverse_column (m, name, true); + } + } + + // Create indexes. + // + using sema_rel::index; + using sema_rel::column; + + { + index& i (model_.new_node (id + ".id")); + i.set ("cxx-node", static_cast (&m)); + + model_.new_edge ( + i, dynamic_cast (t.find (id_name)->nameable ())); + + //@@ Once id can be composite, we need to revise this (see + // a comment for the foreign key generation above). + // + model_.new_edge ( + model_, i, name + '_' + id_name + "_i"); + } + + if (ordered) + { + index& i (model_.new_node (id + ".index")); + i.set ("cxx-node", static_cast (&m)); + + model_.new_edge ( + i, dynamic_cast (t.find (index_name)->nameable ())); + + // This is always a single column (simple value). + // + model_.new_edge ( + model_, i, name + '_' + index_name + "_i"); + } + } + + protected: + sema_rel::model& model_; + string id_prefix_; + }; + + struct class_: traversal::class_, virtual context + { + typedef class_ base; + + class_ (sema_rel::model& model) + : model_ (model) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!object (c) || abstract (c)) + return; + + string const& name (table_name (c)); + + // If the table with this name was already created, assume the + // user knows what they are doing and skip it. + // + if (tables_.count (name)) + { + c.set ("model-range-first", model_.names_end ()); + c.set ("model-range-last", model_.names_end ()); + return; + } + + string id (c.fq_name (), 2); // Remove leading '::'. + + sema_rel::object_table& t( + model_.new_node (id)); + + t.set ("cxx-node", static_cast (&c)); + + model_.new_edge (model_, t, name); + + sema_rel::model::names_iterator begin (--model_.names_end ()); + + { + instance oc (model_, t); + oc->traverse (c); + } + + tables_.insert (name); + + // Create tables for members. + // + { + instance mc (model_); + mc->traverse (c); + } + + sema_rel::model::names_iterator end (--model_.names_end ()); + + c.set ("model-range-first", begin); + c.set ("model-range-last", end); + } + + protected: + sema_rel::model& model_; + tables tables_; + }; + } +} + +#endif // ODB_RELATIONAL_MODEL_HXX diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index 08f2df5..8862edf 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -67,16 +67,19 @@ namespace relational } context:: - context (ostream& os, semantics::unit& u, options_type const& ops) + context (ostream& os, + semantics::unit& u, + options_type const& ops, + sema_rel::model* m) : root_context (os, u, ops, data_ptr (new (shared) data (os))), - base_context (static_cast (root_context::data_.get ())), + base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { assert (current_ == 0); current_ = this; - data_->generate_grow_ = true; - data_->need_alias_as_ = true; + generate_grow = true; + need_alias_as = true; data_->bind_vector_ = "MYSQL_BIND*"; data_->truncated_vector_ = "my_bool*"; @@ -299,9 +302,6 @@ namespace relational // SQL type parsing. // - static sql_type - parse_sql_type (semantics::data_member& m, std::string const& sql); - sql_type const& context:: column_sql_type (semantics::data_member& m, string const& kp) { @@ -310,18 +310,30 @@ namespace relational : "mysql-" + kp + "-column-sql-type"); if (!m.count (key)) - m.set (key, parse_sql_type (m, column_type (m, kp))); + { + try + { + m.set (key, parse_sql_type (column_type (m, kp))); + } + catch (invalid_sql_type const& e) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: " << e.message () << endl; + + throw operation_failed (); + } + } return m.get (key); } - static sql_type - parse_sql_type (semantics::data_member& m, string const& sql) + sql_type context:: + parse_sql_type (string const& sqlt) { try { sql_type r; - sql_lexer l (sql); + sql_lexer l (sqlt); // While most type names use single identifier, there are // a couple of exceptions to this rule: @@ -568,16 +580,13 @@ namespace relational if (r.type == sql_type::invalid) { - cerr << m.file () << ":" << m.line () << ":" << - m.column () << ":"; - if (tt == sql_token::t_identifier) - cerr << " error: unknown MySQL type '" << - t.identifier () << "'" << endl; + { + throw invalid_sql_type ( + "unknown MySQL type '" + t.identifier () + "'"); + } else - cerr << " error: expected MySQL type name" << endl; - - throw operation_failed (); + throw invalid_sql_type ("expected MySQL type name"); } // Fall through. @@ -598,11 +607,9 @@ namespace relational { if (t.type () != sql_token::t_string_lit) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: string literal expected in MySQL ENUM " - << "or SET declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "string literal expected in MySQL ENUM or SET " + "declaration"); } if (r.type == sql_type::ENUM) @@ -614,11 +621,8 @@ namespace relational break; else if (t.punctuation () != sql_token::p_comma) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: comma expected in MySQL ENUM or " - << "SET declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "comma expected in MySQL ENUM or SET declaration"); } t = l.next (); @@ -628,11 +632,8 @@ namespace relational { if (t.type () != sql_token::t_int_lit) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: integer range expected in MySQL type " - << "declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "integer range expected in MySQL type declaration"); } unsigned int v; @@ -640,11 +641,9 @@ namespace relational if (!(is >> v && is.eof ())) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: invalid range value '" << t.literal () - << "'in MySQL type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid range value '" + t.literal () + "' in MySQL " + "type declaration"); } r.range = true; @@ -671,11 +670,8 @@ namespace relational if (t.punctuation () != sql_token::p_rparen) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: expected ')' in MySQL type declaration" - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "expected ')' in MySQL type declaration"); } s = parse_sign; @@ -729,10 +725,7 @@ namespace relational if (r.type == sql_type::invalid) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: incomplete MySQL type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ("incomplete MySQL type declaration"); } // If range is omitted for CHAR or BIT types, it defaults to 1. @@ -747,11 +740,8 @@ namespace relational } catch (sql_lexer::invalid_input const& e) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: invalid MySQL type declaration: " << e.message - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid MySQL type declaration: " + e.message); } } } diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 26568ec..2615397 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -84,6 +84,21 @@ namespace relational column_sql_type (semantics::data_member&, string const& key_prefix = string ()); + public: + struct invalid_sql_type + { + invalid_sql_type (string const& message): message_ (message) {} + + string const& + message () const {return message_;} + + private: + string message_; + }; + + static sql_type + parse_sql_type (string const&); + protected: virtual bool grow_impl (semantics::class_&); @@ -106,7 +121,10 @@ namespace relational virtual ~context (); context (); - context (std::ostream&, semantics::unit&, options_type const&); + context (std::ostream&, + semantics::unit&, + options_type const&, + sema_rel::model*); static context& current () diff --git a/odb/relational/mysql/model.cxx b/odb/relational/mysql/model.cxx new file mode 100644 index 0000000..0ebf65f --- /dev/null +++ b/odb/relational/mysql/model.cxx @@ -0,0 +1,111 @@ +// file : odb/relational/mysql/model.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace mysql + { + namespace model + { + namespace relational = relational::model; + + struct object_columns: relational::object_columns, context + { + object_columns (base const& x): base (x) {} + + virtual string + default_bool (semantics::data_member&, bool v) + { + // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still + // use them for self-documentation. + // + return v ? "TRUE" : "FALSE"; + } + + virtual string + default_enum (semantics::data_member& m, tree en, string const& name) + { + // Make sure the column is mapped to an ENUM or integer type. + // + sql_type const& t (column_sql_type (m)); + + switch (t.type) + { + case sql_type::ENUM: + case sql_type::TINYINT: + case sql_type::SMALLINT: + case sql_type::MEDIUMINT: + case sql_type::INT: + case sql_type::BIGINT: + break; + default: + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column with default value specified as C++ " + << "enumerator must map to MySQL ENUM or integer type" + << endl; + + throw operation_failed (); + } + } + + using semantics::enum_; + using semantics::enumerator; + + enumerator& er (dynamic_cast (*unit.find (en))); + enum_& e (er.enum_ ()); + + if (t.type == sql_type::ENUM) + { + // Assuming the enumerators in the C++ enum and MySQL ENUM are + // in the same order, calculate the poistion of the C++ + // enumerator and use that as an index in the MySQL ENUM. + // + size_t pos (0); + + for (enum_::enumerates_iterator i (e.enumerates_begin ()), + end (e.enumerates_end ()); i != end; ++i) + { + if (&i->enumerator () == &er) + break; + + pos++; + } + + if (pos < t.enumerators.size ()) + return t.enumerators[pos]; + else + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: unable to map C++ enumerator '" << name + << "' to MySQL ENUM value" << endl; + + throw operation_failed (); + } + } + else + { + ostringstream ostr; + + if (e.unsigned_ ()) + ostr << er.value (); + else + ostr << static_cast (er.value ()); + + return ostr.str (); + } + } + }; + entry object_columns_; + } + } +} diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index 485fa9f..428db1c 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -20,143 +20,110 @@ namespace relational // Create. // - struct create_common: virtual relational::create_common + struct create_column: relational::create_column, context { - virtual void - create_table_post () - { - os << ")"; - - string const& engine (options.mysql_engine ()); - - if (engine != "default") - os << endl - << " ENGINE=" << engine; - - os << endl; - } - }; - - struct object_columns: relational::object_columns, context - { - object_columns (base const& x): base (x) {} + create_column (base const& x): base (x) {} virtual void - null (semantics::data_member& m) + null (sema_rel::column& c) { // MySQL TIMESTAMP is by default NOT NULL. If we want it // to contain NULL values, we need to explicitly declare // the column as NULL. // - if (context::null (m, prefix_) && - column_sql_type (m, prefix_).type == sql_type::TIMESTAMP) - os << " NULL"; - else - base::null (m); + if (c.null ()) + { + // This should never fail since we have already parsed this. + // + sql_type const& t (parse_sql_type (c.type ())); + + if (t.type == sql_type::TIMESTAMP) + { + os << " NULL"; + return; + } + } + + base::null (c); } virtual void - default_bool (semantics::data_member&, bool v) + auto_ (sema_rel::column&) { - // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still - // use them for self-documentation. - // - os << " DEFAULT " << (v ? "TRUE" : "FALSE"); + os << " AUTO_INCREMENT"; } + }; + entry create_column_; + + struct create_foreign_key: relational::create_foreign_key, context + { + create_foreign_key (base const& x): base (x) {} virtual void - default_enum (semantics::data_member& m, tree en, string const& name) + traverse (sema_rel::foreign_key& fk) { - // Make sure the column is mapped to an ENUM or integer type. + // MySQL does not support deferred constraint checking. Output + // such foreign keys as comments, for documentation, unless we + // are generating embedded schema. // - sql_type const& t (column_sql_type (m)); - - switch (t.type) - { - case sql_type::ENUM: - case sql_type::TINYINT: - case sql_type::SMALLINT: - case sql_type::MEDIUMINT: - case sql_type::INT: - case sql_type::BIGINT: - break; - default: - { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: column with default value specified as C++ " - << "enumerator must map to MySQL ENUM or integer type" - << endl; - - throw operation_failed (); - } - } - - using semantics::enum_; - using semantics::enumerator; - - enumerator& er (dynamic_cast (*unit.find (en))); - enum_& e (er.enum_ ()); - - if (t.type == sql_type::ENUM) + if (fk.deferred ()) { - // Assuming the enumerators in the C++ enum and MySQL ENUM are - // in the same order, calculate the poistion of the C++ - // enumerator and use that as an index in the MySQL ENUM. + // Don't bloat C++ code with comment strings if we are + // generating embedded schema. // - size_t pos (0); - - for (enum_::enumerates_iterator i (e.enumerates_begin ()), - end (e.enumerates_end ()); i != end; ++i) + if (format_ != schema_format::embedded) { - if (&i->enumerator () == &er) - break; - - pos++; - } + os << endl + << endl + << " /*" << endl; - if (pos < t.enumerators.size ()) - os << " DEFAULT " << t.enumerators[pos]; - else - { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: unable to map C++ enumerator '" << name - << "' to MySQL ENUM value" << endl; + base::create (fk); - throw operation_failed (); + os << endl + << " */"; } } else - { - if (e.unsigned_ ()) - os << " DEFAULT " << er.value (); - else - os << " DEFAULT " << static_cast (er.value ()); - } + base::traverse (fk); } - virtual void - constraints (semantics::data_member& m) + virtual string + name (sema_rel::foreign_key& fk) { - base::constraints (m); - - if (m.count ("auto")) - os << " AUTO_INCREMENT"; + // In MySQL, foreign key names are database-global. Make them + // unique by prefixing the key name with table name. + // + return static_cast (fk.scope ()).name () + + '_' + fk.name (); } + virtual void + deferred () + { + // MySQL doesn't support deferred. + } }; - entry object_columns_; + entry create_foreign_key_; - struct member_create: relational::member_create, create_common + struct create_table: relational::create_table, context { - member_create (base const& x): base (x) {} - }; - entry member_create_; + create_table (base const& x): base (x) {} - struct class_create: relational::class_create, create_common - { - class_create (base const& x): base (x) {} + virtual void + create_post () + { + os << ")"; + + string const& engine (options.mysql_engine ()); + + if (engine != "default") + os << endl + << " ENGINE=" << engine; + + os << endl; + } }; - entry class_create_; + entry create_table_; } } } diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index 236c725..3fe3e6d 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -67,16 +67,19 @@ namespace relational } context:: - context (ostream& os, semantics::unit& u, options_type const& ops) + context (ostream& os, + semantics::unit& u, + options_type const& ops, + sema_rel::model* m) : root_context (os, u, ops, data_ptr (new (shared) data (os))), - base_context (static_cast (root_context::data_.get ())), + base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { assert (current_ == 0); current_ = this; - data_->generate_grow_ = false; - data_->need_alias_as_ = false; + generate_grow = false; + need_alias_as = false; data_->bind_vector_ = "oracle::bind*"; // Populate the C++ type to DB type map. @@ -143,9 +146,6 @@ namespace relational // SQL type parsing. // - static sql_type - parse_sql_type (semantics::data_member& m, std::string const& sql); - sql_type const& context:: column_sql_type (semantics::data_member& m, string const& kp) { @@ -154,18 +154,30 @@ namespace relational : "oracle-" + kp + "-column-sql-type"); if (!m.count (key)) - m.set (key, parse_sql_type (m, column_type (m, kp))); + { + try + { + m.set (key, parse_sql_type (column_type (m, kp))); + } + catch (invalid_sql_type const& e) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: " << e.message () << endl; + + throw operation_failed (); + } + } return m.get (key); } - static sql_type - parse_sql_type (semantics::data_member& m, string const& sql) + sql_type context:: + parse_sql_type (string const& sqlt) { try { sql_type r; - sql_lexer l (sql); + sql_lexer l (sqlt); // While most type names use single identifier, there are // a couple of exceptions to this rule: @@ -304,11 +316,9 @@ namespace relational (prefix == "TIMESTAMP WITH LOCAL TIME" || prefix == "TIMESTAMP WITH TIME")) { - cerr << m.file () << ":" << m.line () << ":" - << m.column ()<< ": error: Oracle timestamps with time " - << "zones are not currently supported" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "Oracle timestamps with time zones are not currently " + "supported"); } // // String and binary types. @@ -387,24 +397,18 @@ namespace relational // else if (id == "LONG") { - cerr << m.file () << ":" << m.line () << ":" - << m.column () << ": error: LONG types are not " - << " supported" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "Oracle LONG types are not supported"); } else { - cerr << m.file () << ":" << m.line () << ":" << - m.column () << ":"; - if (tt == sql_token::t_identifier) - cerr << " error: unknown Oracle type '" << - t.identifier () << "'" << endl; + { + throw invalid_sql_type ( + "unknown Oracle type '" + t.identifier () + "'"); + } else - cerr << " error: expected Oracle type name" << endl; - - throw operation_failed (); + throw invalid_sql_type ("expected Oracle type name"); } } @@ -440,16 +444,13 @@ namespace relational if (r.type == sql_type::invalid) { - cerr << m.file () << ":" << m.line () << ":" << - m.column () << ":"; - if (tt == sql_token::t_identifier) - cerr << " error: unknown Oracle type '" << - prefix + t.identifier () << "'" << endl; + { + throw invalid_sql_type ( + "unknown Oracle type '" + t.identifier () + "'"); + } else - cerr << " error: expected Oracle type name" << endl; - - throw operation_failed (); + throw invalid_sql_type ("expected Oracle type name"); } } @@ -465,11 +466,8 @@ namespace relational if (t.type () != sql_token::t_int_lit) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: integer range expected in Oracle type " - << "declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "integer range expected in Oracle type declaration"); } // Parse the range. @@ -480,12 +478,9 @@ namespace relational if (!(is >> v && is.eof ())) { - cerr << m.file () << ":" << m.line () << ":" - << m.column () - << ": error: invalid range value '" << t.literal () - << "'in Oracle type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid range value '" + t.literal () + "' in Oracle " + "type declaration"); } r.range = true; @@ -502,11 +497,8 @@ namespace relational if (t.type () != sql_token::t_int_lit) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: integer scale expected in Oracle type " - << "declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "integer scale expected in Oracle type declaration"); } short v; @@ -514,12 +506,9 @@ namespace relational if (!(is >> v && is.eof ())) { - cerr << m.file () << ":" << m.line () << ":" - << m.column () - << ": error: invalid scale value '" << t.literal () - << "'in Oracle type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid scale value '" + t.literal () + "' in Oracle " + "type declaration"); } r.scale = true; @@ -535,20 +524,16 @@ namespace relational r.byte_semantics = false; else if (id != "BYTE") { - cerr << m.file () << ":" << m.line () << ":" - << m.column () - << ": error: invalid keyword '" << t.literal () - << "'in Oracle type declaration" << endl; + throw invalid_sql_type ( + "invalid keyword '" + t.literal () + "' in Oracle " + "type declaration"); } } if (t.punctuation () != sql_token::p_rparen) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: expected ')' in Oracle type declaration" - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "expected ')' in Oracle type declaration"); } } @@ -594,21 +579,16 @@ namespace relational if (r.type == sql_type::invalid) { - cerr << "error: incomplete Oracle type declaration: " << prefix - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "incomplete Oracle type declaration: '" + prefix + "'"); } return r; } catch (sql_lexer::invalid_input const& e) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: invalid Oracle type declaration: " << e.message - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid Oracle type declaration: " + e.message); } } } diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx index d334f56..e99b273 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/relational/oracle/context.hxx @@ -74,6 +74,22 @@ namespace relational column_sql_type (semantics::data_member&, string const& key_prefix = string ()); + public: + struct invalid_sql_type + { + invalid_sql_type (string const& message): message_ (message) {} + + string const& + message () const {return message_;} + + private: + string message_; + }; + + static sql_type + parse_sql_type (string const&); + + public: static bool unsigned_integer (semantics::type&); @@ -90,7 +106,10 @@ namespace relational ~context (); context (); - context (std::ostream&, semantics::unit&, options_type const&); + context (std::ostream&, + semantics::unit&, + options_type const&, + sema_rel::model*); static context& current () diff --git a/odb/relational/oracle/model.cxx b/odb/relational/oracle/model.cxx new file mode 100644 index 0000000..a71d351 --- /dev/null +++ b/odb/relational/oracle/model.cxx @@ -0,0 +1,74 @@ +// file : odb/relational/oracle/model.cxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace oracle + { + namespace model + { + namespace relational = relational::model; + + struct object_columns: relational::object_columns, context + { + object_columns (base const& x): base (x) {} + + virtual string + default_bool (semantics::data_member&, bool v) + { + // Oracle seems to have the TRUE and FALSE literals, though no + // boolean type. + // + return v ? "TRUE" : "FALSE"; + } + + virtual string + default_enum (semantics::data_member& m, tree en, string const&) + { + // Make sure the column is mapped to Oracle NUMBER. + // + sql_type t (column_sql_type (m)); + + if (t.type != sql_type::NUMBER) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column with default value specified as C++ " + << "enumerator must map to Oracle NUMBER" << endl; + + throw operation_failed (); + } + + using semantics::enumerator; + + enumerator& e (dynamic_cast (*unit.find (en))); + + ostringstream ostr; + + if (e.enum_ ().unsigned_ ()) + ostr << e.value (); + else + ostr << static_cast (e.value ()); + + return ostr.str (); + } + + virtual void + reference (semantics::data_member&) + { + } + }; + entry object_columns_; + } + } +} diff --git a/odb/relational/oracle/schema.cxx b/odb/relational/oracle/schema.cxx index 8469761..a371235 100644 --- a/odb/relational/oracle/schema.cxx +++ b/odb/relational/oracle/schema.cxx @@ -3,7 +3,7 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file -#include +#include #include @@ -27,8 +27,13 @@ namespace relational virtual void line (const std::string& l) { - base::line (l); - last_ = l; + // SQLPlus doesn't like empty line in the middle of a statement. + // + if (!l.empty ()) + { + base::line (l); + last_ = l; + } } virtual void @@ -61,8 +66,11 @@ namespace relational schema_file (const base& x): base (x) {} virtual void - pre () + prologue () { + // Quiet down SQLPlus and make sure it exits with an error + // code if there is an error. + // os << "SET FEEDBACK OFF;" << endl << "WHENEVER SQLERROR EXIT FAILURE;" << endl << "WHENEVER OSERROR EXIT FAILURE;" << endl @@ -70,7 +78,7 @@ namespace relational } virtual void - post () + epilogue () { os << "EXIT;" << endl; } @@ -81,10 +89,12 @@ namespace relational // Drop. // - struct drop_common: virtual relational::drop_common + struct drop_table: relational::drop_table, context { + drop_table (base const& x): base (x) {} + virtual void - drop_table (string const& table) + drop (string const& table) { // Oracle has no IF EXISTS conditional for dropping objects. The // PL/SQL approach below seems to be the least error-prone and the @@ -107,7 +117,7 @@ namespace relational << " END;" << endl << " BEGIN" << endl << " EXECUTE IMMEDIATE 'DROP TRIGGER " << - quote_id (table + "_trig") << "';" << endl + quote_id (table + "_trg") << "';" << endl << " EXCEPTION" << endl << " WHEN OTHERS THEN" << endl << " IF SQLCODE != -4080 THEN RAISE; END IF;" << endl @@ -115,250 +125,176 @@ namespace relational << "END;" << endl; } }; - - struct member_drop: relational::member_drop, drop_common - { - member_drop (base const& x): base (x) {} - }; - entry member_drop_; - - struct class_drop: relational::class_drop, drop_common - { - class_drop (base const& x): base (x) {} - }; - entry class_drop_; + entry drop_table_; // // Create. // - struct object_columns: relational::object_columns, context + struct create_foreign_key; + + struct create_table: relational::create_table, context { - object_columns (base const& x): base (x) {} + create_table (base const& x): base (x) {} - virtual void - null (semantics::data_member& m) - { - sql_type::core_type t (column_sql_type (m, prefix_).type); + void + traverse (sema_rel::table&); - // Oracle interprets empty VARCHAR2 and NVARCHAR2 strings as NULL. As - // an empty string is always valid within the C++ context, VARCHAR2 - // and NVARCHAR2 columns are always specified as nullable. - // - if (t != sql_type::VARCHAR2 && t != sql_type::NVARCHAR2) - base::null (m); - } + private: + friend class create_foreign_key; + set tables_; // Set of tables we have already defined. + }; + entry create_table_; + + struct create_column: relational::create_column, context + { + create_column (base const& x): base (x) {} virtual void - default_enum (semantics::data_member& m, tree en, string const&) + null (sema_rel::column& c) { - // Make sure the column is mapped to Oracle NUMBER. + // Oracle interprets empty VARCHAR2 and NVARCHAR2 strings as + // NULL. As an empty string is always valid within the C++ + // context, VARCHAR2 and NVARCHAR2 columns are always + // specified as nullable. // - sql_type t (column_sql_type (m)); - - if (t.type != sql_type::NUMBER) + if (!c.null ()) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: column with default value specified as C++ " - << "enumerator must map to Oracle NUMBER" << endl; + // This should never fail since we have already parsed this. + // + sql_type const& t (parse_sql_type (c.type ())); - throw operation_failed (); + if (t.type == sql_type::VARCHAR2 || t.type == sql_type::NVARCHAR2) + return; } - using semantics::enumerator; - - enumerator& e (dynamic_cast (*unit.find (en))); - - if (e.enum_ ().unsigned_ ()) - os << " DEFAULT " << e.value (); - else - os << " DEFAULT " << static_cast (e.value ()); - } - - virtual void - reference (semantics::data_member&) - { + base::null (c); } }; - entry object_columns_; + entry create_column_; - struct object_columns_references: - object_columns_base, relational::common, context + struct create_foreign_key: relational::create_foreign_key, context { - object_columns_references (emitter& e, - ostream& os, - string const& table, - string const& prefix = string ()) - : relational::common (e, os), - table_ (table), - prefix_ (prefix) + create_foreign_key (schema_format f, relational::create_table& ct) + : base (f, ct) { } - virtual bool - traverse_column (semantics::data_member& m, string const& name, bool) - { - if (inverse (m)) - return false; - - if (semantics::class_* c = - object_pointer (member_utype (m, prefix_))) - { - pre_statement (); + create_foreign_key (base const& x): base (x) {} - os << "ALTER TABLE " << quote_id (table_) << endl - << " ADD FOREIGN KEY (" << quote_id (name) << ")" << endl - << " REFERENCES " << table_qname (*c) << endl - << " DEFERRABLE INITIALLY DEFERRED" << endl; + virtual void + traverse (sema_rel::foreign_key& fk) + { + // If the referenced table has already been defined, do the + // foreign key definition in the table definition. Otherwise + // postpone it until pass 2 where we do it via ALTER TABLE + // (see add_foreign_key below). + // + create_table& ct (static_cast (create_table_)); - post_statement (); - } - else if (prefix_ == "id") + if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ()) { - semantics::class_& c (*context::top_object); - - pre_statement (); - - // We don't need INITIALLY DEFERRED here since the object row - // must exist before any container row. - // - os << "ALTER TABLE " << quote_id (table_) << endl - << " ADD FOREIGN KEY (" << quote_id (name) << ")" << endl - << " REFERENCES " << table_qname (c) << endl - << " ON DELETE CASCADE" << endl; - - post_statement (); + base::traverse (fk); + fk.set ("oracle-fk-defined", true); // Mark it as defined. } - - return true; } - private: - string table_; - string prefix_; + virtual string + name (sema_rel::foreign_key& fk) + { + // In Oracle, foreign key names are schema-global. Make them + // unique by prefixing the key name with table name. + // + return static_cast (fk.scope ()).name () + + '_' + fk.name (); + } }; + entry create_foreign_key_; - struct member_create: object_members_base, context + struct add_foreign_key: create_foreign_key, relational::common { - member_create (emitter& e, ostream& os, relational::tables& tables) - : object_members_base (false, true, false), - e_ (e), - os_ (os), - tables_ (tables) + add_foreign_key (schema_format f, relational::create_table& ct) + : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ()) { } virtual void - traverse_container (semantics::data_member& m, semantics::type& t) + traverse (sema_rel::foreign_key& fk) { - using semantics::type; - using semantics::data_member; - - // Ignore inverse containers of object pointers. - // - if (inverse (m, "value")) - return; - - string const& name (table_name (m, table_prefix_)); - - if (tables_.count (name)) - return; + if (!fk.count ("oracle-fk-defined")) + { + sema_rel::table& t (dynamic_cast (fk.scope ())); - type& vt (container_vt (t)); + pre_statement (); - // object_id - // - { - object_columns_references ocr (e_, os_, name, "id"); - string id_name (column_name (m, "id", "object_id")); - ocr.traverse_column (m, id_name, true); - } + os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl; + base::create (fk); + os << endl; - // value - // - if (semantics::class_* cvt = composite_wrapper (vt)) - { - object_columns_references ocr (e_, os_, name); - ocr.traverse (m, *cvt, "value", "value"); - } - else - { - object_columns_references ocr (e_, os_, name, "value"); - string const& value_name (column_name (m, "value", "value")); - ocr.traverse_column (m, value_name, true); + post_statement (); } - - tables_.insert (name); } - - private: - emitter& e_; - ostream& os_; - relational::tables& tables_; }; - struct class_create: relational::class_create, context + void create_table:: + traverse (sema_rel::table& t) { - class_create (base const& x): base (x) {} - - virtual void - traverse (type& c) + if (pass_ == 1) { - if (pass_ != 2) - { - base::traverse (c); - return; - } + tables_.insert (t.name ()); // Add it before to cover self-refs. + base::traverse (t); - if (c.file () != unit.file ()) - return; - - if (!object (c) || abstract (c)) - return; - - string const& name (table_name (c)); + // Create the sequence and trigger if we have auto primary key. + // + using sema_rel::primary_key; - if (tables_[pass_].count (name)) - return; + sema_rel::table::names_iterator i (t.find ("")); // Special name. - semantics::data_member* id (id_member (c)); + primary_key* pk (i != t.names_end () + ? &dynamic_cast (i->nameable ()) + : 0); - if (id->count ("auto")) + if (pk != 0 && pk->auto_ ()) { - string seq_name (quote_id (name + "_seq")); + string const& tname (t.name ()); + string const& cname (pk->contains_begin ()->column ().name ()); + string seq_name (tname + "_seq"); + string trg_name (tname + "_trg"); + + // Sequence. + // pre_statement (); - os_ << "CREATE SEQUENCE " << seq_name << endl - << " START WITH 1 INCREMENT BY 1" << endl - << endl; + os_ << "CREATE SEQUENCE " << quote_id (seq_name) << endl + << " START WITH 1 INCREMENT BY 1" << endl; post_statement (); + // Trigger. + // pre_statement (); - os_ << "CREATE TRIGGER " << - quote_id (name + "_trig") << endl - << " BEFORE INSERT ON " << quote_id (name) << endl + os_ << "CREATE TRIGGER " << quote_id (trg_name) << endl + << " BEFORE INSERT ON " << quote_id (tname) << endl << " FOR EACH ROW" << endl << "BEGIN" << endl - << " SELECT " << seq_name << ".nextval INTO :new." << - column_qname (*id) << " FROM DUAL;" << endl + << " SELECT " << quote_id (seq_name) << ".nextval " << + "INTO :new." << quote_id (cname) << " FROM DUAL;" << endl << "END;" << endl; post_statement (); } - object_columns_references ocr (e_, os_, name); - ocr.traverse (c); - - tables_[pass_].insert (name); - - member_create mc (e_, os_, tables_[pass_]); - mc.traverse (c); + return; } - }; - entry class_create_; + + // Add foreign keys. + // + instance fk (format_, *this); + trav_rel::names n (*fk); + names (t, n); + } } } } diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 28a6cbd..c17b41f 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -67,16 +67,19 @@ namespace relational } context:: - context (ostream& os, semantics::unit& u, options_type const& ops) + context (ostream& os, + semantics::unit& u, + options_type const& ops, + sema_rel::model* m) : root_context (os, u, ops, data_ptr (new (shared) data (os))), - base_context (static_cast (root_context::data_.get ())), + base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { assert (current_ == 0); current_ = this; - data_->generate_grow_ = true; - data_->need_alias_as_ = true; + generate_grow = true; + need_alias_as = true; data_->bind_vector_ = "pgsql::bind*"; data_->truncated_vector_ = "bool*"; @@ -232,9 +235,6 @@ namespace relational // SQL type parsing. // - static sql_type - parse_sql_type (semantics::data_member& m, std::string const& sql); - sql_type const& context:: column_sql_type (semantics::data_member& m, string const& kp) { @@ -243,18 +243,30 @@ namespace relational : "pgsql-" + kp + "-column-sql-type"); if (!m.count (key)) - m.set (key, parse_sql_type (m, column_type (m, kp))); + { + try + { + m.set (key, parse_sql_type (column_type (m, kp))); + } + catch (invalid_sql_type const& e) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: " << e.message () << endl; + + throw operation_failed (); + } + } return m.get (key); } - static sql_type - parse_sql_type (semantics::data_member& m, string const& sql) + sql_type context:: + parse_sql_type (string const& sqlt) { try { sql_type r; - sql_lexer l (sql); + sql_lexer l (sqlt); // While most type names use single identifier, there are // a couple of exceptions to this rule: @@ -368,11 +380,8 @@ namespace relational } else if (id == "TIMETZ") { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: PostgreSQL time zones are not currently " - << "supported" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "PostgreSQL time zones are not currently supported"); } else if (id == "TIMESTAMP") { @@ -380,11 +389,8 @@ namespace relational } else if (id == "TIMESTAMPTZ") { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: PostgreSQL time zones are not currently " - << "supported" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "PostgreSQL time zones are not currently supported"); } // // String and binary types. @@ -450,16 +456,13 @@ namespace relational if (r.type == sql_type::invalid) { - cerr << m.file () << ":" << m.line () << ":" << - m.column () << ":"; - if (tt == sql_token::t_identifier) - cerr << " error: unknown PostgreSQL type '" << - t.identifier () << "'" << endl; + { + throw invalid_sql_type ( + "unknown PostgreSQL type '" + t.identifier () + "'"); + } else - cerr << " error: expected PostgreSQL type name" << endl; - - throw operation_failed (); + throw invalid_sql_type ("expected PostgreSQL type name"); } // Fall through. @@ -474,11 +477,8 @@ namespace relational if (t.type () != sql_token::t_int_lit) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: integer range expected in PostgreSQL type " - << "declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "integer range expected in PostgreSQL type declaration"); } unsigned int v; @@ -486,11 +486,9 @@ namespace relational if (!(is >> v && is.eof ())) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: invalid range value '" << t.literal () - << "'in PostgreSQL type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid range value '" + t.literal () + "' in PostgreSQL " + "type declaration"); } r.range = true; @@ -508,11 +506,8 @@ namespace relational if (t.punctuation () != sql_token::p_rparen) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: expected ')' in PostgreSQL type " - << "declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "expected ')' in PostgreSQL type declaration"); } s = parse_suffix; @@ -549,11 +544,9 @@ namespace relational if (id3 == "ZONE") { - cerr << m.file () << ":" << m.line () << ":" - << m.column ()<< ": error: PostgreSQL time " - << "zones are not currently supported" << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "PostgreSQL time zones are not currently " + "supported"); } } } @@ -596,10 +589,7 @@ namespace relational if (r.type == sql_type::invalid) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: incomplete PostgreSQL type declaration" << endl; - - throw operation_failed (); + throw invalid_sql_type ("incomplete PostgreSQL type declaration"); } // If range is omitted for CHAR or BIT types, it defaults to 1. @@ -614,11 +604,8 @@ namespace relational } catch (sql_lexer::invalid_input const& e) { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: invalid PostgreSQL type declaration: " << e.message - << endl; - - throw operation_failed (); + throw invalid_sql_type ( + "invalid PostgreSQL type declaration: " + e.message); } } } diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx index a4fbc00..cab7095 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/relational/pgsql/context.hxx @@ -73,6 +73,21 @@ namespace relational column_sql_type (semantics::data_member&, string const& key_prefix = string ()); + public: + struct invalid_sql_type + { + invalid_sql_type (string const& message): message_ (message) {} + + string const& + message () const {return message_;} + + private: + string message_; + }; + + static sql_type + parse_sql_type (string const&); + protected: virtual bool grow_impl (semantics::class_&); @@ -92,7 +107,10 @@ namespace relational ~context (); context (); - context (std::ostream&, semantics::unit&, options_type const&); + context (std::ostream&, + semantics::unit&, + options_type const&, + sema_rel::model*); static context& current () diff --git a/odb/relational/pgsql/model.cxx b/odb/relational/pgsql/model.cxx new file mode 100644 index 0000000..83ab289 --- /dev/null +++ b/odb/relational/pgsql/model.cxx @@ -0,0 +1,71 @@ +// file : odb/relational/pgsql/model.cxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace pgsql + { + namespace model + { + namespace relational = relational::model; + + struct object_columns: relational::object_columns, context + { + object_columns (base const& x): base (x) {} + + virtual string + default_bool (semantics::data_member&, bool v) + { + return v ? "TRUE" : "FALSE"; + } + + virtual string + default_enum (semantics::data_member& m, tree en, string const&) + { + // Make sure the column is mapped to an integer type. + // + switch (column_sql_type (m).type) + { + case sql_type::SMALLINT: + case sql_type::INTEGER: + case sql_type::BIGINT: + break; + default: + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column with default value specified as C++ " + << "enumerator must map to PostgreSQL integer type" << endl; + + throw operation_failed (); + } + } + + using semantics::enumerator; + + enumerator& e (dynamic_cast (*unit.find (en))); + + ostringstream ostr; + + if (e.enum_ ().unsigned_ ()) + ostr << e.value (); + else + ostr << static_cast (e.value ()); + + return ostr.str (); + } + }; + entry object_columns_; + } + } +} diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx index 450d056..c723818 100644 --- a/odb/relational/pgsql/schema.cxx +++ b/odb/relational/pgsql/schema.cxx @@ -3,11 +3,15 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include + #include #include #include +using namespace std; + namespace relational { namespace pgsql @@ -20,42 +24,50 @@ namespace relational // Drop. // - struct drop_common: virtual relational::drop_common + struct drop_table: relational::drop_table, context { + drop_table (base const& x): base (x) {} + virtual void - drop_table (string const& table) + drop (string const& table) { - os << "DROP TABLE IF EXISTS " << quote_id (table) << " CASCADE" - << endl; + os << "DROP TABLE IF EXISTS " << quote_id (table) << + " CASCADE" << endl; } }; - - struct member_drop: relational::member_drop, drop_common - { - member_drop (base const& x): base (x) {} - }; - entry member_drop_; - - struct class_drop: relational::class_drop, drop_common - { - class_drop (base const& x): base (x) {} - }; - entry class_drop_; + entry drop_table_; // // Create. // - struct object_columns: relational::object_columns, context + struct create_foreign_key; + + struct create_table: relational::create_table, context { - object_columns (base const& x): base (x) {} + create_table (base const& x): base (x) {} + + void + traverse (sema_rel::table&); + + private: + friend class create_foreign_key; + set tables_; // Set of tables we have already defined. + }; + entry create_table_; + + struct create_column: relational::create_column, context + { + create_column (base const& x): base (x) {} virtual void - type (semantics::data_member& m) + type (sema_rel::column& c, bool auto_) { - if (m.count ("auto")) + if (auto_) { - sql_type const& t (column_sql_type (m)); + // This should never fail since we have already parsed this. + // + sql_type const& t (parse_sql_type (c.type ())); if (t.type == sql_type::INTEGER) os << "SERIAL"; @@ -63,7 +75,9 @@ namespace relational os << "BIGSERIAL"; else { - cerr << m.file () << ":" << m.line () << ":" << m.column () + semantics::node& n (*c.get ("cxx-node")); + + cerr << n.file () << ":" << n.line () << ":" << n.column () << ": error: automatically assigned object id must map " << "to PostgreSQL INTEGER or BIGINT" << endl; @@ -71,204 +85,87 @@ namespace relational } } else - { - base::type (m); - } + base::type (c, auto_); } + }; + entry create_column_; - virtual void - default_bool (semantics::data_member&, bool v) + struct create_foreign_key: relational::create_foreign_key, context + { + create_foreign_key (schema_format f, relational::create_table& ct) + : base (f, ct) { - os << " DEFAULT " << (v ? "TRUE" : "FALSE"); } + create_foreign_key (base const& x): base (x) {} + virtual void - default_enum (semantics::data_member& m, tree en, string const&) + traverse (sema_rel::foreign_key& fk) { - // Make sure the column is mapped to an integer type. + // If the referenced table has already been defined, do the + // foreign key definition in the table definition. Otherwise + // postpone it until pass 2 where we do it via ALTER TABLE + // (see add_foreign_key below). // - switch (column_sql_type (m).type) - { - case sql_type::SMALLINT: - case sql_type::INTEGER: - case sql_type::BIGINT: - break; - default: - { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: column with default value specified as C++ " - << "enumerator must map to PostgreSQL integer type" << endl; + create_table& ct (static_cast (create_table_)); - throw operation_failed (); - } + if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ()) + { + base::traverse (fk); + fk.set ("pgsql-fk-defined", true); // Mark it as defined. } - - using semantics::enumerator; - - enumerator& e (dynamic_cast (*unit.find (en))); - - if (e.enum_ ().unsigned_ ()) - os << " DEFAULT " << e.value (); - else - os << " DEFAULT " << static_cast (e.value ()); } virtual void - reference (semantics::data_member&) + deferred () { + os << endl + << " INITIALLY DEFERRED"; } }; - entry object_columns_; + entry create_foreign_key_; - struct object_columns_references: - object_columns_base, relational::common, context + struct add_foreign_key: create_foreign_key, relational::common { - object_columns_references (emitter& e, - ostream& os, - string const& table, - string const& prefix = string ()) - : relational::common (e, os), - table_ (table), - prefix_ (prefix) + add_foreign_key (schema_format f, relational::create_table& ct) + : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ()) { } - virtual bool - traverse_column (semantics::data_member& m, string const& name, bool) + virtual void + traverse (sema_rel::foreign_key& fk) { - if (inverse (m)) - return false; - - if (semantics::class_* c = - object_pointer (member_utype (m, prefix_))) - { - pre_statement (); - - os << "ALTER TABLE " << quote_id (table_) << endl - << " ADD FOREIGN KEY (" << quote_id (name) << ")" << endl - << " REFERENCES " << table_qname (*c) << endl - << " INITIALLY DEFERRED" << endl; - - post_statement (); - } - else if (prefix_ == "id") + if (!fk.count ("pgsql-fk-defined")) { - semantics::class_& c (*context::top_object); + sema_rel::table& t (dynamic_cast (fk.scope ())); pre_statement (); - // We don't need INITIALLY DEFERRED here since the object row - // must exist before any container row. - // - os << "ALTER TABLE " << quote_id (table_) << endl - << " ADD FOREIGN KEY (" << quote_id (name) << ")" << endl - << " REFERENCES " << table_qname (c) << endl - << " ON DELETE CASCADE" << endl; + os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl; + base::create (fk); + os << endl; post_statement (); } - - return true; } - - private: - string table_; - string prefix_; }; - struct member_create: object_members_base, context + void create_table:: + traverse (sema_rel::table& t) { - member_create (emitter& e, ostream& os, relational::tables& tables) - : object_members_base (false, true, false), - e_ (e), - os_ (os), - tables_ (tables) + if (pass_ == 1) { + tables_.insert (t.name ()); // Add it before to cover self-refs. + base::traverse (t); + return; } - virtual void - traverse_container (semantics::data_member& m, semantics::type& t) - { - using semantics::type; - using semantics::data_member; - - // Ignore inverse containers of object pointers. - // - if (inverse (m, "value")) - return; - - string const& name (table_name (m, table_prefix_)); - - if (tables_.count (name)) - return; - - type& vt (container_vt (t)); - - // object_id - // - { - object_columns_references ocr (e_, os_, name, "id"); - string id_name (column_name (m, "id", "object_id")); - ocr.traverse_column (m, id_name, true); - } - - // value - // - if (semantics::class_* cvt = composite_wrapper (vt)) - { - object_columns_references ocr (e_, os_, name); - ocr.traverse (m, *cvt, "value", "value"); - } - else - { - object_columns_references ocr (e_, os_, name, "value"); - string const& value_name (column_name (m, "value", "value")); - ocr.traverse_column (m, value_name, true); - } - - tables_.insert (name); - } - - private: - emitter& e_; - ostream& os_; - relational::tables& tables_; - }; - - struct class_create: relational::class_create - { - class_create (base const& x): base (x) {} - - virtual void - traverse (type& c) - { - if (pass_ != 2) - { - base::traverse (c); - return; - } - - if (c.file () != unit.file ()) - return; - - if (!object (c) || abstract (c)) - return; - - string const& name (table_name (c)); - - if (tables_[pass_].count (name)) - return; - - object_columns_references ocr (e_, os_, name); - ocr.traverse (c); - - tables_[pass_].insert (name); - - member_create mc (e_, os_, tables_[pass_]); - mc.traverse (c); - } - }; - entry class_create_; + // Add foreign keys. + // + instance fk (format_, *this); + trav_rel::names n (*fk); + names (t, n); + } } } } diff --git a/odb/relational/schema.cxx b/odb/relational/schema.cxx index 031bf64..8a06427 100644 --- a/odb/relational/schema.cxx +++ b/odb/relational/schema.cxx @@ -3,8 +3,6 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file -#include - #include #include #include @@ -20,101 +18,6 @@ namespace relational { namespace schema { - // object_columns - // - void object_columns:: - default_ (semantics::data_member& m) - { - default_value* dv (0); - - semantics::type& t (utype (m)); - - if (m.count ("default")) - dv = &m.get ("default"); - else if (t.count ("default")) - dv = &t.get ("default"); - else - return; // No default value for this column. - - switch (dv->kind) - { - case default_value::reset: - { - // No default value. - break; - } - case default_value::null: - { - default_null (m); - break; - } - case default_value::boolean: - { - default_bool (m, dv->value == "true"); - break; - } - case default_value::number: - { - tree n (dv->node); - - switch (TREE_CODE (n)) - { - case INTEGER_CST: - { - HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n)); - HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n)); - - unsigned long long l (hwl); - unsigned long long h (hwh); - unsigned short width (HOST_BITS_PER_WIDE_INT); - - unsigned long long v ((h << width) + l); - - default_integer (m, v, dv->value == "-"); - break; - } - case REAL_CST: - { - double v; - - REAL_VALUE_TYPE d (TREE_REAL_CST (n)); - - if (REAL_VALUE_ISINF (d)) - v = numeric_limits::infinity (); - else if (REAL_VALUE_ISNAN (d)) - v = numeric_limits::quiet_NaN (); - else - { - char tmp[256]; - real_to_decimal (tmp, &d, sizeof (tmp), 0, true); - istringstream is (tmp); - is >> v; - } - - if (dv->value == "-") - v = -v; - - default_float (m, v); - break; - } - default: - assert (false); - } - break; - } - case default_value::string: - { - default_string (m, dv->value); - break; - } - case default_value::enumerator: - { - default_enum (m, dv->node, dv->value); - break; - } - } - } - static char const file_header[] = "/* This file was generated by ODB, object-relational mapping (ORM)\n" " * compiler for C++.\n" @@ -128,33 +31,36 @@ namespace relational os << file_header; - instance emitter; - instance file; - file->pre (); + file->prologue (); + + instance em; + emitter_ostream emos (*em); + + schema_format f (schema_format::sql); // Drop. // { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - instance c (*emitter); - unit >> unit_defines >> ns; - unit_defines >> c; + instance model (*em, emos, f); + trav_rel::names names; + instance table (*em, emos, f); + instance index (*em, emos, f); - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; + model >> names; + names >> table; + names >> index; // Pass 1 and 2. // for (unsigned short pass (1); pass < 3; ++pass) { - c->pass (pass); - unit.dispatch (ctx.unit); + model->pass (pass); + table->pass (pass); + index->pass (pass); + + model->traverse (*ctx.model); } } @@ -163,29 +69,28 @@ namespace relational // Create. // { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - instance c (*emitter); + instance model (*em, emos, f); + trav_rel::names names; + instance table (*em, emos, f); + instance index (*em, emos, f); - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; + model >> names; + names >> table; + names >> index; // Pass 1 and 2. // for (unsigned short pass (1); pass < 3; ++pass) { - c->pass (pass); - unit.dispatch (ctx.unit); + model->pass (pass); + table->pass (pass); + index->pass (pass); + + model->traverse (*ctx.model); } } - file->post (); + file->epilogue (); } } } diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index ecafe2c..855f67f 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -11,7 +11,6 @@ #include #include - #include #include @@ -19,11 +18,11 @@ namespace relational { namespace schema { - typedef std::set tables; - struct common: virtual context { - common (emitter& e, ostream& os): e_ (e), os_ (os) {} + typedef ::emitter emitter_type; + + common (emitter_type& e, ostream& os): e_ (e), os_ (os) {} void pre_statement () @@ -39,8 +38,20 @@ namespace relational e_.post (); } + emitter_type& + emitter () const + { + return e_; + } + + ostream& + stream () const + { + return os_; + } + protected: - emitter& e_; + emitter_type& e_; ostream& os_; }; @@ -57,7 +68,7 @@ namespace relational virtual void line (const std::string& l) { - if (first_) + if (first_ && !l.empty ()) first_ = false; else os << endl; @@ -78,7 +89,7 @@ namespace relational }; // - // File. + // File prologue/epilogue. // struct schema_file: virtual context @@ -86,12 +97,12 @@ namespace relational typedef schema_file base; virtual void - pre () + prologue () { } virtual void - post () + epilogue () { } }; @@ -100,474 +111,467 @@ namespace relational // Drop. // - struct drop_common: virtual context + struct drop_table: trav_rel::table, common { + typedef drop_table base; + + drop_table (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f) + { + } + virtual void - drop_table (string const& table) + drop (string const& table) { os << "DROP TABLE IF EXISTS " << quote_id (table) << endl; } virtual void - drop_index (string const& /*table*/, string const& /*column*/) + traverse (sema_rel::table& t) { - // Most database systems drop indexes together with the table. + // By default we do everything in a single pass. But some + // databases may require the second pass. // + if (pass_ > 1) + return; - //os << "DROP INDEX IF EXISTS " << quote_id (table + '_' + column) - // << endl; + pre_statement (); + drop (t.name ()); + post_statement (); } + + void + pass (unsigned short p) + { + pass_ = p; + } + + protected: + schema_format format_; + unsigned short pass_; }; - struct member_drop: object_members_base, common, virtual drop_common + struct drop_index: trav_rel::index, common { - typedef member_drop base; + typedef drop_index base; - member_drop (emitter& e, ostream& os, std::vector& t) - : object_members_base (false, true, false), - common (e, os), - tables_ (t) + drop_index (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f) { } - void - pass (unsigned short p) + virtual void + drop (string const& /*index*/) { - pass_ = p; + // Most database systems drop indexes together with the table. + // + //os << "DROP INDEX IF EXISTS " << quote_id (index); } virtual void - traverse_container (semantics::data_member& m, semantics::type& c) + traverse (sema_rel::index& in) { - // Ignore inverse containers of object pointers. + // By default we do everything in a single pass. But some + // databases may require the second pass. // - if (inverse (m, "value")) - return; - - string const& name (table_name (m, table_prefix_)); - - if (tables_[pass_].count (name)) + if (pass_ > 1) return; - // Drop table. - // - pre_statement (); - drop_table (name); - post_statement (); - - tables_[pass_].insert (name); - - // Drop indexes. - // pre_statement (); - drop_index (name, column_name (m, "id", "object_id")); + drop (in.name ()); post_statement (); + } - if (container_kind (c) == ck_ordered && !unordered (m)) - { - pre_statement (); - drop_index (name, column_name (m, "index", "index")); - post_statement (); - } + void + pass (unsigned short p) + { + pass_ = p; } protected: - std::vector& tables_; + schema_format format_; unsigned short pass_; }; - struct class_drop: traversal::class_, common, virtual drop_common + struct drop_model: trav_rel::model, common { - typedef class_drop base; + typedef drop_model base; - class_drop (emitter& e) - : common (e, os_), os_ (e), member_drop_ (e, os_, tables_) + drop_model (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f) { - tables_.push_back (tables ()); // Dummy entry. - } - - class_drop (class_drop const& x) - : root_context (), //@@ -Wextra - context (), - common (x.e_, os_), os_ (x.e_), member_drop_ (x.e_, os_, tables_) - { - tables_.push_back (tables ()); // Dummy entry. - } - - void - pass (unsigned short p) - { - pass_ = p; - - if (tables_.size () == pass_) - tables_.push_back (tables ()); - - member_drop_->pass (p); } + // This version is only called for file schema. + // virtual void - traverse (type& c) + traverse (sema_rel::model& m) { - // By default we do everything in a single pass. But some - // databases may require the second pass. - // - if (pass_ == 1) - drop (c); + traverse (m.names_begin (), m.names_end ()); } virtual void - drop (type& c) + traverse (sema_rel::model::names_iterator begin, + sema_rel::model::names_iterator end) { - if (c.file () != unit.file ()) - return; - - if (!object (c) || abstract (c)) - return; - - string const& name (table_name (c)); - - if (tables_[pass_].count (name)) - return; - - // Drop tables for members. Do it before dropping the primary - // table -- some databases may prefer it that way. + // Traverse named entities in the reverse order. This way we + // drop them in the order opposite to creating. // - member_drop_->traverse (c); + if (begin != end) + { + for (--end;; --end) + { + dispatch (*end); - pre_statement (); - drop_table (name); - post_statement (); + if (begin == end) + break; + } + } + } - tables_[pass_].insert (name); + void + pass (unsigned short p) + { + pass_ = p; } protected: - emitter_ostream os_; + schema_format format_; unsigned short pass_; - std::vector tables_; // Seperate table for each pass. - instance member_drop_; }; // // Create. // + struct create_table; - struct object_columns: object_columns_base, virtual context + struct create_column: trav_rel::column, virtual context { - typedef object_columns base; + typedef create_column base; - object_columns (string const& prefix = string ()) - : prefix_ (prefix) + create_column (schema_format f, create_table& ct) + : format_ (f), create_table_ (ct), first_ (true) { } - virtual bool - traverse_column (semantics::data_member& m, - string const& name, - bool first) + virtual void + traverse (sema_rel::column& c) { - // Ignore inverse object pointers. - // - if (inverse (m)) - return false; - - if (!first) + if (first_) + first_ = false; + else os << "," << endl; - os << " " << quote_id (name) << " "; + create (c); + } - type (m); - null (m); + virtual void + create (sema_rel::column& c) + { + using sema_rel::column; - // An id member cannot have a default value. + // See if this column is (part of) a primary key. // - if (!m.count ("id")) - default_ (m); + sema_rel::primary_key* pk (0); + + for (column::contained_iterator i (c.contained_begin ()); + i != c.contained_end (); + ++i) + { + if ((pk = dynamic_cast (&i->key ()))) + break; + } + + os << " " << quote_id (c.name ()) << " "; - // If we have options, add them. + type (c, pk != 0 && pk->auto_ ()); + null (c); + + // If this is a single-column primary key, generate it inline. // - string const& o (column_options (m, prefix_)); + if (pk != 0 && pk->contains_size () == 1) + primary_key (); - if (!o.empty ()) - os << " " << o; + if (pk != 0 && pk->auto_ ()) + auto_ (c); - constraints (m); - reference (m); + if (!c.default_ ().empty ()) + os << " DEFAULT " << c.default_ (); - return true; + if (!c.options ().empty ()) + os << " " << c.options (); } virtual void - type (semantics::data_member& m) + type (sema_rel::column& c, bool /*auto*/) { - os << column_type (m, prefix_); + os << c.type (); } virtual void - null (semantics::data_member& m) + null (sema_rel::column& c) { - if (!context::null (m, prefix_)) + if (!c.null ()) os << " NOT NULL"; } virtual void - default_null (semantics::data_member&) + primary_key () { - os << " DEFAULT NULL"; + os << " PRIMARY KEY"; } virtual void - default_bool (semantics::data_member&, bool v) + auto_ (sema_rel::column&) { - // Most databases do not support boolean literals. Those that - // do should override this. - // - os << " DEFAULT " << (v ? "1" : "0"); } - virtual void - default_integer (semantics::data_member&, unsigned long long v, bool neg) - { - os << " DEFAULT " << (neg ? "-" : "") << v; - } + protected: + schema_format format_; + create_table& create_table_; + bool first_; + }; - virtual void - default_float (semantics::data_member&, double v) - { - os << " DEFAULT " << v; - } + struct create_primary_key: trav_rel::primary_key, virtual context + { + typedef create_primary_key base; - virtual void - default_string (semantics::data_member&, string const& v) + create_primary_key (schema_format f, create_table& ct) + : format_ (f), create_table_ (ct) { - os << " DEFAULT " << quote_string (v); } virtual void - default_enum (semantics::data_member&, - tree /*enumerator*/, - string const& /*name*/) + traverse (sema_rel::primary_key& pk) { - // Has to be implemented by the database-specific override. + // Single-column primary keys are generated inline in the + // column declaration. // - assert (false); - } + if (pk.contains_size () == 1) + return; - virtual void - constraints (semantics::data_member& m) - { - if (m.count ("id")) - os << " PRIMARY KEY"; + // We will always follow a column. + // + os << "," << endl + << endl; + + create (pk); } virtual void - reference (semantics::data_member& m) + create (sema_rel::primary_key& pk) { - if (semantics::class_* c = object_pointer (member_utype (m, prefix_))) - { - os << " REFERENCES " << table_qname (*c) << " (" << - column_qname (*id_member (*c)) << ")"; - } - else if (prefix_ == "id") + using sema_rel::primary_key; + + // By default we create unnamed primary key constraint. + // + + os << " PRIMARY KEY ("; + + for (primary_key::contains_iterator i (pk.contains_begin ()); + i != pk.contains_end (); + ++i) { - // Container id column references the object table. It also - // cascades on delete so that we can delete the object with - // a single delete statement (needed for erase_query()). - // - semantics::class_& c (*context::top_object); - - os << " REFERENCES " << table_qname (c) << " (" << - column_qname (*id_member (c)) << ") ON DELETE CASCADE"; + if (pk.contains_size () > 1) + { + if (i != pk.contains_begin ()) + os << ","; + + os << endl + << " "; + } + + os << quote_id (i->column ().name ()); } - } - protected: - void - default_ (semantics::data_member&); + os << ")"; + } protected: - string prefix_; + schema_format format_; + create_table& create_table_; }; - struct create_common: virtual context + struct create_foreign_key: trav_rel::foreign_key, virtual context { - virtual void - create_table_pre (string const& table) - { - os << "CREATE TABLE " << quote_id (table) << " (" << endl; - } + typedef create_foreign_key base; - virtual void - create_table_post () + create_foreign_key (schema_format f, create_table& ct) + : format_ (f), create_table_ (ct) { - os << ")" << endl; } virtual void - create_index (string const& table, string const& column) + traverse (sema_rel::foreign_key& fk) { - os << "CREATE INDEX " << quote_id (table + '_' + column) << endl - << " ON " << quote_id (table) << " (" << quote_id (column) << ")" + // We will always follow a column or another key. + // + os << "," << endl << endl; - } - }; - - struct member_create: object_members_base, common, virtual create_common - { - typedef member_create base; - member_create (emitter& e, ostream& os, std::vector& t) - : object_members_base (false, true, false), - common (e, os), - tables_ (t) - { - } - - void - pass (unsigned short p) - { - pass_ = p; + create (fk); } virtual void - traverse_container (semantics::data_member& m, semantics::type& t) + create (sema_rel::foreign_key& fk) { - using semantics::type; - using semantics::data_member; + using sema_rel::foreign_key; - // Ignore inverse containers of object pointers. - // - if (inverse (m, "value")) - return; + os << " CONSTRAINT " << quote_id (name (fk)) << endl + << " FOREIGN KEY ("; - container_kind_type ck (container_kind (t)); - type& vt (container_vt (t)); - - string const& name (table_name (m, table_prefix_)); - - if (tables_[pass_].count (name)) - return; - - pre_statement (); - create_table_pre (name); - - // object_id (simple value) - // - string id_name (column_name (m, "id", "object_id")); + for (foreign_key::contains_iterator i (fk.contains_begin ()); + i != fk.contains_end (); + ++i) { - instance oc ("id"); - oc->traverse_column (m, id_name, true); - } + if (fk.contains_size () > 1) + { + if (i != fk.contains_begin ()) + os << ","; - // index (simple value) - // - string index_name; - bool ordered (ck == ck_ordered && !unordered (m)); - if (ordered) - { - os << "," << endl; + os << endl + << " "; + } - instance oc ("index"); - index_name = column_name (m, "index", "index"); - oc->traverse_column (m, index_name, true); + os << quote_id (i->column ().name ()); } - // key (simple or composite value) - // - if (ck == ck_map || ck == ck_multimap) - { - type& kt (container_kt (t)); + os << ")" << endl + << " REFERENCES " << quote_id (fk.referenced_table ()) << " ("; - os << "," << endl; + foreign_key::columns const& refs (fk.referenced_columns ()); - if (semantics::class_* ckt = composite_wrapper (kt)) - { - instance oc; - oc->traverse (m, *ckt, "key", "key"); - } - else + for (foreign_key::columns::const_iterator i (refs.begin ()); + i != refs.end (); + ++i) + { + if (refs.size () > 1) { - instance oc ("key"); - string const& name (column_name (m, "key", "key")); - oc->traverse_column (m, name, true); + if (i != refs.begin ()) + os << ","; + + os << endl + << " "; } + + os << quote_id (*i); } - // value (simple or composite value) - // - { - os << "," << endl; + os << ")"; - if (semantics::class_* cvt = composite_wrapper (vt)) - { - instance oc; - oc->traverse (m, *cvt, "value", "value"); - } - else - { - instance oc ("value"); - string const& name (column_name (m, "value", "value")); - oc->traverse_column (m, name, true); - } - } + if (fk.on_delete () != foreign_key::no_action) + on_delete (fk.on_delete ()); - create_table_post (); - post_statement (); + if (fk.deferred ()) + deferred (); + } - tables_[pass_].insert (name); + virtual string + name (sema_rel::foreign_key& fk) + { + return fk.name (); + } - // Create indexes. - // - pre_statement (); - create_index (name, id_name); - post_statement (); + virtual void + on_delete (sema_rel::foreign_key::action a) + { + using sema_rel::foreign_key; - if (ordered) + switch (a) { - pre_statement (); - create_index (name, index_name); - post_statement (); + case foreign_key::cascade: + { + os << endl + << " ON DELETE CASCADE"; + break; + } + case foreign_key::no_action: + break; } } + virtual void + deferred () + { + os << endl + << " DEFERRABLE INITIALLY DEFERRED"; + } + protected: - std::vector& tables_; - unsigned short pass_; + schema_format format_; + create_table& create_table_; }; - struct class_create: traversal::class_, common, virtual create_common + struct create_table: trav_rel::table, common { - typedef class_create base; + typedef create_table base; - class_create (emitter& e) - : common (e, os_), os_ (e), member_create_ (e, os_, tables_) + create_table (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f) { - tables_.push_back (tables ()); // Dummy entry. } - class_create (class_create const& x) - : root_context (), //@@ -Wextra - context (), - common (x.e_, os_), - os_ (x.e_), - member_create_ (x.e_, os_, tables_) + virtual void + create_pre (string const& table) { - tables_.push_back (tables ()); // Dummy entry. + os << "CREATE TABLE " << quote_id (table) << " (" << endl; + } + + virtual void + create_post () + { + os << ")" << endl; + } + + virtual void + traverse (sema_rel::table& t) + { + // By default we do everything in a single pass. But some + // databases may require the second pass. + // + if (pass_ > 1) + return; + + pre_statement (); + create_pre (t.name ()); + + instance c (format_, *this); + instance pk (format_, *this); + instance fk (format_, *this); + trav_rel::names n; + + n >> c; + n >> pk; + n >> fk; + + names (t, n); + + create_post (); + post_statement (); } void pass (unsigned short p) { pass_ = p; + } - if (tables_.size () == pass_) - tables_.push_back (tables ()); + protected: + schema_format format_; + unsigned short pass_; + }; - member_create_->pass (p); + struct create_index: trav_rel::index, common + { + typedef create_index base; + + create_index (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f) + { } virtual void - traverse (type& c) + traverse (sema_rel::index& in) { // By default we do everything in a single pass. But some // databases may require the second pass. @@ -575,43 +579,83 @@ namespace relational if (pass_ > 1) return; - if (c.file () != unit.file ()) - return; + pre_statement (); + create (in); + post_statement (); + } - if (!object (c) || abstract (c)) - return; + virtual void + create (sema_rel::index& in) + { + using sema_rel::index; - string const& name (table_name (c)); + os << "CREATE INDEX " << quote_id (in.name ()) << endl + << " ON " << quote_id (in.table ().name ()) << " ("; - // If the table with this name was already created, assume the - // user knows what they are doing and skip it. - // - if (tables_[pass_].count (name)) - return; + for (index::contains_iterator i (in.contains_begin ()); + i != in.contains_end (); + ++i) + { + if (in.contains_size () > 1) + { + if (i != in.contains_begin ()) + os << ","; - pre_statement (); - create_table_pre (name); + os << endl + << " "; + } - { - instance oc; - oc->traverse (c); + os << quote_id (i->column ().name ()); } - create_table_post (); - post_statement (); + os << ")" << endl; + } - tables_[pass_].insert (name); + void + pass (unsigned short p) + { + pass_ = p; + } - // Create tables for members. - // - member_create_->traverse (c); + protected: + schema_format format_; + unsigned short pass_; + }; + + struct create_model: trav_rel::model, common + { + typedef create_model base; + + create_model (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f) + { + } + + // This version is only called for file schema. + // + virtual void + traverse (sema_rel::model& m) + { + traverse (m.names_begin (), m.names_end ()); + } + + virtual void + traverse (sema_rel::model::names_iterator begin, + sema_rel::model::names_iterator end) + { + for (; begin != end; ++begin) + dispatch (*begin); + } + + void + pass (unsigned short p) + { + pass_ = p; } protected: - emitter_ostream os_; + schema_format format_; unsigned short pass_; - std::vector tables_; // Seperate table for each pass. - instance member_create_; }; } } diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index a216653..9b04ae4 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -2098,6 +2098,10 @@ namespace relational query_parameters& qp_; }; + //@@ (im)-perfect forwarding. + // + static schema_format format_embedded (schema_format::embedded); + // // struct class_: traversal::class_, virtual context @@ -2110,8 +2114,13 @@ namespace relational bind_id_member_ ("id_"), init_id_image_member_ ("id_", "id"), init_id_value_member_ ("id"), - schema_drop_ (schema_emitter_), - schema_create_ (schema_emitter_) + stream_ (emitter_), + drop_model_ (emitter_, stream_, format_embedded), + drop_table_ (emitter_, stream_, format_embedded), + drop_index_ (emitter_, stream_, format_embedded), + create_model_ (emitter_, stream_, format_embedded), + create_table_ (emitter_, stream_, format_embedded), + create_index_ (emitter_, stream_, format_embedded) { init (); } @@ -2124,8 +2133,13 @@ namespace relational bind_id_member_ ("id_"), init_id_image_member_ ("id_", "id"), init_id_value_member_ ("id"), - schema_drop_ (schema_emitter_), - schema_create_ (schema_emitter_) + stream_ (emitter_), + drop_model_ (emitter_, stream_, format_embedded), + drop_table_ (emitter_, stream_, format_embedded), + drop_index_ (emitter_, stream_, format_embedded), + create_model_ (emitter_, stream_, format_embedded), + create_table_ (emitter_, stream_, format_embedded), + create_index_ (emitter_, stream_, format_embedded) { init (); } @@ -2147,6 +2161,17 @@ namespace relational init_value_base_inherits_ >> init_value_base_; init_value_member_names_ >> init_value_member_; + + if (embedded_schema) + { + drop_model_ >> drop_names_; + drop_names_ >> drop_table_; + drop_names_ >> drop_index_; + + create_model_ >> create_names_; + create_names_ >> create_table_; + create_names_ >> create_index_; + } } virtual void @@ -3035,6 +3060,9 @@ namespace relational virtual void line (const string& l) { + if (l.empty ()) + return; // Ignore empty lines. + if (first_) { first_ = false; @@ -3088,6 +3116,17 @@ namespace relational virtual void schema (type& c) { + typedef sema_rel::model::names_iterator iterator; + + iterator begin (c.get ("model-range-first")); + iterator end (c.get ("model-range-last")); + + if (begin == model->names_end ()) + return; // This class doesn't have any model entities (e.g., + // a second class mapped to the same table). + + ++end; // Transform the range from [begin, end] to [begin, end). + string const& type (c.fq_name ()); string traits ("access::object_traits< " + type + " >"); @@ -3110,10 +3149,14 @@ namespace relational for (unsigned short pass (1); pass < 3; ++pass) { - schema_emitter_.pass (pass); - schema_drop_->pass (pass); - schema_drop_->traverse (c); - close = close || !schema_emitter_.empty (); + emitter_.pass (pass); + drop_model_->pass (pass); + drop_table_->pass (pass); + drop_index_->pass (pass); + + drop_model_->traverse (begin, end); + + close = close || !emitter_.empty (); } if (close) // Close the last case and the switch block. @@ -3134,10 +3177,14 @@ namespace relational for (unsigned short pass (1); pass < 3; ++pass) { - schema_emitter_.pass (pass); - schema_create_->pass (pass); - schema_create_->traverse (c); - close = close || !schema_emitter_.empty (); + emitter_.pass (pass); + create_model_->pass (pass); + create_table_->pass (pass); + create_index_->pass (pass); + + create_model_->traverse (begin, end); + + close = close || !emitter_.empty (); } if (close) // Close the last case and the switch block. @@ -4050,9 +4097,18 @@ namespace relational traversal::names init_value_member_names_; instance init_id_value_member_; - schema_emitter schema_emitter_; - instance schema_drop_; - instance schema_create_; + schema_emitter emitter_; + emitter_ostream stream_; + + trav_rel::names drop_names_; + instance drop_model_; + instance drop_table_; + instance drop_index_; + + trav_rel::names create_names_; + instance create_model_; + instance create_table_; + instance create_index_; }; struct include: virtual context diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx index 466c5b9..a769fac 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/relational/sqlite/context.cxx @@ -65,16 +65,19 @@ namespace relational } context:: - context (ostream& os, semantics::unit& u, options_type const& ops) + context (ostream& os, + semantics::unit& u, + options_type const& ops, + sema_rel::model* m) : root_context (os, u, ops, data_ptr (new (shared) data (os))), - base_context (static_cast (root_context::data_.get ())), + base_context (static_cast (root_context::data_.get ()), m), data_ (static_cast (base_context::data_)) { assert (current_ == 0); current_ = this; - data_->generate_grow_ = true; - data_->need_alias_as_ = true; + generate_grow = true; + need_alias_as = true; data_->bind_vector_ = "sqlite::bind*"; data_->truncated_vector_ = "bool*"; @@ -222,8 +225,8 @@ namespace relational { struct sql_parser { - sql_parser (semantics::data_member& m, std::string const& sql) - : m_ (m), l_ (sql) + sql_parser (std::string const& sql) + : l_ (sql) { } @@ -269,27 +272,20 @@ namespace relational } else { - cerr << m_.file () << ":" << m_.line () << ":" << m_.column () - << ": error: expected SQLite type name instead of '" - << t << "'" << endl; - throw operation_failed (); + throw context::invalid_sql_type ( + "expected SQLite type name instead of '" + t.string () + + "'"); } } } catch (sql_lexer::invalid_input const& e) { - cerr << m_.file () << ":" << m_.line () << ":" << m_.column () - << ": error: invalid SQLite type declaration: " << e.message - << endl; - throw operation_failed (); + throw context::invalid_sql_type ( + "invalid SQLite type declaration: " + e.message); } if (ids_.empty ()) - { - cerr << m_.file () << ":" << m_.line () << ":" << m_.column () - << ": error: expected SQLite type name" << endl; - throw operation_failed (); - } + throw context::invalid_sql_type ("expected SQLite type name"); sql_type r; @@ -322,9 +318,8 @@ namespace relational r.type = sql_type::TEXT; else { - cerr << m_.file () << ":" << m_.line () << ":" << m_.column () - << " error: unknown SQLite type '" << id << "'" << endl; - throw operation_failed (); + throw context::invalid_sql_type ( + "unknown SQLite type '" + id + "'"); } } @@ -343,10 +338,8 @@ namespace relational if (t.type () == sql_token::t_eos) { - cerr << m_.file () << ":" << m_.line () << ":" << m_.column () - << ": error: missing ')' in SQLite type declaration" - << endl; - throw operation_failed (); + throw context::invalid_sql_type ( + "missing ')' in SQLite type declaration"); } } } @@ -368,7 +361,6 @@ namespace relational typedef vector identifiers; private: - semantics::data_member& m_; sql_lexer l_; identifiers ids_; }; @@ -383,11 +375,27 @@ namespace relational if (!m.count (key)) { - sql_parser p (m, column_type (m, kp)); - m.set (key, p.parse ()); + try + { + m.set (key, parse_sql_type (column_type (m, kp))); + } + catch (invalid_sql_type const& e) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: " << e.message () << endl; + + throw operation_failed (); + } } return m.get (key); } + + sql_type context:: + parse_sql_type (string const& t) + { + sql_parser p (t); + return p.parse (); + } } } diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx index b5c3d85..de4e4c7 100644 --- a/odb/relational/sqlite/context.hxx +++ b/odb/relational/sqlite/context.hxx @@ -37,6 +37,21 @@ namespace relational column_sql_type (semantics::data_member&, string const& key_prefix = string ()); + public: + struct invalid_sql_type + { + invalid_sql_type (string const& message): message_ (message) {} + + string const& + message () const {return message_;} + + private: + string message_; + }; + + static sql_type + parse_sql_type (string const&); + protected: virtual bool grow_impl (semantics::class_&); @@ -55,7 +70,10 @@ namespace relational virtual ~context (); context (); - context (std::ostream&, semantics::unit&, options_type const&); + context (std::ostream&, + semantics::unit&, + options_type const&, + sema_rel::model*); static context& current () diff --git a/odb/relational/sqlite/model.cxx b/odb/relational/sqlite/model.cxx new file mode 100644 index 0000000..1324255 --- /dev/null +++ b/odb/relational/sqlite/model.cxx @@ -0,0 +1,58 @@ +// file : odb/relational/sqlite/model.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +#include +#include + +using namespace std; + +namespace relational +{ + namespace sqlite + { + namespace model + { + namespace relational = relational::model; + + struct object_columns: relational::object_columns, context + { + object_columns (base const& x): base (x) {} + + virtual string + default_enum (semantics::data_member& m, tree en, string const&) + { + // Make sure the column is mapped to INTEGER. + // + if (column_sql_type (m).type != sql_type::INTEGER) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column with default value specified as C++ " + << "enumerator must map to SQLite INTEGER" << endl; + + throw operation_failed (); + } + + using semantics::enumerator; + + enumerator& e (dynamic_cast (*unit.find (en))); + + ostringstream ostr; + + if (e.enum_ ().unsigned_ ()) + ostr << e.value (); + else + ostr << static_cast (e.value ()); + + return ostr.str (); + } + }; + entry object_columns_; + } + } +} diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx index 589ce7b..6562905 100644 --- a/odb/relational/sqlite/schema.cxx +++ b/odb/relational/sqlite/schema.cxx @@ -20,66 +20,20 @@ namespace relational // Create. // - struct object_columns: relational::object_columns, context + struct create_column: relational::create_column, context { - object_columns (base const& x): base (x) {} + create_column (base const& x): base (x) {} virtual void - default_enum (semantics::data_member& m, tree en, string const&) + auto_ (sema_rel::column&) { - // Make sure the column is mapped to INTEGER. - // - if (column_sql_type (m).type != sql_type::INTEGER) - { - cerr << m.file () << ":" << m.line () << ":" << m.column () - << ": error: column with default value specified as C++ " - << "enumerator must map to SQLite INTEGER" << endl; - - throw operation_failed (); - } - - using semantics::enumerator; - - enumerator& e (dynamic_cast (*unit.find (en))); - - if (e.enum_ ().unsigned_ ()) - os << " DEFAULT " << e.value (); + if (options.sqlite_lax_auto_id ()) + os << " /*AUTOINCREMENT*/"; else - os << " DEFAULT " << static_cast (e.value ()); + os << " AUTOINCREMENT"; } - - virtual void - constraints (semantics::data_member& m) - { - base::constraints (m); - - if (m.count ("auto")) - { - if (options.sqlite_lax_auto_id ()) - os << " /*AUTOINCREMENT*/"; - else - os << " AUTOINCREMENT"; - } - } - - virtual void - reference (semantics::data_member& m) - { - // In SQLite, by default, constraints are immediate. - // - if (semantics::class_* c = - object_pointer (member_utype (m, prefix_))) - { - os << " REFERENCES " << table_qname (*c) << " (" << - column_qname (*id_member (*c)) << ") " << - "DEFERRABLE INITIALLY DEFERRED"; - } - else - base::reference (m); - } - }; - entry object_columns_; + entry create_column_; } } } diff --git a/odb/semantics/relational.hxx b/odb/semantics/relational.hxx new file mode 100644 index 0000000..0d3d06c --- /dev/null +++ b/odb/semantics/relational.hxx @@ -0,0 +1,18 @@ +// file : odb/semantics/relational.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_HXX +#define ODB_SEMANTICS_RELATIONAL_HXX + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ODB_SEMANTICS_RELATIONAL_HXX diff --git a/odb/semantics/relational/column.cxx b/odb/semantics/relational/column.cxx new file mode 100644 index 0000000..99df5d9 --- /dev/null +++ b/odb/semantics/relational/column.cxx @@ -0,0 +1,35 @@ +// file : odb/semantics/relational/column.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +namespace semantics +{ + namespace relational + { + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // column + // + { + type_info ti (typeid (column)); + ti.add_base (typeid (nameable)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/column.hxx b/odb/semantics/relational/column.hxx new file mode 100644 index 0000000..a1024cf --- /dev/null +++ b/odb/semantics/relational/column.hxx @@ -0,0 +1,118 @@ +// file : odb/semantics/relational/column.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_COLUMN_HXX +#define ODB_SEMANTICS_RELATIONAL_COLUMN_HXX + +#include +#include + +namespace semantics +{ + namespace relational + { + class contains; + + class column: public nameable + { + typedef std::vector contained_list; + + public: + column (string const& id, string const& type, bool null) + : nameable (id), type_ (type), null_ (null) + { + } + + string const& + type () const + { + return type_; + } + + bool + null () const + { + return null_; + } + + string const& + default_ () const + { + return default__; + } + + void + default_ (string const& d) + { + default__ = d; + } + + string const& + options () const + { + return options_; + } + + void + options (string const& o) + { + options_ = o; + } + + public: + typedef relational::table table_type; + + table_type& + table () const + { + return dynamic_cast (scope ()); + } + + // Key containment. + // + public: + typedef + pointer_iterator + contained_iterator; + + contained_iterator + contained_begin () const + { + return contained_.begin (); + } + + contained_iterator + contained_end () const + { + return contained_.end (); + } + + public: + void + add_edge_right (contains& e) + { + contained_.push_back (&e); + } + + using nameable::add_edge_right; + + virtual string + kind () const + { + return "column"; + } + + private: + string type_; + bool null_; + string default__; + string options_; + + contained_list contained_; + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_COLUMN_HXX diff --git a/odb/semantics/relational/elements.cxx b/odb/semantics/relational/elements.cxx new file mode 100644 index 0000000..6ab977a --- /dev/null +++ b/odb/semantics/relational/elements.cxx @@ -0,0 +1,137 @@ +// file : odb/semantics/relational/elements.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include +#include + +namespace semantics +{ + namespace relational + { + // scope + // + + scope::names_iterator scope:: + find (string const& name) + { + names_map::iterator i (names_map_.find (name)); + + if (i == names_map_.end ()) + return names_.end (); + else + return i->second; + } + + scope::names_const_iterator scope:: + find (string const& name) const + { + names_map::const_iterator i (names_map_.find (name)); + + if (i == names_map_.end ()) + return names_.end (); + else + return names_const_iterator (i->second); + } + + scope::names_iterator scope:: + find (names const& e) + { + names_iterator_map::iterator i (iterator_map_.find (&e)); + return i != iterator_map_.end () ? i->second : names_.end (); + } + + scope::names_const_iterator scope:: + find (names const& e) const + { + names_iterator_map::const_iterator i (iterator_map_.find (&e)); + return i != iterator_map_.end () ? i->second : names_.end (); + } + + void scope:: + add_edge_left (names& e) + { + nameable& n (e.nameable ()); + string const& name (e.name ()); + + names_map::iterator i (names_map_.find (name)); + + if (i == names_map_.end ()) + { + names_list::iterator i; + + // We want the order to be columns first, then the primary key, + // and then the foreign keys. + // + if (n.is_a ()) + i = names_.insert (first_key_, &e); + else + { + if (n.is_a ()) + first_key_ = i = names_.insert (first_key_, &e); + else + { + i = names_.insert (names_.end (), &e); + + if (first_key_ == names_.end ()) + first_key_ = i; + } + } + + names_map_[name] = i; + iterator_map_[&e] = i; + } + else + throw duplicate_name (*this, (*i->second)->nameable (), n); + } + + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // node + // + insert (type_info (typeid (node))); + + // edge + // + insert (type_info (typeid (edge))); + + // names + // + { + type_info ti (typeid (names)); + ti.add_base (typeid (edge)); + insert (ti); + } + + // nameable + // + { + type_info ti (typeid (nameable)); + ti.add_base (typeid (node)); + insert (ti); + } + + // scope + // + { + type_info ti (typeid (scope)); + ti.add_base (typeid (node)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/elements.hxx b/odb/semantics/relational/elements.hxx new file mode 100644 index 0000000..d07902d --- /dev/null +++ b/odb/semantics/relational/elements.hxx @@ -0,0 +1,277 @@ +// file : odb/semantics/relational/elements.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX +#define ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace semantics +{ + namespace relational + { + using namespace cutl; + + using std::string; + + using container::graph; + using container::pointer_iterator; + + using compiler::context; + + // + // + class node; + class edge; + + // + // + class edge: public context + { + public: + virtual + ~edge () {} + + public: + template + bool + is_a () const + { + return dynamic_cast (this) != 0; + } + }; + + // + // + class node: public context + { + public: + virtual + ~node () {} + + // Return name of the node. + // + virtual string + kind () const = 0; + + public: + template + bool + is_a () const + { + return dynamic_cast (this) != 0; + } + + // Sink functions that allow extensions in the form of one-way + // edges. + // + public: + void + add_edge_right (edge&) + { + } + }; + + // + // + class scope; + class nameable; + + // + // + class names: public edge + { + public: + typedef relational::scope scope_type; + typedef relational::nameable nameable_type; + + string const& + name () const + { + return name_; + } + + scope_type& + scope () const + { + return *scope_; + } + + nameable_type& + nameable () const + { + return *nameable_; + } + + public: + names (string const& name): name_ (name) {} + + void + set_left_node (scope_type& n) + { + scope_ = &n; + } + + void + set_right_node (nameable_type& n) + { + nameable_ = &n; + } + + protected: + string name_; + scope_type* scope_; + nameable_type* nameable_; + }; + + // + // + class nameable: public virtual node + { + public: + typedef relational::scope scope_type; + + string const& + name () const + { + return named_->name (); + } + + scope_type& + scope () const + { + return named ().scope (); + } + + names& + named () const + { + return *named_; + } + + public: + // Id identifies the C++ node (e.g., a class or a data member) that + // this model node corresponds to. The ids are not necessarily unique + // (e.g., there can be a table and an index with the same id that + // correspond to a container member). However, in any given scope, + // the {id,typeid} must be unique. This becomes important when we + // try to find correspondance between nodes during model diff'ing. + // + nameable (string const& id): id_ (id), named_ (0) {} + + void + add_edge_right (names& e) + { + assert (named_ == 0); + named_ = &e; + } + + using node::add_edge_right; + + private: + string id_; + names* named_; + }; + + + // + // + struct duplicate_name + { + typedef relational::scope scope_type; + typedef relational::nameable nameable_type; + + duplicate_name (scope_type& s, nameable_type& n, nameable_type& d) + : scope (s), nameable (n), duplicate (d) + { + } + + scope_type& scope; + nameable_type& nameable; + nameable_type& duplicate; + }; + + class scope: public virtual node + { + protected: + typedef std::list names_list; + typedef std::map names_map; + typedef std::map names_iterator_map; + + public: + typedef pointer_iterator names_iterator; + typedef + pointer_iterator + names_const_iterator; + + public: + // Iteration. + // + names_iterator + names_begin () + { + return names_.begin (); + } + + names_iterator + names_end () + { + return names_.end (); + } + + names_const_iterator + names_begin () const + { + return names_.begin (); + } + + names_const_iterator + names_end () const + { + return names_.end (); + } + + // Find. + // + names_iterator + find (string const& name); + + names_const_iterator + find (string const& name) const; + + names_iterator + find (names const&); + + names_const_iterator + find (names const&) const; + + public: + scope () + : first_key_ (names_.end ()) + { + } + + void + add_edge_left (names&); + + private: + names_list names_; + names_map names_map_; + names_iterator_map iterator_map_; + + names_list::iterator first_key_; + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX diff --git a/odb/semantics/relational/foreign-key.cxx b/odb/semantics/relational/foreign-key.cxx new file mode 100644 index 0000000..eae9c5f --- /dev/null +++ b/odb/semantics/relational/foreign-key.cxx @@ -0,0 +1,35 @@ +// file : odb/semantics/relational/foreign-key.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +namespace semantics +{ + namespace relational + { + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // foreign_key + // + { + type_info ti (typeid (foreign_key)); + ti.add_base (typeid (key)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/foreign-key.hxx b/odb/semantics/relational/foreign-key.hxx new file mode 100644 index 0000000..4a0b543 --- /dev/null +++ b/odb/semantics/relational/foreign-key.hxx @@ -0,0 +1,87 @@ +// file : odb/semantics/relational/foreign-key.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX +#define ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX + +#include +#include + +namespace semantics +{ + namespace relational + { + class foreign_key: public key + { + public: + enum action + { + no_action, + cascade + }; + + foreign_key (string const& id, + string const& referenced_table, + bool deferred, + action on_delete = no_action) + : key (id), + referenced_table_ (referenced_table), + deferred_ (deferred), + on_delete_ (on_delete) + { + } + + public: + string + referenced_table () const + { + return referenced_table_; + } + + typedef std::vector columns; + + columns const& + referenced_columns () const + { + return referenced_columns_; + } + + columns& + referenced_columns () + { + return referenced_columns_; + } + + public: + bool + deferred () const + { + return deferred_; + } + + public: + action + on_delete () const + { + return on_delete_; + } + + public: + virtual string + kind () const + { + return "foreign key"; + } + + private: + string referenced_table_; + columns referenced_columns_; + bool deferred_; + action on_delete_; + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX diff --git a/odb/semantics/relational/index.cxx b/odb/semantics/relational/index.cxx new file mode 100644 index 0000000..376a312 --- /dev/null +++ b/odb/semantics/relational/index.cxx @@ -0,0 +1,35 @@ +// file : odb/semantics/relational/index.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +namespace semantics +{ + namespace relational + { + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // index + // + { + type_info ti (typeid (index)); + ti.add_base (typeid (key)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/index.hxx b/odb/semantics/relational/index.hxx new file mode 100644 index 0000000..ee6b202 --- /dev/null +++ b/odb/semantics/relational/index.hxx @@ -0,0 +1,44 @@ +// file : odb/semantics/relational/index.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_INDEX_HXX +#define ODB_SEMANTICS_RELATIONAL_INDEX_HXX + +#include +#include +#include + +namespace semantics +{ + namespace relational + { + // Note that unlike other keys, indexes are defined in the model + // scope, not table scope. + // + class index: public key + { + public: + relational::table& + table () const + { + return contains_begin ()->column ().table (); + } + + public: + index (string const& id) + : key (id) + { + } + + virtual string + kind () const + { + return "index"; + } + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_INDEX_HXX diff --git a/odb/semantics/relational/key.cxx b/odb/semantics/relational/key.cxx new file mode 100644 index 0000000..648fb26 --- /dev/null +++ b/odb/semantics/relational/key.cxx @@ -0,0 +1,43 @@ +// file : odb/semantics/relational/key.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +namespace semantics +{ + namespace relational + { + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // contains + // + { + type_info ti (typeid (contains)); + ti.add_base (typeid (edge)); + insert (ti); + } + + // key + // + { + type_info ti (typeid (key)); + ti.add_base (typeid (nameable)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/key.hxx b/odb/semantics/relational/key.hxx new file mode 100644 index 0000000..7e7a847 --- /dev/null +++ b/odb/semantics/relational/key.hxx @@ -0,0 +1,100 @@ +// file : odb/semantics/relational/key.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_KEY_HXX +#define ODB_SEMANTICS_RELATIONAL_KEY_HXX + +#include +#include + +namespace semantics +{ + namespace relational + { + class key; + + class contains: public edge + { + public: + typedef relational::key key_type; + typedef relational::column column_type; + + key_type& + key () const + { + return *key_; + } + + column_type& + column () const + { + return *column_; + } + + public: + void + set_left_node (key_type& n) + { + key_ = &n; + } + + void + set_right_node (column_type& n) + { + column_ = &n; + } + + protected: + key_type* key_; + column_type* column_; + }; + + class key: public nameable + { + typedef std::vector contains_list; + + public: + typedef + pointer_iterator + contains_iterator; + + contains_iterator + contains_begin () const + { + return contains_.begin (); + } + + contains_iterator + contains_end () const + { + return contains_.end (); + } + + contains_list::size_type + contains_size () const + { + return contains_.size (); + } + + public: + void + add_edge_left (contains& e) + { + contains_.push_back (&e); + } + + protected: + key (string const& id) + : nameable (id) + { + } + + private: + contains_list contains_; + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_KEY_HXX diff --git a/odb/semantics/relational/model.cxx b/odb/semantics/relational/model.cxx new file mode 100644 index 0000000..a0ac23f --- /dev/null +++ b/odb/semantics/relational/model.cxx @@ -0,0 +1,35 @@ +// file : odb/semantics/relational/model.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +namespace semantics +{ + namespace relational + { + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // model + // + { + type_info ti (typeid (model)); + ti.add_base (typeid (scope)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/model.hxx b/odb/semantics/relational/model.hxx new file mode 100644 index 0000000..7d2f944 --- /dev/null +++ b/odb/semantics/relational/model.hxx @@ -0,0 +1,39 @@ +// file : odb/semantics/relational/model.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_MODEL_HXX +#define ODB_SEMANTICS_RELATIONAL_MODEL_HXX + +#include + +namespace semantics +{ + namespace relational + { + class model: public graph, public scope + { + public: + model () + { + } + + virtual string + kind () const + { + return "model"; + } + + public: + using scope::add_edge_left; + using scope::add_edge_right; + + private: + model (model const&); + model& operator= (model const&); + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_MODEL_HXX diff --git a/odb/semantics/relational/primary-key.cxx b/odb/semantics/relational/primary-key.cxx new file mode 100644 index 0000000..367013e --- /dev/null +++ b/odb/semantics/relational/primary-key.cxx @@ -0,0 +1,35 @@ +// file : odb/semantics/relational/primary-key.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +namespace semantics +{ + namespace relational + { + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // primary_key + // + { + type_info ti (typeid (primary_key)); + ti.add_base (typeid (key)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/primary-key.hxx b/odb/semantics/relational/primary-key.hxx new file mode 100644 index 0000000..a35e8f5 --- /dev/null +++ b/odb/semantics/relational/primary-key.hxx @@ -0,0 +1,45 @@ +// file : odb/semantics/relational/primary-key.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX +#define ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX + +#include +#include + +namespace semantics +{ + namespace relational + { + class primary_key: public key + { + public: + bool + auto_ () const + { + return auto__; + } + + public: + // Primary key has the implicit empty id. + // + primary_key (bool auto_) + : key (""), auto__ (auto_) + { + } + + virtual string + kind () const + { + return "primary key"; + } + + private: + bool auto__; + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX diff --git a/odb/semantics/relational/table.cxx b/odb/semantics/relational/table.cxx new file mode 100644 index 0000000..a9290c6 --- /dev/null +++ b/odb/semantics/relational/table.cxx @@ -0,0 +1,52 @@ +// file : odb/semantics/relational/table.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +namespace semantics +{ + namespace relational + { + // type info + // + namespace + { + struct init + { + init () + { + using compiler::type_info; + + // table + // + { + type_info ti (typeid (table)); + ti.add_base (typeid (nameable)); + ti.add_base (typeid (scope)); + insert (ti); + } + + // object_table + // + { + type_info ti (typeid (object_table)); + ti.add_base (typeid (table)); + insert (ti); + } + + // container_table + // + { + type_info ti (typeid (container_table)); + ti.add_base (typeid (table)); + insert (ti); + } + } + } init_; + } + } +} diff --git a/odb/semantics/relational/table.hxx b/odb/semantics/relational/table.hxx new file mode 100644 index 0000000..d2f8649 --- /dev/null +++ b/odb/semantics/relational/table.hxx @@ -0,0 +1,56 @@ +// file : odb/semantics/relational/table.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_SEMANTICS_RELATIONAL_TABLE_HXX +#define ODB_SEMANTICS_RELATIONAL_TABLE_HXX + +#include + +namespace semantics +{ + namespace relational + { + class table: public nameable, public scope + { + protected: + table (string const& id) + : nameable (id) + { + } + }; + + class object_table: public table + { + public: + object_table (string const& id) + : table (id) + { + } + + virtual string + kind () const + { + return "object table"; + } + }; + + class container_table: public table + { + public: + container_table (string const& id) + : table (id) + { + } + + virtual string + kind () const + { + return "container table"; + } + }; + } +} + +#endif // ODB_SEMANTICS_RELATIONAL_TABLE_HXX diff --git a/odb/traversal/relational.hxx b/odb/traversal/relational.hxx new file mode 100644 index 0000000..1e0d9e6 --- /dev/null +++ b/odb/traversal/relational.hxx @@ -0,0 +1,18 @@ +// file : odb/traversal/relational.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_HXX +#define ODB_TRAVERSAL_RELATIONAL_HXX + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ODB_TRAVERSAL_RELATIONAL_HXX diff --git a/odb/traversal/relational/column.hxx b/odb/traversal/relational/column.hxx new file mode 100644 index 0000000..20c8716 --- /dev/null +++ b/odb/traversal/relational/column.hxx @@ -0,0 +1,20 @@ +// file : odb/traversal/relational/column.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX +#define ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + struct column: node {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX diff --git a/odb/traversal/relational/elements.cxx b/odb/traversal/relational/elements.cxx new file mode 100644 index 0000000..de9c259 --- /dev/null +++ b/odb/traversal/relational/elements.cxx @@ -0,0 +1,18 @@ +// file : odb/traversal/relational/elements.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +namespace traversal +{ + namespace relational + { + void names:: + traverse (type& e) + { + dispatch (e.nameable ()); + } + } +} diff --git a/odb/traversal/relational/elements.hxx b/odb/traversal/relational/elements.hxx new file mode 100644 index 0000000..72b8c39 --- /dev/null +++ b/odb/traversal/relational/elements.hxx @@ -0,0 +1,147 @@ +// file : odb/traversal/relational/elements.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX +#define ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + using namespace cutl; + + // + // + typedef compiler::dispatcher node_dispatcher; + typedef compiler::dispatcher edge_dispatcher; + + // + // + struct node_base: node_dispatcher, edge_dispatcher + { + void + edge_traverser (edge_dispatcher& d) + { + edge_dispatcher::traverser (d); + } + + edge_dispatcher& + edge_traverser () + { + return *this; + } + + using node_dispatcher::dispatch; + using edge_dispatcher::dispatch; + + using edge_dispatcher::iterate_and_dispatch; + }; + + struct edge_base: edge_dispatcher, node_dispatcher + { + void + node_traverser (node_dispatcher& d) + { + node_dispatcher::traverser (d); + } + + node_dispatcher& + node_traverser () + { + return *this; + } + + using edge_dispatcher::dispatch; + using node_dispatcher::dispatch; + + using node_dispatcher::iterate_and_dispatch; + }; + + inline edge_base& + operator>> (node_base& n, edge_base& e) + { + n.edge_traverser (e); + return e; + } + + inline node_base& + operator>> (edge_base& e, node_base& n) + { + e.node_traverser (n); + return n; + } + + // + // + template + struct node: compiler::traverser_impl, + virtual node_base + { + }; + + template + struct edge: compiler::traverser_impl, + virtual edge_base + { + }; + + // + // Edges + // + + struct names: edge + { + names () + { + } + + names (node_dispatcher& n) + { + node_traverser (n); + } + + virtual void + traverse (type&); + }; + + // + // Nodes + // + + struct nameable: node {}; + + // + // + template + struct scope_template: node + { + public: + virtual void + traverse (T& s) + { + names (s); + } + + virtual void + names (T& s) + { + names (s, *this); + } + + virtual void + names (T& s, edge_dispatcher& d) + { + iterate_and_dispatch (s.names_begin (), s.names_end (), d); + } + }; + + struct scope: scope_template {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX diff --git a/odb/traversal/relational/foreign-key.hxx b/odb/traversal/relational/foreign-key.hxx new file mode 100644 index 0000000..dc9b87e --- /dev/null +++ b/odb/traversal/relational/foreign-key.hxx @@ -0,0 +1,20 @@ +// file : odb/traversal/relational/foreign-key.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX +#define ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + struct foreign_key: key_template {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX diff --git a/odb/traversal/relational/index.hxx b/odb/traversal/relational/index.hxx new file mode 100644 index 0000000..20a8bc5 --- /dev/null +++ b/odb/traversal/relational/index.hxx @@ -0,0 +1,20 @@ +// file : odb/traversal/relational/index.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_INDEX_HXX +#define ODB_TRAVERSAL_RELATIONAL_INDEX_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + struct index: key_template {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_INDEX_HXX diff --git a/odb/traversal/relational/key.hxx b/odb/traversal/relational/key.hxx new file mode 100644 index 0000000..ae2487d --- /dev/null +++ b/odb/traversal/relational/key.hxx @@ -0,0 +1,43 @@ +// file : odb/traversal/relational/key.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_KEY_HXX +#define ODB_TRAVERSAL_RELATIONAL_KEY_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + template + struct key_template: node + { + public: + virtual void + traverse (T& k) + { + contains (k); + } + + virtual void + contains (T& k) + { + contains (k, *this); + } + + virtual void + contains (T& k, edge_dispatcher& d) + { + iterate_and_dispatch (k.contains_begin (), k.contains_end (), d); + } + }; + + struct key: key_template {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_KEY_HXX diff --git a/odb/traversal/relational/model.hxx b/odb/traversal/relational/model.hxx new file mode 100644 index 0000000..6c120f6 --- /dev/null +++ b/odb/traversal/relational/model.hxx @@ -0,0 +1,20 @@ +// file : odb/traversal/relational/model.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_MODEL_HXX +#define ODB_TRAVERSAL_RELATIONAL_MODEL_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + struct model: scope_template {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_MODEL_HXX diff --git a/odb/traversal/relational/primary-key.hxx b/odb/traversal/relational/primary-key.hxx new file mode 100644 index 0000000..6374e4c --- /dev/null +++ b/odb/traversal/relational/primary-key.hxx @@ -0,0 +1,20 @@ +// file : odb/traversal/relational/primary-key.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX +#define ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + struct primary_key: key_template {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX diff --git a/odb/traversal/relational/table.hxx b/odb/traversal/relational/table.hxx new file mode 100644 index 0000000..28783b1 --- /dev/null +++ b/odb/traversal/relational/table.hxx @@ -0,0 +1,22 @@ +// file : odb/traversal/relational/table.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_TRAVERSAL_RELATIONAL_TABLE_HXX +#define ODB_TRAVERSAL_RELATIONAL_TABLE_HXX + +#include +#include + +namespace traversal +{ + namespace relational + { + struct table: scope_template {}; + struct object_table: scope_template {}; + struct container_table: scope_template {}; + } +} + +#endif // ODB_TRAVERSAL_RELATIONAL_TABLE_HXX diff --git a/odb/validator.cxx b/odb/validator.cxx index 59d231f..34bb69e 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -661,7 +661,7 @@ validate (options const& ops, semantics::path const&, unsigned short pass) { - auto_ptr ctx (create_context (cerr, u, ops)); + auto_ptr ctx (create_context (cerr, u, ops, 0)); bool valid (true); -- cgit v1.1