aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--odb/context.cxx13
-rw-r--r--odb/context.hxx6
-rw-r--r--odb/generator.cxx34
-rw-r--r--odb/makefile167
-rw-r--r--odb/processor.cxx2
-rw-r--r--odb/relational/common.hxx16
-rw-r--r--odb/relational/context.cxx10
-rw-r--r--odb/relational/context.hxx13
-rw-r--r--odb/relational/generate.hxx10
-rw-r--r--odb/relational/model.cxx168
-rw-r--r--odb/relational/model.hxx528
-rw-r--r--odb/relational/mysql/context.cxx96
-rw-r--r--odb/relational/mysql/context.hxx20
-rw-r--r--odb/relational/mysql/model.cxx111
-rw-r--r--odb/relational/mysql/schema.cxx171
-rw-r--r--odb/relational/oracle/context.cxx134
-rw-r--r--odb/relational/oracle/context.hxx21
-rw-r--r--odb/relational/oracle/model.cxx74
-rw-r--r--odb/relational/oracle/schema.cxx306
-rw-r--r--odb/relational/pgsql/context.cxx103
-rw-r--r--odb/relational/pgsql/context.hxx20
-rw-r--r--odb/relational/pgsql/model.cxx71
-rw-r--r--odb/relational/pgsql/schema.cxx265
-rw-r--r--odb/relational/schema.cxx157
-rw-r--r--odb/relational/schema.hxx708
-rw-r--r--odb/relational/source.hxx86
-rw-r--r--odb/relational/sqlite/context.cxx66
-rw-r--r--odb/relational/sqlite/context.hxx20
-rw-r--r--odb/relational/sqlite/model.cxx58
-rw-r--r--odb/relational/sqlite/schema.cxx60
-rw-r--r--odb/semantics/relational.hxx18
-rw-r--r--odb/semantics/relational/column.cxx35
-rw-r--r--odb/semantics/relational/column.hxx118
-rw-r--r--odb/semantics/relational/elements.cxx137
-rw-r--r--odb/semantics/relational/elements.hxx277
-rw-r--r--odb/semantics/relational/foreign-key.cxx35
-rw-r--r--odb/semantics/relational/foreign-key.hxx87
-rw-r--r--odb/semantics/relational/index.cxx35
-rw-r--r--odb/semantics/relational/index.hxx44
-rw-r--r--odb/semantics/relational/key.cxx43
-rw-r--r--odb/semantics/relational/key.hxx100
-rw-r--r--odb/semantics/relational/model.cxx35
-rw-r--r--odb/semantics/relational/model.hxx39
-rw-r--r--odb/semantics/relational/primary-key.cxx35
-rw-r--r--odb/semantics/relational/primary-key.hxx45
-rw-r--r--odb/semantics/relational/table.cxx52
-rw-r--r--odb/semantics/relational/table.hxx56
-rw-r--r--odb/traversal/relational.hxx18
-rw-r--r--odb/traversal/relational/column.hxx20
-rw-r--r--odb/traversal/relational/elements.cxx18
-rw-r--r--odb/traversal/relational/elements.hxx147
-rw-r--r--odb/traversal/relational/foreign-key.hxx20
-rw-r--r--odb/traversal/relational/index.hxx20
-rw-r--r--odb/traversal/relational/key.hxx43
-rw-r--r--odb/traversal/relational/model.hxx20
-rw-r--r--odb/traversal/relational/primary-key.hxx20
-rw-r--r--odb/traversal/relational/table.hxx22
-rw-r--r--odb/validator.cxx2
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);