From a4f25daf17392c9c4b90de60b9d777290706f667 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 8 Apr 2013 11:13:51 +0200 Subject: Generate add/drop foreign key migration statements Also add the --fkeys-deferrable-mode option. General schemas generation rework. --- odb/relational/sqlite/schema.cxx | 118 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 6 deletions(-) (limited to 'odb/relational/sqlite/schema.cxx') 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 (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 (&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_; + 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_; + struct create_index: relational::create_index, context { create_index (base const& x): base (x) {} @@ -115,6 +174,24 @@ namespace relational }; entry 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_; + 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 cc (emitter (), stream (), format_); - trav_rel::alter_column ac; // Override. - trav_rel::unames n; - n >> cc; - n >> ac; + instance 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 (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_; @@ -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 (&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_; -- cgit v1.1