diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2011-10-24 16:32:51 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2011-10-24 16:32:51 +0200 |
commit | 08a47c70ad517b80b72914d47d547463f576bcd3 (patch) | |
tree | 8a6ab07cf05e8668ea3c91735dfe97e2a98f3f05 /odb | |
parent | a976183dc95a8b7a9bd7a308c3ea94f08982c426 (diff) |
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.
Diffstat (limited to 'odb')
58 files changed, 3745 insertions, 1310 deletions
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<context> -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<context> 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 <odb/options.hxx> #include <odb/cxx-token.hxx> #include <odb/semantics.hxx> +#include <odb/semantics/relational/model.hxx> #include <odb/traversal.hxx> using std::endl; @@ -787,7 +788,10 @@ private: // Create concrete database context. // std::auto_ptr<context> -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<semantics::relational::model> model; + + if (ops.generate_schema ()) + { + auto_ptr<context> 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<context> ctx (create_context (hxx, unit, ops)); + auto_ptr<context> 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<context> ctx (create_context (ixx, unit, ops)); + auto_ptr<context> 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<context> ctx (create_context (cxx, unit, ops)); + auto_ptr<context> ctx (create_context (cxx, unit, ops, model.get ())); cxx << "#include <odb/pre.hxx>" << endl << endl; @@ -363,7 +389,7 @@ generate (options const& ops, semantics::unit& unit, path const& p) // if (sql_schema) { - auto_ptr<context> ctx (create_context (sql, unit, ops)); + auto_ptr<context> 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<context> ctx (create_context (cerr, unit, ops)); + auto_ptr<context> 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 <typename T> + inline traversal::relational::edge_base& + operator>> (instance<T>& n, traversal::relational::edge_base& e) + { + n->edge_traverser (e); + return e; + } + + template <typename T> inline traversal::node_base& operator>> (traversal::edge_base& e, instance<T>& n) { e.node_traverser (*n); return *n; } + + template <typename T> + inline traversal::relational::node_base& + operator>> (traversal::relational::edge_base& e, instance<T>& 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 <odb/context.hxx> +#include <odb/semantics/relational.hxx> +#include <odb/traversal/relational.hxx> + 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 <cutl/shared-ptr.hxx> + +#include <odb/semantics/relational/model.hxx> + namespace relational { namespace header @@ -26,6 +30,12 @@ namespace relational generate (); } + namespace model + { + cutl::shared_ptr<semantics::relational::model> + 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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <odb/gcc.hxx> + +#include <cassert> +#include <limits> +#include <sstream> + +#include <odb/relational/model.hxx> +#include <odb/relational/generate.hxx> + +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_value> ("default"); + else if (t.count ("default")) + dv = &t.get<default_value> ("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<double>::infinity (); + else if (REAL_VALUE_ISNAN (d)) + v = numeric_limits<double>::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<sema_rel::model> + generate () + { + context ctx; + cutl::shared_ptr<sema_rel::model> m (new (shared) sema_rel::model); + + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + instance<class_> 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<semantics::node*> ("cxx-node")); + semantics::node& d (*e.duplicate.get<semantics::node*> ("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 <boris@codesynthesis.com> +// 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 <set> +#include <cassert> +#include <sstream> + +#include <odb/emitter.hxx> + +#include <odb/semantics/relational.hxx> + +#include <odb/relational/common.hxx> +#include <odb/relational/context.hxx> + +namespace relational +{ + namespace model + { + typedef std::set<std::string> 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<sema_rel::column> ( + id, column_type (m, prefix_), context::null (m, prefix_))); + c.set ("cxx-node", static_cast<semantics::node*> (&m)); + + model_.new_edge<sema_rel::names> (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<sema_rel::primary_key> (m.count ("auto"))); + pk.set ("cxx-node", static_cast<semantics::node*> (&m)); + + model_.new_edge<sema_rel::contains> (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<sema_rel::names> (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<sema_rel::foreign_key> ( + id, + table_name (*p), + true)); // deferred + + fk.set ("cxx-node", static_cast<semantics::node*> (&m)); + + fk.referenced_columns ().push_back (column_name (*id_member (*p))); + + model_.new_edge<sema_rel::contains> (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<sema_rel::names> (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<sema_rel::container_table> (id)); + t.set ("cxx-node", static_cast<semantics::node*> (&m)); + + model_.new_edge<sema_rel::names> (model_, t, name); + + // object_id (simple value, for now) + // + string id_name (column_name (m, "id", "object_id")); + { + instance<object_columns> 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<sema_rel::foreign_key> ( + id + ".id", + table_name (*context::top_object), + false, // immediate + sema_rel::foreign_key::cascade)); + + fk.set ("cxx-node", static_cast<semantics::node*> (&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<sema_rel::contains> ( + fk, dynamic_cast<sema_rel::column&> (i->nameable ())); + + // Derive the constraint name. See the comment for the other + // foreign key code above. + // + model_.new_edge<sema_rel::names> (t, fk, id_name + "_fk"); + } + + // index (simple value) + // + string index_name; + bool ordered (ck == ck_ordered && !unordered (m)); + if (ordered) + { + instance<object_columns> 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<object_columns> oc (model_, t); + oc->traverse (m, *ckt, "key", "key"); + } + else + { + instance<object_columns> 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<object_columns> oc (model_, t); + oc->traverse (m, *cvt, "value", "value"); + } + else + { + instance<object_columns> 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<index> (id + ".id")); + i.set ("cxx-node", static_cast<semantics::node*> (&m)); + + model_.new_edge<sema_rel::contains> ( + i, dynamic_cast<column&> (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<sema_rel::names> ( + model_, i, name + '_' + id_name + "_i"); + } + + if (ordered) + { + index& i (model_.new_node<index> (id + ".index")); + i.set ("cxx-node", static_cast<semantics::node*> (&m)); + + model_.new_edge<sema_rel::contains> ( + i, dynamic_cast<column&> (t.find (index_name)->nameable ())); + + // This is always a single column (simple value). + // + model_.new_edge<sema_rel::names> ( + 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<sema_rel::object_table> (id)); + + t.set ("cxx-node", static_cast<semantics::node*> (&c)); + + model_.new_edge<sema_rel::names> (model_, t, name); + + sema_rel::model::names_iterator begin (--model_.names_end ()); + + { + instance<object_columns> oc (model_, t); + oc->traverse (c); + } + + tables_.insert (name); + + // Create tables for members. + // + { + instance<member_create> 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<data*> (root_context::data_.get ())), + base_context (static_cast<data*> (root_context::data_.get ()), m), data_ (static_cast<data*> (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<sql_type> (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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <sstream> + +#include <odb/relational/model.hxx> +#include <odb/relational/mysql/context.hxx> + +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<enumerator&> (*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<long long> (er.value ()); + + return ostr.str (); + } + } + }; + entry<object_columns> 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> 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<enumerator&> (*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<long long> (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<sema_rel::table&> (fk.scope ()).name () + + '_' + fk.name (); } + virtual void + deferred () + { + // MySQL doesn't support deferred. + } }; - entry<object_columns> object_columns_; + entry<create_foreign_key> 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> 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> class_create_; + entry<create_table> 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<data*> (root_context::data_.get ())), + base_context (static_cast<data*> (root_context::data_.get ()), m), data_ (static_cast<data*> (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<sql_type> (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 <constantin@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <sstream> + +#include <odb/relational/model.hxx> + +#include <odb/relational/oracle/common.hxx> +#include <odb/relational/oracle/context.hxx> + +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<enumerator&> (*unit.find (en))); + + ostringstream ostr; + + if (e.enum_ ().unsigned_ ()) + ostr << e.value (); + else + ostr << static_cast<long long> (e.value ()); + + return ostr.str (); + } + + virtual void + reference (semantics::data_member&) + { + } + }; + entry<object_columns> 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 <string> +#include <set> #include <odb/relational/schema.hxx> @@ -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> member_drop_; - - struct class_drop: relational::class_drop, drop_common - { - class_drop (base const& x): base (x) {} - }; - entry<class_drop> class_drop_; + entry<drop_table> 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<string> tables_; // Set of tables we have already defined. + }; + entry<create_table> 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<enumerator&> (*unit.find (en))); - - if (e.enum_ ().unsigned_ ()) - os << " DEFAULT " << e.value (); - else - os << " DEFAULT " << static_cast<long long> (e.value ()); - } - - virtual void - reference (semantics::data_member&) - { + base::null (c); } }; - entry<object_columns> object_columns_; + entry<create_column> 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&> (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<sema_rel::table&> (fk.scope ()).name () + + '_' + fk.name (); + } }; + entry<create_foreign_key> 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<sema_rel::table&> (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<primary_key&> (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> class_create_; + + // Add foreign keys. + // + instance<add_foreign_key> 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<data*> (root_context::data_.get ())), + base_context (static_cast<data*> (root_context::data_.get ()), m), data_ (static_cast<data*> (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<sql_type> (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 <constantin@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <sstream> + +#include <odb/relational/model.hxx> + +#include <odb/relational/pgsql/common.hxx> +#include <odb/relational/pgsql/context.hxx> + +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<enumerator&> (*unit.find (en))); + + ostringstream ostr; + + if (e.enum_ ().unsigned_ ()) + ostr << e.value (); + else + ostr << static_cast<long long> (e.value ()); + + return ostr.str (); + } + }; + entry<object_columns> 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 <set> + #include <odb/relational/schema.hxx> #include <odb/relational/pgsql/common.hxx> #include <odb/relational/pgsql/context.hxx> +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> member_drop_; - - struct class_drop: relational::class_drop, drop_common - { - class_drop (base const& x): base (x) {} - }; - entry<class_drop> class_drop_; + entry<drop_table> 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<string> tables_; // Set of tables we have already defined. + }; + entry<create_table> 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<semantics::node*> ("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> 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&> (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<enumerator&> (*unit.find (en))); - - if (e.enum_ ().unsigned_ ()) - os << " DEFAULT " << e.value (); - else - os << " DEFAULT " << static_cast<long long> (e.value ()); } virtual void - reference (semantics::data_member&) + deferred () { + os << endl + << " INITIALLY DEFERRED"; } }; - entry<object_columns> object_columns_; + entry<create_foreign_key> 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<sema_rel::table&> (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> class_create_; + // Add foreign keys. + // + instance<add_foreign_key> 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 <odb/gcc.hxx> - #include <cassert> #include <limits> #include <sstream> @@ -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_value> ("default"); - else if (t.count ("default")) - dv = &t.get<default_value> ("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<double>::infinity (); - else if (REAL_VALUE_ISNAN (d)) - v = numeric_limits<double>::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<schema_emitter> emitter; - instance<schema_file> file; - file->pre (); + file->prologue (); + + instance<schema_emitter> em; + emitter_ostream emos (*em); + + schema_format f (schema_format::sql); // Drop. // { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - instance<class_drop> c (*emitter); - unit >> unit_defines >> ns; - unit_defines >> c; + instance<drop_model> model (*em, emos, f); + trav_rel::names names; + instance<drop_table> table (*em, emos, f); + instance<drop_index> 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<class_create> c (*emitter); + instance<create_model> model (*em, emos, f); + trav_rel::names names; + instance<create_table> table (*em, emos, f); + instance<create_index> 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 <cassert> #include <odb/emitter.hxx> - #include <odb/relational/common.hxx> #include <odb/relational/context.hxx> @@ -19,11 +18,11 @@ namespace relational { namespace schema { - typedef std::set<std::string> 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<tables>& 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>& 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> tables_; // Seperate table for each pass. - instance<member_drop> 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<sema_rel::primary_key*> (&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<tables>& 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<object_columns> 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<object_columns> 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<object_columns> 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<object_columns> 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<object_columns> oc; - oc->traverse (m, *cvt, "value", "value"); - } - else - { - instance<object_columns> 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>& 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<create_column> c (format_, *this); + instance<create_primary_key> pk (format_, *this); + instance<create_foreign_key> 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<object_columns> 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> tables_; // Seperate table for each pass. - instance<member_create> 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<iterator> ("model-range-first")); + iterator end (c.get<iterator> ("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_value_member> init_id_value_member_; - schema_emitter schema_emitter_; - instance<schema::class_drop> schema_drop_; - instance<schema::class_create> schema_create_; + schema_emitter emitter_; + emitter_ostream stream_; + + trav_rel::names drop_names_; + instance<schema::drop_model> drop_model_; + instance<schema::drop_table> drop_table_; + instance<schema::drop_index> drop_index_; + + trav_rel::names create_names_; + instance<schema::create_model> create_model_; + instance<schema::create_table> create_table_; + instance<schema::create_index> 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<data*> (root_context::data_.get ())), + base_context (static_cast<data*> (root_context::data_.get ()), m), data_ (static_cast<data*> (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<string> 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<sql_type> (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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <sstream> + +#include <odb/relational/model.hxx> + +#include <odb/relational/sqlite/common.hxx> +#include <odb/relational/sqlite/context.hxx> + +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<enumerator&> (*unit.find (en))); + + ostringstream ostr; + + if (e.enum_ ().unsigned_ ()) + ostr << e.value (); + else + ostr << static_cast<long long> (e.value ()); + + return ostr.str (); + } + }; + entry<object_columns> 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<enumerator&> (*unit.find (en))); - - if (e.enum_ ().unsigned_ ()) - os << " DEFAULT " << e.value (); + if (options.sqlite_lax_auto_id ()) + os << " /*AUTOINCREMENT*/"; else - os << " DEFAULT " << static_cast<long long> (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> object_columns_; + entry<create_column> 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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/column.hxx> +#include <odb/semantics/relational/elements.hxx> +#include <odb/semantics/relational/foreign-key.hxx> +#include <odb/semantics/relational/index.hxx> +#include <odb/semantics/relational/key.hxx> +#include <odb/semantics/relational/model.hxx> +#include <odb/semantics/relational/primary-key.hxx> +#include <odb/semantics/relational/table.hxx> + +#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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/column.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/elements.hxx> +#include <odb/semantics/relational/table.hxx> + +namespace semantics +{ + namespace relational + { + class contains; + + class column: public nameable + { + typedef std::vector<contains*> 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<table_type&> (scope ()); + } + + // Key containment. + // + public: + typedef + pointer_iterator<contained_list::const_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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/elements.hxx> +#include <odb/semantics/relational/column.hxx> +#include <odb/semantics/relational/primary-key.hxx> + +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<column> ()) + i = names_.insert (first_key_, &e); + else + { + if (n.is_a<primary_key> ()) + 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 <boris@codesynthesis.com> +// 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 <map> +#include <list> +#include <vector> +#include <string> +#include <cassert> + +#include <cutl/container/graph.hxx> +#include <cutl/container/pointer-iterator.hxx> +#include <cutl/compiler/context.hxx> + +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 <typename X> + bool + is_a () const + { + return dynamic_cast<X const*> (this) != 0; + } + }; + + // + // + class node: public context + { + public: + virtual + ~node () {} + + // Return name of the node. + // + virtual string + kind () const = 0; + + public: + template <typename X> + bool + is_a () const + { + return dynamic_cast<X const*> (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*> names_list; + typedef std::map<string, names_list::iterator> names_map; + typedef std::map<names const*, names_list::iterator> names_iterator_map; + + public: + typedef pointer_iterator<names_list::iterator> names_iterator; + typedef + pointer_iterator<names_list::const_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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/foreign-key.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/elements.hxx> +#include <odb/semantics/relational/key.hxx> + +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<string> 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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/index.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/elements.hxx> +#include <odb/semantics/relational/key.hxx> +#include <odb/semantics/relational/table.hxx> + +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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/key.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/elements.hxx> +#include <odb/semantics/relational/column.hxx> + +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*> contains_list; + + public: + typedef + pointer_iterator<contains_list::const_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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/model.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/elements.hxx> + +namespace semantics +{ + namespace relational + { + class model: public graph<node, edge>, 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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/primary-key.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/elements.hxx> +#include <odb/semantics/relational/key.hxx> + +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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <cutl/compiler/type-info.hxx> + +#include <odb/semantics/relational/table.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/elements.hxx> + +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 <boris@codesynthesis.com> +// 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 <odb/traversal/relational/column.hxx> +#include <odb/traversal/relational/elements.hxx> +#include <odb/traversal/relational/foreign-key.hxx> +#include <odb/traversal/relational/index.hxx> +#include <odb/traversal/relational/key.hxx> +#include <odb/traversal/relational/model.hxx> +#include <odb/traversal/relational/primary-key.hxx> +#include <odb/traversal/relational/table.hxx> + +#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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/column.hxx> +#include <odb/traversal/relational/elements.hxx> + +namespace traversal +{ + namespace relational + { + struct column: node<semantics::relational::column> {}; + } +} + +#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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include <odb/traversal/relational/elements.hxx> + +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 <boris@codesynthesis.com> +// 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 <cutl/compiler/traversal.hxx> +#include <odb/semantics/relational/elements.hxx> + +namespace traversal +{ + namespace relational + { + using namespace cutl; + + // + // + typedef compiler::dispatcher<semantics::relational::node> node_dispatcher; + typedef compiler::dispatcher<semantics::relational::edge> 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 <typename X> + struct node: compiler::traverser_impl<X, semantics::relational::node>, + virtual node_base + { + }; + + template <typename X> + struct edge: compiler::traverser_impl<X, semantics::relational::edge>, + virtual edge_base + { + }; + + // + // Edges + // + + struct names: edge<semantics::relational::names> + { + names () + { + } + + names (node_dispatcher& n) + { + node_traverser (n); + } + + virtual void + traverse (type&); + }; + + // + // Nodes + // + + struct nameable: node<semantics::relational::nameable> {}; + + // + // + template <typename T> + struct scope_template: node<T> + { + 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<semantics::relational::scope> {}; + } +} + +#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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/foreign-key.hxx> +#include <odb/traversal/relational/key.hxx> + +namespace traversal +{ + namespace relational + { + struct foreign_key: key_template<semantics::relational::foreign_key> {}; + } +} + +#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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/index.hxx> +#include <odb/traversal/relational/key.hxx> + +namespace traversal +{ + namespace relational + { + struct index: key_template<semantics::relational::index> {}; + } +} + +#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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/key.hxx> +#include <odb/traversal/relational/elements.hxx> + +namespace traversal +{ + namespace relational + { + template <typename T> + struct key_template: node<T> + { + 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<semantics::relational::key> {}; + } +} + +#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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/model.hxx> +#include <odb/traversal/relational/elements.hxx> + +namespace traversal +{ + namespace relational + { + struct model: scope_template<semantics::relational::model> {}; + } +} + +#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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/primary-key.hxx> +#include <odb/traversal/relational/key.hxx> + +namespace traversal +{ + namespace relational + { + struct primary_key: key_template<semantics::relational::primary_key> {}; + } +} + +#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 <boris@codesynthesis.com> +// 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 <odb/semantics/relational/table.hxx> +#include <odb/traversal/relational/elements.hxx> + +namespace traversal +{ + namespace relational + { + struct table: scope_template<semantics::relational::table> {}; + struct object_table: scope_template<semantics::relational::table> {}; + struct container_table: scope_template<semantics::relational::table> {}; + } +} + +#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<context> ctx (create_context (cerr, u, ops)); + auto_ptr<context> ctx (create_context (cerr, u, ops, 0)); bool valid (true); |