aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS8
-rw-r--r--doc/manual.xhtml70
-rw-r--r--odb/context.cxx2
-rw-r--r--odb/context.hxx9
-rw-r--r--odb/makefile3
-rw-r--r--odb/option-functions.cxx5
-rw-r--r--odb/option-types.hxx2
-rw-r--r--odb/options.cli16
-rw-r--r--odb/relational/changelog.cxx4
-rw-r--r--odb/relational/model.hxx10
-rw-r--r--odb/relational/mssql/schema.cxx439
-rw-r--r--odb/relational/mysql/schema.cxx429
-rw-r--r--odb/relational/oracle/schema.cxx187
-rw-r--r--odb/relational/pgsql/schema.cxx100
-rw-r--r--odb/relational/processor.cxx3
-rw-r--r--odb/relational/schema.cxx8
-rw-r--r--odb/relational/schema.hxx602
-rw-r--r--odb/relational/source.cxx2
-rw-r--r--odb/relational/sqlite/schema.cxx118
-rw-r--r--odb/semantics/relational/deferrable.cxx56
-rw-r--r--odb/semantics/relational/deferrable.hxx42
-rw-r--r--odb/semantics/relational/foreign-key.cxx8
-rw-r--r--odb/semantics/relational/foreign-key.hxx23
23 files changed, 1365 insertions, 781 deletions
diff --git a/NEWS b/NEWS
index c6b2db3..899267c 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,14 @@ Version 2.3.0
queries. For more information, refer to Section 4.1, "ODB Query Language"
in the ODB manual.
+ * New option, --fkeys-deferrable-mode, specifies the alternative deferrable
+ mode for foreign keys. By default, the ODB compiler generates deferred
+ foreign keys for databases that support them (SQLite, PostgreSQL, and
+ Oracle) and comments the foreign keys out for database that don't (MySQL
+ and SQL Server). This option can be used to override this behavior. Refer
+ to the ODB compiler command line interface documentation (man pages) for
+ details.
+
* New SQLite-specific exception, odb::sqlite::forced_rollback, which is
thrown if SQLite forces a transaction to roll back. For more information,
refer to Section 16.5.6, "Forced Rollback".
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
index e9471fd..dad3c90 100644
--- a/doc/manual.xhtml
+++ b/doc/manual.xhtml
@@ -16046,9 +16046,16 @@ namespace odb
committed. The only behaviors supported by MySQL are to either
check such constraints immediately (InnoDB engine) or to ignore
foreign key constraints altogether (all other engines). As a
- result, schemas generated by the ODB compiler for MySQL have
- foreign key definitions commented out. They are retained only
- for documentation.</p>
+ result, by default, schemas generated by the ODB compiler for
+ MySQL have foreign key definitions commented out. They are
+ retained only for documentation.</p>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
<h2><a name="15.6">15.6 MySQL Index Definitions</a></h2>
@@ -16909,14 +16916,14 @@ odb::database&amp; db = ...
}
</pre>
- <p>Finally, ODB relies on standard SQL behavior which requires
+ <p>Finally, ODB assumes the standard SQL behavior which requires
that foreign key constraints checking is deferred until the
transaction is committed. Default SQLite behavior is to check such
constraints immediately. As a result, when used with ODB, a custom
- database schema that defines foreign key constraints must declare
- such constraints as <code>DEFERRABLE INITIALLY DEFERRED</code>, as
- shown in the following example. Schemas generated by the ODB compiler
- meet this requirement automatically.</p>
+ database schema that defines foreign key constraints may need to
+ declare such constraints as <code>DEFERRABLE INITIALLY DEFERRED</code>,
+ as shown in the following example. By default, schemas generated by
+ the ODB compiler meet this requirement automatically.</p>
<pre class="sql">
CREATE TABLE Employee (
@@ -16925,6 +16932,12 @@ CREATE TABLE Employee (
DEFERRABLE INITIALLY DEFERRED);
</pre>
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
<h3><a name="16.5.4">16.5.4 Constraint Violations</a></h3>
@@ -17646,14 +17659,14 @@ namespace odb
<h3><a name="17.5.2">17.5.2 Foreign Key Constraints</a></h3>
- <p>ODB relies on standard SQL behavior which requires that
+ <p>ODB assumes the standard SQL behavior which requires that
foreign key constraints checking is deferred until the
transaction is committed. Default PostgreSQL behavior is
to check such constraints immediately. As a result, when
used with ODB, a custom database schema that defines foreign
- key constraints must declare such constraints as
+ key constraints may need to declare such constraints as
<code>INITIALLY DEFERRED</code>, as shown in the following example.
- Schemas generated by the ODB compiler meet this requirement
+ By default, schemas generated by the ODB compiler meet this requirement
automatically.</p>
<pre class="sql">
@@ -17662,6 +17675,13 @@ CREATE TABLE Employee (
employer BIGINT REFERENCES Employer(id) INITIALLY DEFERRED);
</pre>
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
<h3><a name="17.5.3">17.5.3 Unique Constraint Violations</a></h3>
<p>Due to the granularity of the PostgreSQL error codes, it is impossible
@@ -18533,14 +18553,14 @@ class long_class_name
<h3><a name="18.5.3">18.5.3 Foreign Key Constraints</a></h3>
- <p>ODB relies on standard SQL behavior which requires that
+ <p>ODB assumes the standard SQL behavior which requires that
foreign key constraints checking is deferred until the
transaction is committed. Default Oracle behavior is
to check such constraints immediately. As a result, when
used with ODB, a custom database schema that defines foreign
- key constraints must declare such constraints as
+ key constraints may need to declare such constraints as
<code>INITIALLY DEFERRED</code>, as shown in the following example.
- Schemas generated by the ODB compiler meet this requirement
+ By default, schemas generated by the ODB compiler meet this requirement
automatically.</p>
<pre class="sql">
@@ -18550,6 +18570,13 @@ CREATE TABLE Employee (
DEFERRABLE INITIALLY DEFERRED);
</pre>
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
<h3><a name="18.5.4">18.5.4 Unique Constraint Violations</a></h3>
<p>Due to the granularity of the Oracle error codes, it is impossible
@@ -19667,12 +19694,19 @@ namespace odb
<h3><a name="19.5.2">19.5.2 Foreign Key Constraints</a></h3>
- <p>ODB relies on standard SQL behavior which requires that foreign
+ <p>ODB assumes the standard SQL behavior which requires that foreign
key constraints checking is deferred until the transaction is
committed. The only behavior supported by SQL Server is to check
- such constraints immediately. As a result, schemas generated by
- the ODB compiler for SQL Server have foreign key definitions
- commented out. They are retained only for documentation.</p>
+ such constraints immediately. As a result, by default, schemas
+ generated by the ODB compiler for SQL Server have foreign key
+ definitions commented out. They are retained only for documentation.</p>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
<h3><a name="19.5.3">19.5.3 Unique Constraint Violations</a></h3>
diff --git a/odb/context.cxx b/odb/context.cxx
index 9b8cb3c..760d653 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -442,6 +442,7 @@ context (ostream& os_,
options (ops),
features (f),
db (options.database ()[0]),
+ in_comment (data_->in_comment_),
exp (data_->exp_),
ext (data_->ext_),
keyword_set (data_->keyword_set_),
@@ -546,6 +547,7 @@ context ()
options (current ().options),
features (current ().features),
db (current ().db),
+ in_comment (current ().in_comment),
exp (current ().exp),
ext (current ().ext),
keyword_set (current ().keyword_set),
diff --git a/odb/context.hxx b/odb/context.hxx
index d148a97..c06a9c4 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -1050,7 +1050,10 @@ protected:
~data () {}
data (std::ostream& os)
- : os_ (os.rdbuf ()), top_object_ (0), cur_object_ (0),
+ : os_ (os.rdbuf ()),
+ in_comment_ (false),
+ top_object_ (0),
+ cur_object_ (0),
sql_name_upper_ ("(.+)", "\\U$1"),
sql_name_lower_ ("(.+)", "\\L$1")
{
@@ -1060,6 +1063,8 @@ protected:
std::ostream os_;
std::stack<std::streambuf*> os_stack_;
+ bool in_comment_;
+
semantics::class_* top_object_;
semantics::class_* cur_object_;
@@ -1090,6 +1095,8 @@ public:
features_type& features;
database const db;
+ bool& in_comment;
+
string& exp; // Export symbol (with trailing space if specified).
string& ext; // Extern symbol.
diff --git a/odb/makefile b/odb/makefile
index 104bc4a..02be483 100644
--- a/odb/makefile
+++ b/odb/makefile
@@ -156,7 +156,8 @@ cxx_ctun := \
option-types.cxx \
option-functions.cxx \
profile.cxx \
-semantics/relational/name.cxx
+semantics/relational/name.cxx \
+semantics/relational/deferrable.cxx
# Options file.
#
diff --git a/odb/option-functions.cxx b/odb/option-functions.cxx
index 016b729..61db175 100644
--- a/odb/option-functions.cxx
+++ b/odb/option-functions.cxx
@@ -53,6 +53,11 @@ process_options (options& o)
if (o.schema_name ().count (db) == 0)
o.schema_name ()[db] = "";
+ // Set default --fkeys-deferrable-mode value.
+ //
+ if (o.fkeys_deferrable_mode ().count (db) == 0)
+ o.fkeys_deferrable_mode ()[db] = deferrable::deferred;
+
// Set default --{export,extern}-symbol values.
//
if (o.export_symbol ().count (db) == 0)
diff --git a/odb/option-types.hxx b/odb/option-types.hxx
index 9a0cad5..2f8825d 100644
--- a/odb/option-types.hxx
+++ b/odb/option-types.hxx
@@ -12,8 +12,10 @@
#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/deferrable.hxx>
using semantics::relational::qname;
+using semantics::relational::deferrable;
struct cxx_version
{
diff --git a/odb/options.cli b/odb/options.cli
index 457c06f..569566d 100644
--- a/odb/options.cli
+++ b/odb/options.cli
@@ -169,6 +169,22 @@ class options
schema name, is used."
};
+ database_map<deferrable> --fkeys-deferrable-mode
+ {
+ "<m>",
+ "Use constraint checking mode <m> in foreign keys generated for object
+ relationships. Valid values for this option are \cb{not_deferrable},
+ \cb{immediate}, and \cb{deferred} (default). MySQL and SQL Server do
+ not support deferrable foreign keys and for these databases such keys
+ are generated commented out. Other foreign keys generated by the ODB
+ compiler (such as the ones used to support containers and polymorphic
+ hierarchies) are always generated as not deferrable.
+
+ Note also that if you use either \cb{not_deferrable} or \cb{immediate}
+ mode, then the order in which you persist, update, and erase objects
+ within a transaction becomes important."
+ };
+
std::string --default-pointer = "*"
{
"<ptr>",
diff --git a/odb/relational/changelog.cxx b/odb/relational/changelog.cxx
index f576c79..af6371a 100644
--- a/odb/relational/changelog.cxx
+++ b/odb/relational/changelog.cxx
@@ -145,8 +145,8 @@ namespace relational
{
if (foreign_key* ofk = other.find<foreign_key> (fk.name ()))
{
- if (fk.deferred () != ofk->deferred ())
- diagnose_foreign_key (fk, "deferred kind");
+ if (fk.deferrable () != ofk->deferrable ())
+ diagnose_foreign_key (fk, "deferrable mode");
if (fk.on_delete () != ofk->on_delete ())
diagnose_foreign_key (fk, "on delete action");
diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx
index 4c4bba6..44f01c9 100644
--- a/odb/relational/model.hxx
+++ b/odb/relational/model.hxx
@@ -228,14 +228,16 @@ namespace relational
string id (id_prefix_ +
(key_prefix_.empty () ? m.name () : key_prefix_));
- bool deferred (m.get<bool> ("deferred", true));
+ deferrable def (
+ m.get<deferrable> ("deferrable",
+ options.fkeys_deferrable_mode ()[db]));
+
foreign_key::action_type on_delete (
m.get<foreign_key::action_type> (
"on-delete", foreign_key::no_action));
foreign_key& fk (
- model_.new_node<foreign_key> (
- id, table_name (c), deferred, on_delete));
+ model_.new_node<foreign_key> (id, table_name (c), def, on_delete));
fk.set ("cxx-location", m.location ());
@@ -522,7 +524,7 @@ namespace relational
model_.new_node<sema_rel::foreign_key> (
id + ".id",
table_name (*context::top_object),
- false, // immediate
+ sema_rel::deferrable::not_deferrable,
sema_rel::foreign_key::cascade));
fk.set ("cxx-location", m.location ());
model_.new_edge<sema_rel::unames> (
diff --git a/odb/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx
index d1d892a..61e7e68 100644
--- a/odb/relational/mssql/schema.cxx
+++ b/odb/relational/mssql/schema.cxx
@@ -2,8 +2,6 @@
// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
-#include <set>
-
#include <odb/relational/schema.hxx>
#include <odb/relational/mssql/common.hxx>
@@ -18,6 +16,7 @@ namespace relational
namespace schema
{
namespace relational = relational::schema;
+ using relational::table_set;
struct sql_emitter: relational::sql_emitter
{
@@ -58,89 +57,98 @@ namespace relational
};
entry<drop_column> drop_column_;
- struct drop_table: relational::drop_table, context
+ struct drop_foreign_key: relational::drop_foreign_key, context
{
- drop_table (base const& x): base (x) {}
+ drop_foreign_key (base const& x): base (x) {}
virtual void
- traverse (sema_rel::table&, bool);
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ bool migration (dropped_ == 0);
- private:
- friend class drop_foreign_key;
- set<qname> tables_; // Set of tables we would have already dropped.
- };
- entry<drop_table> drop_table_;
+ if (migration)
+ {
+ if (fk.not_deferrable ())
+ pre_statement ();
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
- struct drop_foreign_key: trav_rel::foreign_key, relational::common
- {
- drop_foreign_key (drop_table& dt, bool m)
- : common (dt.emitter (), dt.stream ()), dt_ (dt), migration_ (m)
- {
+ os << "/*" << endl;
+ }
+ }
+ else
+ {
+ // Here we drop potentially deferrable keys and also need to
+ // test if the key exists.
+ //
+ pre_statement ();
+
+ os << "IF OBJECT_ID(" << quote_string (fk.name ()) << ", " <<
+ quote_string ("F") << ") IS NOT NULL" << endl
+ << " ";
+ }
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << (migration ? " " : " ") << "DROP CONSTRAINT " <<
+ quote_id (fk.name ()) << endl;
+
+
+ if (!migration || fk.not_deferrable ())
+ post_statement ();
+ else
+ os << "*/" << endl
+ << endl;
}
virtual void
- traverse (sema_rel::foreign_key& fk)
+ traverse (sema_rel::drop_foreign_key& dfk)
{
- // Deferred constraints are not supported by SQL Server.
+ // Find the foreign key we are dropping in the base model.
//
- if (fk.deferred ())
- return;
+ sema_rel::foreign_key& fk (find<sema_rel::foreign_key> (dfk));
- // If the table which we reference is droped before us, then
- // we need to drop the constraint first. Similarly, if the
- // referenced table is not part if this model, then assume
- // it is dropped before us. In migration we always do this
- // first.
- //
- sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
+ bool c (!fk.not_deferrable () && !in_comment);
- if (!migration_)
- {
- sema_rel::qname const& rt (fk.referenced_table ());
- sema_rel::model& m (dynamic_cast<sema_rel::model&> (t.scope ()));
+ if (c && format_ != schema_format::sql)
+ return;
- if (dt_.tables_.find (rt) == dt_.tables_.end () &&
- m.find (rt) != m.names_end ())
- return;
- }
+ if (!first_)
+ os << (c ? "" : ",") << endl
+ << " ";
- pre_statement ();
+ if (c)
+ os << "/* ";
- if (!migration_)
- os << "IF OBJECT_ID(" << quote_string (fk.name ()) << ", " <<
- quote_string ("F") << ") IS NOT NULL" << endl
- << " ";
+ os << quote_id (fk.name ());
- os << "ALTER TABLE " << quote_id (t.name ()) << " DROP" << endl
- << (!migration_ ? " " : "") << " CONSTRAINT " <<
- quote_id (fk.name ()) << endl;
+ if (c)
+ os << " */";
- post_statement ();
+ if (first_)
+ {
+ if (c)
+ // There has to be a real name otherwise the whole statement
+ // would have been commented out.
+ //
+ os << endl
+ << " ";
+ else
+ first_ = false;
+ }
}
-
- private:
- drop_table& dt_;
- bool migration_;
};
+ entry<drop_foreign_key> drop_foreign_key_;
- void drop_table::
- traverse (sema_rel::table& t, bool migration)
+ struct drop_table: relational::drop_table, context
{
- qname const& table (t.name ());
+ drop_table (base const& x): base (x) {}
- if (pass_ == 1)
- {
- // Drop constraints. In migration this is always done on pass 1.
- //
- if (!migration)
- tables_.insert (table); // Add it before to cover self-refs.
- drop_foreign_key fk (*this, migration);
- trav_rel::unames n (fk);
- names (t, n);
- }
- else if (pass_ == 2)
+ virtual void
+ drop (sema_rel::qname const& table, bool migration)
{
- // SQL Server has no IF EXISTS conditional for dropping table.
+ // SQL Server has no IF EXISTS conditional for dropping tables.
// The following approach appears to be the recommended way to
// drop a table if it exists.
//
@@ -155,7 +163,8 @@ namespace relational
post_statement ();
}
- }
+ };
+ entry<drop_table> drop_table_;
//
// Create.
@@ -185,141 +194,107 @@ namespace relational
};
entry<create_column> create_column_;
- struct create_foreign_key;
-
- struct create_table: relational::create_table, context
- {
- create_table (base const& x): base (x) {}
-
- void
- traverse (sema_rel::table&);
-
- private:
- friend class create_foreign_key;
- set<qname> tables_; // Set of tables we have already defined.
- };
- entry<create_table> create_table_;
-
struct create_foreign_key: relational::create_foreign_key, context
{
- create_foreign_key (schema_format f, relational::create_table& ct)
- : base (f, ct)
- {
- }
-
create_foreign_key (base const& x): base (x) {}
virtual void
- traverse (sema_rel::foreign_key& fk)
+ generate (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).
+ // SQL Server does not support deferrable constraint checking.
+ // Output such foreign keys as comments, for documentation,
+ // unless we are generating embedded schema.
//
- create_table& ct (static_cast<create_table&> (create_table_));
-
- if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ())
+ if (fk.not_deferrable ())
+ base::generate (fk);
+ else
{
- // SQL Server does not support deferred constraint checking.
- // Output such foreign keys as comments, for documentation,
- // unless we are generating embedded schema.
+ // Don't bloat C++ code with comment strings if we are
+ // generating embedded schema.
//
- if (fk.deferred ())
- {
- // Don't bloat C++ code with comment strings if we are
- // generating embedded schema.
- //
- if (format_ != schema_format::embedded)
- {
- os << endl
- << endl
- << " /*" << endl;
+ if (format_ != schema_format::sql)
+ return;
- base::create (fk);
+ os << endl
+ << " /*" << endl
+ << " CONSTRAINT ";
+ create (fk);
+ os << endl
+ << " */";
+ }
+ }
- os << endl
- << " */";
- }
- }
- else
- base::traverse (fk);
+ virtual void
+ traverse (sema_rel::add_foreign_key& afk)
+ {
+ bool c (!afk.not_deferrable () && !in_comment);
- fk.set ("mssql-fk-defined", true); // Mark it as defined.
+ if (c && format_ != schema_format::sql)
+ return;
+
+ if (!first_)
+ os << (c ? "" : ",") << endl
+ << " ";
+
+ if (c)
+ os << "/*" << endl
+ << " ";
+
+ os << "CONSTRAINT ";
+ create (afk);
+
+ if (c)
+ os << endl
+ << " */";
+
+ if (first_)
+ {
+ if (c)
+ // There has to be a real key otherwise the whole statement
+ // would have been commented out.
+ //
+ os << endl
+ << " ";
+ else
+ first_ = false;
}
}
virtual void
- deferred ()
+ deferrable (sema_rel::deferrable)
{
- // SQL Server doesn't support deferred.
+ // This will still be called to output the comment.
}
};
entry<create_foreign_key> create_foreign_key_;
- struct add_foreign_key: create_foreign_key, relational::common
+ struct add_foreign_key: relational::add_foreign_key, context
{
- add_foreign_key (schema_format f, relational::create_table& ct)
- : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ())
- {
- }
+ add_foreign_key (base const& x): base (x) {}
virtual void
- traverse (sema_rel::foreign_key& fk)
+ generate (sema_rel::table& t, sema_rel::foreign_key& fk)
{
- if (!fk.count ("mssql-fk-defined"))
+ // SQL Server has no deferrable constraints.
+ //
+ if (fk.not_deferrable ())
+ base::generate (t, fk);
+ else
{
- sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
-
- // SQL Server has no deferred constraints.
- //
- if (fk.deferred ())
- {
- if (format_ != schema_format::embedded)
- {
- os << "/*" << endl;
-
- os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
- base::create (fk);
-
- os << endl
- << "*/" << endl
- << endl;
- }
- }
- else
- {
- pre_statement ();
-
- os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
- base::create (fk);
- os << endl;
+ if (format_ != schema_format::sql)
+ return;
- post_statement ();
- }
+ os << "/*" << endl;
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ADD CONSTRAINT ";
+ def_->create (fk);
+ os << endl
+ << "*/" << endl
+ << endl;
}
}
};
-
- void create_table::
- traverse (sema_rel::table& t)
- {
- if (pass_ == 1)
- {
- // In migration we always add foreign keys on pass 2.
- //
- if (!t.is_a<sema_rel::add_table> ())
- tables_.insert (t.name ()); // Add it before to cover self-refs.
- base::traverse (t);
- return;
- }
-
- // Add foreign keys.
- //
- add_foreign_key fk (format_, *this);
- trav_rel::unames n (fk);
- names (t, n);
- }
+ entry<add_foreign_key> add_foreign_key_;
struct drop_index: relational::drop_index, context
{
@@ -367,22 +342,76 @@ namespace relational
{
alter_table_pre (base const& x): base (x) {}
+ // Check if we are only dropping deferrable foreign keys.
+ //
+ bool
+ check_drop_deferrable_only (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ if (drop_foreign_key* dfk =
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()))
+ {
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ if (fk.not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
virtual void
alter (sema_rel::alter_table& at)
{
// SQL Server can only alter one kind of thing at a time.
//
+ if (check<sema_rel::drop_foreign_key> (at))
+ {
+ bool deferrable (check_drop_deferrable_only (at));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ alter_header (at.name ());
+ os << endl
+ << " DROP CONSTRAINT ";
+ instance<drop_foreign_key> dfc (*this);
+ trav_rel::unames n (*dfc);
+ names (at, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+
if (check<sema_rel::add_column> (at))
{
pre_statement ();
alter_header (at.name ());
- os << " ADD ";
+ os << endl
+ << " ADD ";
- instance<create_column> cc (emitter (), stream (), format_);
- trav_rel::alter_column ac; // Override.
- trav_rel::unames n;
- n >> cc;
- n >> ac;
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
names (at, n);
os << endl;
@@ -393,7 +422,7 @@ namespace relational
//
{
bool tl (true); // (Im)perfect forwarding.
- instance<alter_column> ac (emitter (), stream (), format_, tl);
+ instance<alter_column> ac (*this, tl);
trav_rel::unames n (*ac);
names (at, n);
}
@@ -405,6 +434,26 @@ namespace relational
{
alter_table_post (base const& x): base (x) {}
+ // Check if we are only adding deferrable foreign keys.
+ //
+ bool
+ check_add_deferrable_only (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_foreign_key;
+
+ if (add_foreign_key* afk =
+ dynamic_cast<add_foreign_key*> (&i->nameable ()))
+ {
+ if (afk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
virtual void
alter (sema_rel::alter_table& at)
{
@@ -414,9 +463,10 @@ namespace relational
{
pre_statement ();
alter_header (at.name ());
- os << " DROP COLUMN ";
+ os << endl
+ << " DROP COLUMN ";
- instance<drop_column> dc (emitter (), stream (), format_);
+ instance<drop_column> dc (*this);
trav_rel::unames n (*dc);
names (at, n);
os << endl;
@@ -428,10 +478,43 @@ namespace relational
//
{
bool fl (false); // (Im)perfect forwarding.
- instance<alter_column> ac (emitter (), stream (), format_, fl);
+ instance<alter_column> ac (*this, fl);
trav_rel::unames n (*ac);
names (at, n);
}
+
+ if (check<sema_rel::add_foreign_key> (at))
+ {
+ bool deferrable (check_add_deferrable_only (at));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ alter_header (at.name ());
+ os << endl
+ << " ADD ";
+ instance<create_foreign_key> cfc (*this);
+ trav_rel::unames n (*cfc);
+ names (at, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
}
};
entry<alter_table_post> alter_table_post_;
diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx
index 2ae47ae..be058ce 100644
--- a/odb/relational/mysql/schema.cxx
+++ b/odb/relational/mysql/schema.cxx
@@ -2,8 +2,6 @@
// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
-#include <set>
-
#include <odb/relational/schema.hxx>
#include <odb/relational/mysql/common.hxx>
@@ -18,58 +16,18 @@ namespace relational
namespace schema
{
namespace relational = relational::schema;
+ using relational::table_set;
//
// Drop.
//
- struct drop_table: relational::drop_table, context
+ struct drop_foreign_key: relational::drop_foreign_key, context
{
- drop_table (base const& x): base (x) {}
+ drop_foreign_key (base const& x): base (x) {}
virtual void
- traverse (sema_rel::table&, bool migration);
-
- private:
- friend class drop_foreign_key;
- set<qname> tables_; // Set of tables we would have already dropped.
- };
- entry<drop_table> drop_table_;
-
- struct drop_foreign_key: trav_rel::foreign_key, relational::common
- {
- drop_foreign_key (drop_table& dt, bool m)
- : common (dt.emitter (), dt.stream ()), dt_ (dt), migration_ (m)
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
{
- }
-
- virtual void
- traverse (sema_rel::foreign_key& fk)
- {
- // Deferred constraints are not supported by MySQL.
- //
- if (fk.deferred ())
- return;
-
- // If the table which we reference is droped before us, then
- // we need to drop the constraint first. Similarly, if the
- // referenced table is not part if this model, then assume
- // it is dropped before us. In migration we always do this
- // first.
- //
- sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
-
- if (!migration_)
- {
- sema_rel::qname const& rt (fk.referenced_table ());
- sema_rel::model& m (dynamic_cast<sema_rel::model&> (t.scope ()));
-
- if (dt_.tables_.find (rt) == dt_.tables_.end () &&
- m.find (rt) != m.names_end ())
- return;
- }
-
- pre_statement ();
-
/*
// @@ This does not work: in MySQL control statements can only
// be used in stored procedures. It seems the only way to
@@ -91,48 +49,65 @@ namespace relational
<< "END IF;" << endl;
*/
- os << "ALTER TABLE " << quote_id (t.name ()) << " DROP FOREIGN " <<
- "KEY " << quote_id (fk.name ()) << endl;
+ // So for now we only do this in migration.
+ //
+ if (dropped_ == 0)
+ {
+ if (fk.not_deferrable ())
+ pre_statement ();
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
- post_statement ();
- }
+ os << "/*" << endl;
+ }
- private:
- drop_table& dt_;
- bool migration_;
- };
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " DROP FOREIGN KEY " << quote_id (fk.name ()) << endl;
- void drop_table::
- traverse (sema_rel::table& t, bool migration)
- {
- // Only enabled for migration support for now (see above).
- //
- if (!migration)
- {
- base::traverse (t, migration);
- return;
+ if (fk.not_deferrable ())
+ post_statement ();
+ else
+ os << "*/" << endl
+ << endl;
+ }
}
- qname const& table (t.name ());
+ using base::drop;
- if (pass_ == 1)
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
{
- // Drop constraints. In migration this is always done on pass 1.
+ // Find the foreign key we are dropping in the base model.
//
- if (!migration)
- tables_.insert (table); // Add it before to cover self-refs.
- drop_foreign_key fk (*this, migration);
- trav_rel::unames n (fk);
- names (t, n);
+ sema_rel::foreign_key& fk (find<sema_rel::foreign_key> (dfk));
+
+ if (fk.not_deferrable () || in_comment)
+ base::traverse (dfk);
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*"
+ << endl;
+
+ drop (dfk);
+
+ os << endl
+ << " */";
+ }
}
- else if (pass_ == 2)
+
+ virtual void
+ drop_header ()
{
- pre_statement ();
- os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
- quote_id (table) << endl;
- post_statement ();
+ os << "DROP FOREIGN KEY ";
}
- }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
//
// Create.
@@ -150,155 +125,112 @@ namespace relational
};
entry<create_column> create_column_;
- struct create_foreign_key;
-
- struct create_table: relational::create_table, context
+ struct create_foreign_key: relational::create_foreign_key, context
{
- create_table (base const& x): base (x) {}
-
- void
- traverse (sema_rel::table&);
+ create_foreign_key (base const& x): base (x) {}
virtual void
- create_post ()
+ generate (sema_rel::foreign_key& fk)
{
- os << ")";
-
- string const& engine (options.mysql_engine ());
+ // MySQL does not support deferrable constraint checking. Output
+ // such foreign keys as comments, for documentation, unless we
+ // are generating embedded schema.
+ //
+ if (fk.not_deferrable ())
+ base::generate (fk);
+ else
+ {
+ // Don't bloat C++ code with comment strings if we are
+ // generating embedded schema.
+ //
+ if (format_ != schema_format::sql)
+ return;
- if (engine != "default")
os << endl
- << " ENGINE=" << engine;
-
- os << endl;
- }
-
- private:
- friend class create_foreign_key;
- set<qname> tables_; // Set of tables we have already defined.
- };
- entry<create_table> create_table_;
-
- struct create_foreign_key: relational::create_foreign_key, context
- {
- create_foreign_key (schema_format f, relational::create_table& ct)
- : base (f, ct)
- {
+ << " /*" << endl
+ << " CONSTRAINT ";
+ base::create (fk);
+ os << endl
+ << " */";
+ }
}
- create_foreign_key (base const& x): base (x) {}
-
virtual void
- traverse (sema_rel::foreign_key& fk)
+ traverse (sema_rel::add_foreign_key& afk)
{
- // 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_));
-
- if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ())
+ if (afk.not_deferrable () || in_comment)
+ base::traverse (afk);
+ else
{
- // MySQL does not support deferred constraint checking. Output
- // such foreign keys as comments, for documentation, unless we
- // are generating embedded schema.
- //
- if (fk.deferred ())
- {
- // Don't bloat C++ code with comment strings if we are
- // generating embedded schema.
- //
- if (format_ != schema_format::embedded)
- {
- os << endl
- << endl
- << " /*" << endl;
-
- base::create (fk);
-
- os << endl
- << " */";
- }
- }
- else
- base::traverse (fk);
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*"
+ << endl;
+
+ add (afk);
- fk.set ("mysql-fk-defined", true); // Mark it as defined.
+ os << endl
+ << " */";
}
}
virtual void
- deferred ()
+ deferrable (sema_rel::deferrable)
{
- // MySQL doesn't support deferred.
+ // This will still be called to output the comment.
}
};
entry<create_foreign_key> create_foreign_key_;
- struct add_foreign_key: create_foreign_key, relational::common
+ struct add_foreign_key: relational::add_foreign_key, context
{
- add_foreign_key (schema_format f, relational::create_table& ct)
- : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ())
- {
- }
+ add_foreign_key (base const& x): base (x) {}
virtual void
- traverse (sema_rel::foreign_key& fk)
+ generate (sema_rel::table& t, sema_rel::foreign_key& fk)
{
- if (!fk.count ("mysql-fk-defined"))
+ // MySQL has no deferrable constraints.
+ //
+ if (fk.not_deferrable ())
+ base::generate (t, fk);
+ else
{
- sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
-
- // MySQL has no deferred constraints.
- //
- if (fk.deferred ())
- {
- if (format_ != schema_format::embedded)
- {
- os << "/*" << endl;
-
- os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
- base::create (fk);
-
- os << endl
- << "*/" << endl
- << endl;
- }
- }
- else
- {
- pre_statement ();
-
- os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
- base::create (fk);
- os << endl;
+ if (format_ != schema_format::sql)
+ return;
- post_statement ();
- }
+ os << "/*" << endl;
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ADD CONSTRAINT ";
+ def_->create (fk);
+ os << endl
+ << "*/" << endl
+ << endl;
}
}
};
+ entry<add_foreign_key> add_foreign_key_;
- void create_table::
- traverse (sema_rel::table& t)
+ struct create_table: relational::create_table, context
{
- if (pass_ == 1)
+ create_table (base const& x): base (x) {}
+
+ virtual void
+ create_post ()
{
- // In migration we always add foreign keys on pass 2.
- //
- if (!t.is_a<sema_rel::add_table> ())
- tables_.insert (t.name ()); // Add it before to cover self-refs.
- base::traverse (t);
- return;
- }
+ os << ")";
- // Add foreign keys.
- //
- add_foreign_key fk (format_, *this);
- trav_rel::unames n (fk);
- names (t, n);
- }
+ string const& engine (options.mysql_engine ());
+
+ if (engine != "default")
+ os << endl
+ << " ENGINE=" << engine;
+
+ os << endl;
+ }
+ };
+ entry<create_table> create_table_;
struct create_index: relational::create_index, context
{
@@ -356,6 +288,119 @@ namespace relational
}
};
entry<alter_column> alter_column_;
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ // Check if we are only dropping deferrable foreign keys.
+ //
+ bool
+ check_drop_deferrable_only (sema_rel::alter_table& at)
+ {
+ if (check<sema_rel::add_column> (at) ||
+ check_alter_column_null (at, true))
+ return false;
+
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ if (drop_foreign_key* dfk =
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()))
+ {
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ if (fk.not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ if (check_drop_deferrable_only (at))
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ in_comment = true;
+
+ alter_header (at.name ());
+ instance<drop_foreign_key> dfk (*this);
+ trav_rel::unames n (*dfk);
+ names (at, n);
+ os << endl;
+
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ base::alter (at);
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ // Check if we are only adding deferrable foreign keys.
+ //
+ bool
+ check_add_deferrable_only (sema_rel::alter_table& at)
+ {
+ if (check<sema_rel::drop_column> (at) ||
+ check_alter_column_null (at, false))
+ return false;
+
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_foreign_key;
+
+ if (add_foreign_key* afk =
+ dynamic_cast<add_foreign_key*> (&i->nameable ()))
+ {
+ if (afk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ if (check_add_deferrable_only (at))
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ in_comment = true;
+
+ alter_header (at.name ());
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (at, n);
+ os << endl;
+
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ base::alter (at);
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
}
}
}
diff --git a/odb/relational/oracle/schema.cxx b/odb/relational/oracle/schema.cxx
index 9dc1921..298280e 100644
--- a/odb/relational/oracle/schema.cxx
+++ b/odb/relational/oracle/schema.cxx
@@ -2,8 +2,6 @@
// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
-#include <set>
-
#include <odb/relational/schema.hxx>
#include <odb/relational/oracle/common.hxx>
@@ -18,6 +16,7 @@ namespace relational
namespace schema
{
namespace relational = relational::schema;
+ using relational::table_set;
struct sql_emitter: relational::sql_emitter
{
@@ -106,6 +105,19 @@ namespace relational
};
entry<drop_column> drop_column_;
+ struct drop_foreign_key: relational::drop_foreign_key, context
+ {
+ drop_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ os << endl;
+ drop (dfk);
+ }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
+
struct drop_table: relational::drop_table, context
{
drop_table (base const& x): base (x) {}
@@ -113,6 +125,8 @@ namespace relational
virtual void
traverse (sema_rel::table& t, bool migration)
{
+ // For Oracle we use the CASCADE clause to drop foreign keys.
+ //
if (pass_ != 2)
return;
@@ -203,112 +217,52 @@ namespace relational
};
entry<create_column> create_column_;
- struct create_foreign_key;
-
- struct create_table: relational::create_table, context
- {
- create_table (base const& x): base (x) {}
-
- void
- traverse (sema_rel::table&);
-
- private:
- friend class create_foreign_key;
- set<qname> tables_; // Set of tables we have already defined.
- };
- entry<create_table> create_table_;
-
struct create_foreign_key: relational::create_foreign_key, context
{
- create_foreign_key (schema_format f, relational::create_table& ct)
- : base (f, ct)
- {
- }
-
create_foreign_key (base const& x): base (x) {}
virtual void
- traverse (sema_rel::foreign_key& fk)
+ traverse (sema_rel::add_foreign_key& afk)
{
- // 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_));
-
- if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ())
- {
- base::traverse (fk);
- fk.set ("oracle-fk-defined", true); // Mark it as defined.
- }
+ os << endl
+ << " ADD CONSTRAINT ";
+ create (afk);
}
};
entry<create_foreign_key> create_foreign_key_;
- struct add_foreign_key: create_foreign_key, relational::common
+ struct create_table: relational::create_table, context
{
- add_foreign_key (schema_format f, relational::create_table& ct)
- : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ())
- {
- }
-
- virtual void
- traverse (sema_rel::foreign_key& fk)
- {
- if (!fk.count ("oracle-fk-defined"))
- {
- sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
-
- pre_statement ();
-
- os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
- base::create (fk);
- os << endl;
-
- post_statement ();
- }
- }
- };
+ create_table (base const& x): base (x) {}
- void create_table::
- traverse (sema_rel::table& t)
- {
- if (pass_ == 1)
+ void
+ traverse (sema_rel::table& t)
{
- // In migration we always add foreign keys on pass 2.
- //
- if (!t.is_a<sema_rel::add_table> ())
- tables_.insert (t.name ()); // Add it before to cover self-refs.
base::traverse (t);
- // Create the sequence if we have auto primary key.
- //
- using sema_rel::primary_key;
+ if (pass_ == 1)
+ {
+ // Create the sequence if we have auto primary key.
+ //
+ using sema_rel::primary_key;
- sema_rel::table::names_iterator i (t.find ("")); // Special name.
- primary_key* pk (i != t.names_end ()
- ? &dynamic_cast<primary_key&> (i->nameable ())
- : 0);
+ sema_rel::table::names_iterator i (t.find ("")); // Special name.
+ primary_key* pk (i != t.names_end ()
+ ? &dynamic_cast<primary_key&> (i->nameable ())
+ : 0);
- if (pk != 0 && pk->auto_ ())
- {
- pre_statement ();
- os_ << "CREATE SEQUENCE " <<
- quote_id (sequence_name (t.name ())) << endl
- << " START WITH 1 INCREMENT BY 1" << endl;
- post_statement ();
+ if (pk != 0 && pk->auto_ ())
+ {
+ pre_statement ();
+ os_ << "CREATE SEQUENCE " <<
+ quote_id (sequence_name (t.name ())) << endl
+ << " START WITH 1 INCREMENT BY 1" << endl;
+ post_statement ();
+ }
}
-
- return;
}
-
- // Add foreign keys.
- //
- add_foreign_key fk (format_, *this);
- trav_rel::unames n (fk);
- names (t, n);
- }
+ };
+ entry<create_table> create_table_;
struct create_index: relational::create_index, context
{
@@ -377,31 +331,43 @@ namespace relational
// Oracle can only alter certain kinds of things together but
// grouped one at a time.
//
+ if (check<sema_rel::drop_foreign_key> (at))
+ {
+ pre_statement ();
+ alter_header (at.name ());
+
+ instance<drop_foreign_key> dfc (*this);
+ trav_rel::unames n (*dfc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
if (check<sema_rel::add_column> (at))
{
pre_statement ();
alter_header (at.name ());
- os << " ADD (";
+ os << endl
+ << " ADD (";
- instance<create_column> cc (emitter (), stream (), format_);
- trav_rel::alter_column ac; // Override.
- trav_rel::unames n;
- n >> cc;
- n >> ac;
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
names (at, n);
os << ")" << endl;
post_statement ();
}
- if (check_alter_null (at, true))
+ if (check_alter_column_null (at, true))
{
pre_statement ();
alter_header (at.name ());
- os << " MODIFY (";
+ os << endl
+ << " MODIFY (";
bool tl (true); // (Im)perfect forwarding.
- instance<alter_column> ac (emitter (), stream (), format_, tl);
+ instance<alter_column> ac (*this, tl);
trav_rel::unames n (*ac);
names (at, n);
os << ")" << endl;
@@ -426,9 +392,10 @@ namespace relational
{
pre_statement ();
alter_header (at.name ());
- os << " DROP (";
+ os << endl
+ << " DROP (";
- instance<drop_column> dc (emitter (), stream (), format_);
+ instance<drop_column> dc (*this);
trav_rel::unames n (*dc);
names (at, n);
os << ")" << endl;
@@ -436,20 +403,34 @@ namespace relational
post_statement ();
}
- if (check_alter_null (at, false))
+ if (check_alter_column_null (at, false))
{
pre_statement ();
alter_header (at.name ());
- os << " MODIFY (";
+ os << endl
+ << " MODIFY (";
bool fl (false); // (Im)perfect forwarding.
- instance<alter_column> ac (emitter (), stream (), format_, fl);
+ instance<alter_column> ac (*this, fl);
trav_rel::unames n (*ac);
names (at, n);
os << ")" << endl;
post_statement ();
}
+
+ if (check<sema_rel::add_foreign_key> (at))
+ {
+ pre_statement ();
+ alter_header (at.name ());
+
+ instance<create_foreign_key> cfc (*this);
+ trav_rel::unames n (*cfc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
}
};
entry<alter_table_post> alter_table_post_;
diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx
index 575f8e4..6d44139 100644
--- a/odb/relational/pgsql/schema.cxx
+++ b/odb/relational/pgsql/schema.cxx
@@ -2,8 +2,6 @@
// copyright : Copyright (c) 2009-2013 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>
@@ -18,6 +16,7 @@ namespace relational
namespace schema
{
namespace relational = relational::schema;
+ using relational::table_set;
//
// Drop.
@@ -28,10 +27,17 @@ namespace relational
drop_table (base const& x): base (x) {}
virtual void
- drop (sema_rel::qname const& table, bool migration)
+ traverse (sema_rel::table& t, bool migration)
{
+ // For PostgreSQL we use the CASCADE clause to drop foreign keys.
+ //
+ if (pass_ != 2)
+ return;
+
+ pre_statement ();
os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
- quote_id (table) << " CASCADE" << endl;
+ quote_id (t.name ()) << " CASCADE" << endl;
+ post_statement ();
}
};
entry<drop_table> drop_table_;
@@ -40,21 +46,6 @@ namespace relational
// Create.
//
- struct create_foreign_key;
-
- struct create_table: relational::create_table, context
- {
- create_table (base const& x): base (x) {}
-
- void
- traverse (sema_rel::table&);
-
- private:
- friend class create_foreign_key;
- set<qname> 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) {}
@@ -83,84 +74,17 @@ namespace relational
struct create_foreign_key: relational::create_foreign_key, context
{
- create_foreign_key (schema_format f, relational::create_table& ct)
- : base (f, ct)
- {
- }
-
create_foreign_key (base const& x): base (x) {}
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_));
-
- if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ())
- {
- base::traverse (fk);
- fk.set ("pgsql-fk-defined", true); // Mark it as defined.
- }
- }
-
- virtual void
- deferred ()
+ deferrable (sema_rel::deferrable d)
{
os << endl
- << " INITIALLY DEFERRED";
+ << " INITIALLY " << d;
}
};
entry<create_foreign_key> create_foreign_key_;
- struct add_foreign_key: create_foreign_key, relational::common
- {
- add_foreign_key (schema_format f, relational::create_table& ct)
- : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ())
- {
- }
-
- virtual void
- traverse (sema_rel::foreign_key& fk)
- {
- if (!fk.count ("pgsql-fk-defined"))
- {
- sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
-
- pre_statement ();
-
- os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
- base::create (fk);
- os << endl;
-
- post_statement ();
- }
- }
- };
-
- void create_table::
- traverse (sema_rel::table& t)
- {
- if (pass_ == 1)
- {
- // In migration we always add foreign keys on pass 2.
- //
- if (!t.is_a<sema_rel::add_table> ())
- tables_.insert (t.name ()); // Add it before to cover self-refs.
- base::traverse (t);
- return;
- }
-
- // Add foreign keys.
- //
- add_foreign_key fk (format_, *this);
- trav_rel::unames n (fk);
- names (t, n);
- }
-
struct create_index: relational::create_index, context
{
create_index (base const& x): base (x) {}
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
index 1a57205..34c8e2d 100644
--- a/odb/relational/processor.cxx
+++ b/odb/relational/processor.cxx
@@ -209,7 +209,8 @@ namespace relational
if (m.count ("polymorphic-ref"))
{
m.set ("not-null", true);
- m.set ("deferred", false);
+ m.set ("deferrable",
+ sema_rel::deferrable (sema_rel::deferrable::not_deferrable));
m.set ("on-delete", sema_rel::foreign_key::cascade);
}
diff --git a/odb/relational/schema.cxx b/odb/relational/schema.cxx
index 56fb7c0..0586e06 100644
--- a/odb/relational/schema.cxx
+++ b/odb/relational/schema.cxx
@@ -128,14 +128,12 @@ namespace relational
instance<changeset_post> changeset (*em, emos, f);
instance<drop_table> dtable (*em, emos, f);
- instance<alter_table_post> altable (*em, emos, f);
- trav_rel::add_table adtable; // Override.
+ instance<alter_table_post> atable (*em, emos, f);
trav_rel::qnames names;
changeset >> names;
names >> dtable;
- names >> altable;
- names >> adtable;
+ names >> atable;
// Pass 1 and 2.
//
@@ -143,7 +141,7 @@ namespace relational
{
changeset->pass (pass);
dtable->pass (pass);
- altable->pass (pass);
+ atable->pass (pass);
changeset->traverse (cs);
}
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
index 32cac41..247dca2 100644
--- a/odb/relational/schema.hxx
+++ b/odb/relational/schema.hxx
@@ -17,11 +17,14 @@ namespace relational
{
namespace schema
{
+ typedef std::set<qname> table_set;
+
struct common: virtual context
{
typedef ::emitter emitter_type;
- common (emitter_type& e, ostream& os): e_ (e), os_ (os) {}
+ common (emitter_type& e, ostream& os, schema_format f)
+ : e_ (e), os_ (os), format_ (f) {}
void
pre_statement ()
@@ -49,9 +52,32 @@ namespace relational
return os_;
}
+ public:
+ // Find an entity corresponding to the drop node in alter_table.
+ //
+ template <typename T, typename D>
+ T&
+ find (D& d)
+ {
+ using sema_rel::model;
+ using sema_rel::changeset;
+ using sema_rel::table;
+ using sema_rel::alter_table;
+
+ alter_table& at (dynamic_cast<alter_table&> (d.scope ()));
+ changeset& cs (dynamic_cast<changeset&> (at.scope ()));
+ model& bm (cs.base_model ());
+ table* bt (bm.find<table> (at.name ()));
+ assert (bt != 0);
+ T* b (bt->find<T> (d.name ()));
+ assert (b != 0);
+ return *b;
+ }
+
protected:
emitter_type& e_;
ostream& os_;
+ schema_format format_;
};
//
@@ -64,12 +90,8 @@ namespace relational
{
typedef drop_column base;
- drop_column (emitter_type& e,
- ostream& os,
- schema_format f,
- bool* first = 0)
- : common (e, os),
- format_ (f),
+ drop_column (common const& c, bool* first = 0)
+ : common (c),
first_ (first != 0 ? *first : first_data_),
first_data_ (true)
{
@@ -79,7 +101,6 @@ namespace relational
: root_context (), // @@ -Wextra
context (),
common (c),
- format_ (c.format_),
first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
first_data_ (c.first_data_)
{
@@ -99,15 +120,128 @@ namespace relational
if (first_)
first_ = false;
else
- os << "," << endl;
+ os << ",";
- os << " ";
+ os << endl
+ << " ";
drop_header ();
os << quote_id (dc.name ());
}
protected:
- schema_format format_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Normally only used in migration but some databases use it as a
+ // base to also drop foreign keys in schema.
+ //
+ struct drop_foreign_key: trav_rel::foreign_key,
+ trav_rel::drop_foreign_key,
+ trav_rel::add_foreign_key, // Override.
+ common
+ {
+ typedef drop_foreign_key base;
+
+ // Schema constructor.
+ //
+ drop_foreign_key (common const& c, table_set& dropped, bool* first = 0)
+ : common (c),
+ dropped_ (&dropped),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Migration constructor.
+ //
+ drop_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ dropped_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ drop_foreign_key (drop_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ dropped_ (c.dropped_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ drop_header ()
+ {
+ os << "DROP CONSTRAINT ";
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // If the table which we reference is droped before us, then
+ // we need to drop the constraint first. Similarly, if the
+ // referenced table is not part if this model, then assume
+ // it is dropped before us. In migration we always do this
+ // first.
+ //
+ sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
+
+ if (dropped_ != 0)
+ {
+ sema_rel::qname const& rt (fk.referenced_table ());
+ sema_rel::model& m (dynamic_cast<sema_rel::model&> (t.scope ()));
+
+ if (dropped_->find (rt) == dropped_->end () &&
+ m.find (rt) != m.names_end ())
+ return;
+ }
+
+ drop (t, fk);
+ }
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ // When generating schema we would need to check if the key exists.
+ // So this implementation will need to be customize on the per-
+ // database level.
+ //
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ";
+ drop_header ();
+ os << quote_id (fk.name ()) << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ drop (dfk);
+ }
+
+ virtual void
+ drop (sema_rel::drop_foreign_key& dfk)
+ {
+ os << " ";
+ drop_header ();
+ os << quote_id (dfk.name ());
+ }
+
+ protected:
+ table_set* dropped_;
bool& first_;
bool first_data_;
};
@@ -120,33 +254,15 @@ namespace relational
enum index_type {unique, non_unique, all};
- drop_index (emitter_type& e,
- ostream& os,
- schema_format f,
- index_type t = all)
- : common (e, os), format_ (f), type_ (t)
- {
- }
+ drop_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
virtual void
traverse (sema_rel::drop_index& di)
{
- using sema_rel::model;
- using sema_rel::changeset;
- using sema_rel::table;
- using sema_rel::alter_table;
- using sema_rel::index;
-
// Find the index we are dropping in the base model.
//
- alter_table& at (dynamic_cast<alter_table&> (di.scope ()));
- changeset& cs (dynamic_cast<changeset&> (at.scope ()));
- model& bm (cs.base_model ());
- table* bt (bm.find<table> (at.name ()));
- assert (bt != 0);
- index* bi (bt->find<index> (di.name ()));
- assert (bi != 0);
- traverse (*bi);
+ traverse (find<sema_rel::index> (di));
}
virtual void
@@ -180,40 +296,56 @@ namespace relational
}
protected:
- schema_format format_;
index_type type_;
};
struct drop_table: trav_rel::table,
trav_rel::drop_table,
+ trav_rel::add_table, // Override.
+ trav_rel::alter_table, // Override.
common
{
typedef drop_table base;
drop_table (emitter_type& e, ostream& os, schema_format f)
- : common (e, os), format_ (f)
- {
- }
+ : common (e, os, f) {}
virtual void
drop (sema_rel::qname const& table, bool migration)
{
+ pre_statement ();
os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
quote_id (table) << endl;
+ post_statement ();
}
virtual void
traverse (sema_rel::table& t, bool migration)
{
- // By default we do everything in a single, last pass. But some
- // databases may require two passes.
+ // By default drop foreign keys referencing tables that would
+ // have already been dropped on the first pass.
//
- if (pass_ != 2)
- return;
+ if (pass_ == 1)
+ {
+ // Drop constraints. In migration this is always done on pass 1.
+ //
+ if (migration)
+ {
+ instance<drop_foreign_key> dfk (*this);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ else
+ {
+ dropped_.insert (t.name ()); // Add it before to cover self-refs.
- pre_statement ();
- drop (t.name (), migration);
- post_statement ();
+ instance<drop_foreign_key> dfk (*this, dropped_);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ }
+ else
+ drop (t.name (), migration);
}
virtual void
@@ -247,8 +379,8 @@ namespace relational
}
protected:
- schema_format format_;
unsigned short pass_;
+ table_set dropped_;
};
struct drop_model: trav_rel::model, common
@@ -256,7 +388,7 @@ namespace relational
typedef drop_model base;
drop_model (emitter_type& e, ostream& os, schema_format f)
- : common (e, os), format_ (f)
+ : common (e, os, f)
{
}
@@ -294,31 +426,27 @@ namespace relational
}
protected:
- schema_format format_;
unsigned short pass_;
};
//
// Create.
//
- struct create_table;
struct create_column: trav_rel::column,
trav_rel::add_column,
+ trav_rel::alter_column, // Override.
common
{
typedef create_column base;
- create_column (emitter_type& e,
- ostream& os,
- schema_format f,
+ create_column (common const& c,
bool override_null = true,
bool* first = 0)
- : common (e, os),
- format_ (f),
+ : common (c),
+ override_null_ (override_null),
first_ (first != 0 ? *first : first_data_),
- first_data_ (true),
- override_null_ (override_null)
+ first_data_ (true)
{
}
@@ -326,10 +454,9 @@ namespace relational
: root_context (), // @@ -Wextra
context (),
common (c),
- format_ (c.format_),
+ override_null_ (c.override_null_),
first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
- first_data_ (c.first_data_),
- override_null_ (c.override_null_)
+ first_data_ (c.first_data_)
{
}
@@ -339,9 +466,10 @@ namespace relational
if (first_)
first_ = false;
else
- os << "," << endl;
+ os << ",";
- os << " ";
+ os << endl
+ << " ";
create (c);
}
@@ -359,9 +487,10 @@ namespace relational
if (first_)
first_ = false;
else
- os << "," << endl;
+ os << ",";
- os << " ";
+ os << endl
+ << " ";
add_header ();
create (ac);
}
@@ -441,21 +570,17 @@ namespace relational
}
protected:
- schema_format format_;
+ bool override_null_; // Override NOT NULL in add_column.
bool& first_;
bool first_data_;
bool add_;
- bool override_null_; // Override NOT NULL in add_column.
};
- struct create_primary_key: trav_rel::primary_key, virtual context
+ struct create_primary_key: trav_rel::primary_key, common
{
typedef create_primary_key base;
- create_primary_key (schema_format f, create_table& ct)
- : format_ (f), create_table_ (ct)
- {
- }
+ create_primary_key (common const& c): common (c) {}
virtual void
traverse (sema_rel::primary_key& pk)
@@ -468,8 +593,7 @@ namespace relational
// We will always follow a column.
//
- os << "," << endl
- << endl;
+ os << "," << endl;
create (pk);
}
@@ -502,38 +626,104 @@ namespace relational
os << ")";
}
-
- protected:
- schema_format format_;
- create_table& create_table_;
};
- struct create_foreign_key: trav_rel::foreign_key, virtual context
+ struct create_foreign_key: trav_rel::foreign_key,
+ trav_rel::add_foreign_key,
+ common
{
typedef create_foreign_key base;
- create_foreign_key (schema_format f, create_table& ct)
- : format_ (f), create_table_ (ct)
+ // Schema constructor.
+ //
+ create_foreign_key (common const& c, table_set& created, bool* first = 0)
+ : common (c),
+ created_ (&created),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Migration constructor.
+ //
+ create_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ created_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ create_foreign_key (create_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ created_ (c.created_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
{
}
virtual void
traverse (sema_rel::foreign_key& fk)
{
- // We will always follow a column or another key.
+ // 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).
//
- os << "," << endl
- << endl;
+ if (created_->find (fk.referenced_table ()) != created_->end ())
+ {
+ generate (fk);
+ fk.set (db.string () + "-fk-defined", true); // Mark it as defined.
+ }
+ }
+
+ virtual void
+ generate (sema_rel::foreign_key& fk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+ os << endl
+ << " CONSTRAINT ";
create (fk);
}
virtual void
+ add_header ()
+ {
+ os << "ADD CONSTRAINT ";
+ }
+
+ virtual void
+ traverse (sema_rel::add_foreign_key& afk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ add (afk);
+ }
+
+ virtual void
+ add (sema_rel::add_foreign_key& afk)
+ {
+ os << " ";
+ add_header ();
+ create (afk);
+ }
+
+ virtual void
create (sema_rel::foreign_key& fk)
{
using sema_rel::foreign_key;
- os << " CONSTRAINT " << name (fk) << endl
+ os << name (fk) << endl
<< " FOREIGN KEY (";
for (foreign_key::contains_iterator i (fk.contains_begin ());
@@ -578,8 +768,8 @@ namespace relational
if (fk.on_delete () != foreign_key::no_action)
on_delete (fk.on_delete ());
- if (fk.deferred ())
- deferred ();
+ if (!fk.not_deferrable ())
+ deferrable (fk.deferrable ());
}
virtual string
@@ -613,15 +803,69 @@ namespace relational
}
virtual void
- deferred ()
+ deferrable (sema_rel::deferrable d)
{
os << endl
- << " DEFERRABLE INITIALLY DEFERRED";
+ << " DEFERRABLE INITIALLY " << d;
}
protected:
- schema_format format_;
- create_table& create_table_;
+ table_set* created_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Add foreign key via ALTER TABLE.
+ //
+ struct add_foreign_key: trav_rel::foreign_key, common
+ {
+ typedef add_foreign_key base;
+
+ add_foreign_key (common const& c, table_set& created, bool* first = 0)
+ : common (c),
+ created_ (created),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true),
+ def_ (c, created_)
+ {
+ }
+
+ add_foreign_key (add_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ created_ (c.created_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_),
+ def_ (c, created_)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ if (!fk.count (db.string () + "-fk-defined"))
+ generate (dynamic_cast<sema_rel::table&> (fk.scope ()), fk);
+ }
+
+ virtual void
+ generate (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ADD CONSTRAINT ";
+ def_->create (fk);
+ os << endl;
+
+ post_statement ();
+ }
+
+ protected:
+ table_set& created_;
+ bool& first_;
+ bool first_data_;
+ instance<create_foreign_key> def_;
};
struct create_index: trav_rel::index, common
@@ -630,13 +874,8 @@ namespace relational
enum index_type {unique, non_unique, all};
- create_index (emitter_type& e,
- ostream& os,
- schema_format f,
- index_type t = all)
- : common (e, os), format_ (f), type_ (t)
- {
- }
+ create_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
virtual void
traverse (sema_rel::index& in)
@@ -715,23 +954,24 @@ namespace relational
}
protected:
- schema_format format_;
index_type type_;
};
- struct create_table: trav_rel::table, common
+ struct create_table: trav_rel::table,
+ trav_rel::alter_table, // Override.
+ common
{
typedef create_table base;
+ using trav_rel::table::names;
+
create_table (emitter_type& e, ostream& os, schema_format f)
- : common (e, os), format_ (f)
- {
- }
+ : common (e, os, f) {}
virtual void
create_pre (sema_rel::qname const& table)
{
- os << "CREATE TABLE " << quote_id (table) << " (" << endl;
+ os << "CREATE TABLE " << quote_id (table) << " (";
}
virtual void
@@ -741,22 +981,21 @@ namespace relational
}
virtual void
- traverse (sema_rel::table& t)
+ create (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 (emitter (), stream (), format_);
- instance<create_primary_key> pk (format_, *this);
- instance<create_foreign_key> fk (format_, *this);
- trav_rel::unames n;
+ instance<create_column> c (*this);
+ instance<create_primary_key> pk (*this);
+ // We will always follow a column, so set first to false.
+ //
+ bool f (false); // (Im)perfect forwarding.
+ bool* pf (&f); // (Im)perfect forwarding.
+ instance<create_foreign_key> fk (*this, created_, pf);
+
+ trav_rel::unames n;
n >> c;
n >> pk;
n >> fk;
@@ -769,12 +1008,37 @@ namespace relational
// Create indexes.
//
{
- instance<create_index> in (emitter (), stream (), format_);
+ instance<create_index> in (*this);
trav_rel::unames n (*in);
names (t, n);
}
}
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ // By default add foreign keys referencing tables that haven't
+ // yet been defined on the second pass.
+ //
+ if (pass_ == 1)
+ {
+ // In migration we always add foreign keys on pass 2.
+ //
+ if (!t.is_a<sema_rel::add_table> ())
+ created_.insert (t.name ()); // Add it before to cover self-refs.
+
+ create (t);
+ }
+ else
+ {
+ // Add foreign keys.
+ //
+ instance<add_foreign_key> fk (*this, created_);
+ trav_rel::unames n (*fk);
+ names (t, n);
+ }
+ }
+
void
pass (unsigned short p)
{
@@ -782,8 +1046,8 @@ namespace relational
}
protected:
- schema_format format_;
unsigned short pass_;
+ table_set created_;
};
struct create_model: trav_rel::model, common
@@ -791,9 +1055,7 @@ namespace relational
typedef create_model base;
create_model (emitter_type& e, ostream& os, schema_format f)
- : common (e, os), format_ (f)
- {
- }
+ : common (e, os, f) {}
// This version is only called for file schema.
//
@@ -818,7 +1080,6 @@ namespace relational
}
protected:
- schema_format format_;
unsigned short pass_;
};
@@ -832,18 +1093,13 @@ namespace relational
{
typedef alter_column base;
- alter_column (emitter_type& e,
- ostream& os,
- schema_format f,
- bool pre,
- bool* first = 0)
- : common (e, os),
- format_ (f),
+ alter_column (common const& c, bool pre, bool* first = 0)
+ : common (c),
pre_ (pre),
first_ (first != 0 ? *first : first_data_),
first_data_ (true),
fl_ (false),
- def_ (e, os, f, fl_)
+ def_ (c, fl_)
{
}
@@ -851,12 +1107,11 @@ namespace relational
: root_context (), // @@ -Wextra
context (),
common (c),
- format_ (c.format_),
pre_ (c.pre_),
first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
first_data_ (c.first_data_),
fl_ (false),
- def_ (common::e_, common::os_, c.format_, fl_)
+ def_ (c, fl_)
{
}
@@ -885,9 +1140,10 @@ namespace relational
if (first_)
first_ = false;
else
- os << "," << endl;
+ os << ",";
- os << " ";
+ os << endl
+ << " ";
alter_header ();
alter (c);
}
@@ -910,7 +1166,6 @@ namespace relational
}
protected:
- schema_format format_;
bool pre_;
bool& first_;
bool first_data_;
@@ -921,9 +1176,7 @@ namespace relational
struct alter_table_common: trav_rel::alter_table, common
{
alter_table_common (emitter_type& e, ostream& os, schema_format f)
- : common (e, os), format_ (f)
- {
- }
+ : common (e, os, f) {}
template <typename T>
T*
@@ -935,32 +1188,43 @@ namespace relational
if (T* x = dynamic_cast<T*> (&i->nameable ()))
return x;
}
-
return 0;
}
- sema_rel::alter_column*
- check_alter_null (sema_rel::alter_table& at, bool v)
+ sema_rel::column*
+ check_alter_column_null (sema_rel::alter_table& at, bool v)
{
- using sema_rel::alter_column;
-
for (sema_rel::alter_table::names_iterator i (at.names_begin ());
i != at.names_end (); ++i)
{
+ using sema_rel::add_column;
+ using sema_rel::alter_column;
+
if (alter_column* ac = dynamic_cast<alter_column*> (&i->nameable ()))
{
if (ac->null_altered () && ac->null () == v)
return ac;
}
- }
+ // If we are testing for NOT NULL, also look for new columns that
+ // we initially add as NULL and later convert to NOT NULL.
+ //
+ if (!v)
+ {
+ if (add_column* ac = dynamic_cast<add_column*> (&i->nameable ()))
+ {
+ if (!ac->null () && ac->default_ ().empty ())
+ return ac;
+ }
+ }
+ }
return 0;
}
virtual void
alter_header (sema_rel::qname const& table)
{
- os << "ALTER TABLE " << quote_id (table) << endl;
+ os << "ALTER TABLE " << quote_id (table);
}
void
@@ -970,7 +1234,6 @@ namespace relational
}
protected:
- schema_format format_;
unsigned short pass_;
};
@@ -979,9 +1242,7 @@ namespace relational
typedef alter_table_pre base;
alter_table_pre (emitter_type& e, ostream& os, schema_format f)
- : alter_table_common (e, os, f)
- {
- }
+ : alter_table_common (e, os, f) {}
// Check if there will be any clauses in ALTER TABLE.
//
@@ -990,8 +1251,13 @@ namespace relational
virtual bool
check (sema_rel::alter_table& at)
{
- return check<sema_rel::add_column> (at) ||
- check_alter_null (at, true);
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::drop_foreign_key> (at) ||
+ check<sema_rel::add_column> (at) ||
+ check_alter_column_null (at, true);
}
virtual void
@@ -1006,11 +1272,13 @@ namespace relational
bool f (true); // Shared first flag.
bool* pf (&f); // (Im)perfect forwarding.
bool tl (true); // (Im)perfect forwarding.
- instance<create_column> cc (emitter (), stream (), format_, tl, pf);
- instance<alter_column> ac (emitter (), stream (), format_, tl, pf);
+ instance<create_column> cc (*this, tl, pf);
+ instance<alter_column> ac (*this, tl, pf);
+ instance<drop_foreign_key> dfk (*this, pf);
trav_rel::unames n;
n >> cc;
n >> ac;
+ n >> dfk;
names (at, n);
os << endl;
@@ -1026,7 +1294,7 @@ namespace relational
//
{
drop_index::index_type it (drop_index::unique);
- instance<drop_index> in (emitter (), stream (), format_, it);
+ instance<drop_index> in (*this, it);
trav_rel::unames n (*in);
names (at, n);
}
@@ -1040,7 +1308,7 @@ namespace relational
//
{
create_index::index_type it (create_index::non_unique);
- instance<create_index> in (emitter (), stream (), format_, it);
+ instance<create_index> in (*this, it);
trav_rel::unames n (*in);
names (at, n);
}
@@ -1053,9 +1321,7 @@ namespace relational
typedef changeset_pre base;
changeset_pre (emitter_type& e, ostream& os, schema_format f)
- : common (e, os), format_ (f)
- {
- }
+ : common (e, os, f) {}
// This version is only called for file migration.
//
@@ -1080,7 +1346,6 @@ namespace relational
}
protected:
- schema_format format_;
unsigned short pass_;
};
@@ -1089,9 +1354,7 @@ namespace relational
typedef alter_table_post base;
alter_table_post (emitter_type& e, ostream& os, schema_format f)
- : alter_table_common (e, os, f)
- {
- }
+ : alter_table_common (e, os, f) {}
// Check if there will be any clauses in ALTER TABLE.
//
@@ -1100,8 +1363,13 @@ namespace relational
virtual bool
check (sema_rel::alter_table& at)
{
- return check<sema_rel::drop_column> (at) ||
- check_alter_null (at, false);
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::add_foreign_key> (at) ||
+ check<sema_rel::drop_column> (at) ||
+ check_alter_column_null (at, false);
}
virtual void
@@ -1116,11 +1384,14 @@ namespace relational
bool f (true); // Shared first flag.
bool* pf (&f); // (Im)perfect forwarding.
bool fl (false); // (Im)perfect forwarding.
- instance<drop_column> dc (emitter (), stream (), format_, pf);
- instance<alter_column> ac (emitter (), stream (), format_, fl, pf);
+ instance<drop_column> dc (*this, pf);
+ instance<alter_column> ac (*this, fl, pf);
+ instance<create_foreign_key> fk (*this, pf);
+
trav_rel::unames n;
n >> dc;
n >> ac;
+ n >> fk;
names (at, n);
os << endl;
@@ -1136,7 +1407,7 @@ namespace relational
//
{
drop_index::index_type it (drop_index::non_unique);
- instance<drop_index> in (emitter (), stream (), format_, it);
+ instance<drop_index> in (*this, it);
trav_rel::unames n (*in);
names (at, n);
}
@@ -1150,7 +1421,7 @@ namespace relational
//
{
create_index::index_type it (create_index::unique);
- instance<create_index> in (emitter (), stream (), format_, it);
+ instance<create_index> in (*this, it);
trav_rel::unames n (*in);
names (at, n);
}
@@ -1163,9 +1434,7 @@ namespace relational
typedef changeset_post base;
changeset_post (emitter_type& e, ostream& os, schema_format f)
- : common (e, os), format_ (f)
- {
- }
+ : common (e, os, f) {}
// This version is only called for file migration.
//
@@ -1190,7 +1459,6 @@ namespace relational
}
protected:
- schema_format format_;
unsigned short pass_;
};
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index 210d04b..0eb3cd3 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -2503,7 +2503,7 @@ traverse_object (type& c)
if (poly && !poly_derived)
{
os << "void " << traits << "::" << endl
- << "discriminator_ (statements_type & sts," << endl
+ << "discriminator_ (statements_type& sts," << endl
<< "const id_type& id," << endl
<< "discriminator_type* pd";
diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx
index 9d3f859..92f6764 100644
--- a/odb/relational/sqlite/schema.cxx
+++ b/odb/relational/sqlite/schema.cxx
@@ -33,6 +33,9 @@ namespace relational
traverse (sema_rel::add_column& ac)
{
using sema_rel::alter_table;
+ using sema_rel::add_column;
+ using sema_rel::add_foreign_key;
+
alter_table& at (static_cast<alter_table&> (ac.scope ()));
pre_statement ();
@@ -40,8 +43,37 @@ namespace relational
os << "ALTER TABLE " << quote_id (at.name ()) << endl
<< " ADD COLUMN ";
create (ac);
- os << endl;
+ // SQLite doesn't support adding foreign keys other than inline
+ // via a column definition. See if we can handle any.
+ //
+ add_foreign_key* afk (0);
+
+ for (add_column::contained_iterator i (ac.contained_begin ());
+ i != ac.contained_end ();
+ ++i)
+ {
+ if ((afk = dynamic_cast<add_foreign_key*> (&i->key ())))
+ {
+ // Check that it is a single-column foreign key. Also make
+ // sure the column and foreign key are from the same changeset.
+ //
+ if (afk->contains_size () != 1 || &ac.scope () != &afk->scope ())
+ afk = 0;
+ else
+ break;
+ }
+ }
+
+ if (afk != 0)
+ {
+ os << " CONSTRAINT " << quote_id (afk->name ()) << " REFERENCES " <<
+ quote_id (afk->referenced_table ().uname ()) << " (" <<
+ quote_id (afk->referenced_columns ()[0]) << ")";
+ afk->set ("sqlite-fk-defined", true); // Mark it as defined.
+ }
+
+ os << endl;
post_statement ();
}
@@ -60,6 +92,17 @@ namespace relational
{
create_foreign_key (base const& x): base (x) {}
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // In SQLite, all constraints are defined as part of a table.
+ //
+ os << "," << endl
+ << " CONSTRAINT ";
+
+ create (fk);
+ }
+
virtual string
table_name (sema_rel::foreign_key& fk)
{
@@ -71,6 +114,22 @@ namespace relational
};
entry<create_foreign_key> create_foreign_key_;
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::table& t)
+ {
+ // For SQLite we do everything in a single pass since there
+ // is no way to add constraints later.
+ //
+ if (pass_ == 1)
+ create (t);
+ }
+ };
+ entry<create_table> create_table_;
+
struct create_index: relational::create_index, context
{
create_index (base const& x): base (x) {}
@@ -115,6 +174,24 @@ namespace relational
};
entry<drop_index> drop_index_;
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // In SQLite there is no way to drop foreign keys except as part
+ // of the table.
+ //
+ if (pass_ != 2)
+ return;
+
+ drop (t.name (), migration);
+ }
+ };
+ entry<drop_table> drop_table_;
+
struct alter_table_pre: relational::alter_table_pre, context
{
alter_table_pre (base const& x): base (x) {}
@@ -124,11 +201,8 @@ namespace relational
{
// SQLite can only add a single column per ALTER TABLE statement.
//
- instance<create_column> cc (emitter (), stream (), format_);
- trav_rel::alter_column ac; // Override.
- trav_rel::unames n;
- n >> cc;
- n >> ac;
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
names (at, n);
// SQLite does not support altering columns.
@@ -141,6 +215,18 @@ namespace relational
"' in table '" << at.name () << "'" << endl;
throw operation_failed ();
}
+
+ // SQLite does not support dropping constraints.
+ //
+ if (sema_rel::drop_foreign_key* dfk =
+ check<sema_rel::drop_foreign_key> (at))
+ {
+ cerr << "error: SQLite does not support dropping of foreign keys"
+ << endl;
+ cerr << "info: first dropped foreign key is '" << dfk->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
}
};
entry<alter_table_pre> alter_table_pre_;
@@ -162,6 +248,26 @@ namespace relational
"' in table '" << at.name () << "'" << endl;
throw operation_failed ();
}
+
+ // SQLite doesn't support adding foreign keys other than inline
+ // via a column definition. See if there are any that we couldn't
+ // handle that way.
+ //
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ sema_rel::add_foreign_key* afk (
+ dynamic_cast<sema_rel::add_foreign_key*> (&i->nameable ()));
+
+ if (afk == 0 || afk->count ("sqlite-fk-defined"))
+ continue;
+
+ cerr << "error: SQLite does not support adding foreign keys"
+ << endl;
+ cerr << "info: first added foreign key is '" << afk->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
}
};
entry<alter_table_post> alter_table_post_;
diff --git a/odb/semantics/relational/deferrable.cxx b/odb/semantics/relational/deferrable.cxx
new file mode 100644
index 0000000..6aecb12
--- /dev/null
+++ b/odb/semantics/relational/deferrable.cxx
@@ -0,0 +1,56 @@
+// file : odb/semantics/relational/deferrable.cxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <odb/semantics/relational/deferrable.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ static const char* deferrable_[] =
+ {
+ "NOT DEFERRABLE",
+ "IMMEDIATE",
+ "DEFERRED"
+ };
+
+ string deferrable::
+ string () const
+ {
+ return deferrable_[v_];
+ }
+
+ ostream&
+ operator<< (ostream& os, deferrable const& v)
+ {
+ return os << v.string ();
+ }
+
+ istream&
+ operator>> (istream& is, deferrable& v)
+ {
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "not_deferrable" || s == "NOT DEFERRABLE")
+ v = deferrable::not_deferrable;
+ else if (s == "immediate" || s == "IMMEDIATE")
+ v = deferrable::immediate;
+ else if (s == "deferred" || s == "DEFERRED")
+ v = deferrable::deferred;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+ }
+ }
+}
diff --git a/odb/semantics/relational/deferrable.hxx b/odb/semantics/relational/deferrable.hxx
new file mode 100644
index 0000000..87d696c
--- /dev/null
+++ b/odb/semantics/relational/deferrable.hxx
@@ -0,0 +1,42 @@
+// file : odb/semantics/relational/deferrable.hxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
+#define ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
+
+#include <string>
+#include <iosfwd>
+
+namespace semantics
+{
+ namespace relational
+ {
+ struct deferrable
+ {
+ enum value
+ {
+ not_deferrable,
+ immediate,
+ deferred
+ };
+
+ deferrable (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+ private:
+ value v_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, deferrable const&);
+
+ std::istream&
+ operator>> (std::istream&, deferrable&);
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
diff --git a/odb/semantics/relational/foreign-key.cxx b/odb/semantics/relational/foreign-key.cxx
index 0407858..8188e1f 100644
--- a/odb/semantics/relational/foreign-key.cxx
+++ b/odb/semantics/relational/foreign-key.cxx
@@ -50,7 +50,7 @@ namespace semantics
: key (k, s, g),
referenced_table_ (k.referenced_table_),
referenced_columns_ (k.referenced_columns_),
- deferred_ (k.deferred_),
+ deferrable_ (k.deferrable_),
on_delete_ (k.on_delete_)
{
}
@@ -58,7 +58,7 @@ namespace semantics
foreign_key::
foreign_key (xml::parser& p, uscope& s, graph& g)
: key (p, s, g),
- deferred_ (p.attribute ("deferred", false)),
+ deferrable_ (p.attribute ("deferrable", deferrable_type ())),
on_delete_ (p.attribute ("on-delete", no_action))
{
using namespace xml;
@@ -94,8 +94,8 @@ namespace semantics
{
key::serialize_attributes (s);
- if (deferred ())
- s.attribute ("deferred", true);
+ if (deferrable () != deferrable_type::not_deferrable)
+ s.attribute ("deferrable", deferrable ());
if (on_delete () != no_action)
s.attribute ("on-delete", on_delete ());
diff --git a/odb/semantics/relational/foreign-key.hxx b/odb/semantics/relational/foreign-key.hxx
index 50a3fae..458683f 100644
--- a/odb/semantics/relational/foreign-key.hxx
+++ b/odb/semantics/relational/foreign-key.hxx
@@ -7,6 +7,7 @@
#include <iosfwd>
+#include <odb/semantics/relational/deferrable.hxx>
#include <odb/semantics/relational/elements.hxx>
#include <odb/semantics/relational/key.hxx>
@@ -38,10 +39,15 @@ namespace semantics
}
public:
+ typedef relational::deferrable deferrable_type;
+
+ deferrable_type
+ deferrable () const {return deferrable_;}
+
bool
- deferred () const
+ not_deferrable () const
{
- return deferred_;
+ return deferrable_ == deferrable_type::not_deferrable;
}
enum action_type
@@ -51,19 +57,16 @@ namespace semantics
};
action_type
- on_delete () const
- {
- return on_delete_;
- }
+ on_delete () const {return on_delete_;}
public:
foreign_key (string const& id,
qname const& referenced_table,
- bool deferred,
+ deferrable_type deferrable,
action_type on_delete = no_action)
: key (id),
referenced_table_ (referenced_table),
- deferred_ (deferred),
+ deferrable_ (deferrable),
on_delete_ (on_delete)
{
}
@@ -93,7 +96,7 @@ namespace semantics
private:
qname referenced_table_;
columns referenced_columns_;
- bool deferred_;
+ deferrable_type deferrable_;
action_type on_delete_;
};
@@ -108,7 +111,7 @@ namespace semantics
public:
add_foreign_key (string const& id,
qname const& rt,
- bool d,
+ deferrable_type d,
action_type od = no_action)
: foreign_key (id, rt, d, od) {}
add_foreign_key (foreign_key const& fk, uscope& s, graph& g)