From a9da959e71ce02b7e8b0457edcae303043b2799a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 8 Apr 2013 14:22:00 +0200 Subject: Use single ALTER TABLE to add foreign keys on pass 2 --- odb/relational/mssql/schema.cxx | 87 +++++++++++++++++------ odb/relational/mysql/schema.cxx | 96 ++++++++++++++++++-------- odb/relational/oracle/schema.cxx | 4 +- odb/relational/schema.hxx | 145 ++++++++++++++++++--------------------- 4 files changed, 200 insertions(+), 132 deletions(-) diff --git a/odb/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx index 61e7e68..649ab13 100644 --- a/odb/relational/mssql/schema.cxx +++ b/odb/relational/mssql/schema.cxx @@ -199,14 +199,14 @@ namespace relational create_foreign_key (base const& x): base (x) {} virtual void - generate (sema_rel::foreign_key& fk) + traverse_create (sema_rel::foreign_key& fk) { // SQL Server 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); + base::traverse_create (fk); else { // Don't bloat C++ code with comment strings if we are @@ -225,9 +225,9 @@ namespace relational } virtual void - traverse (sema_rel::add_foreign_key& afk) + traverse_add (sema_rel::foreign_key& fk) { - bool c (!afk.not_deferrable () && !in_comment); + bool c (!fk.not_deferrable () && !in_comment); if (c && format_ != schema_format::sql) return; @@ -241,7 +241,7 @@ namespace relational << " "; os << "CONSTRAINT "; - create (afk); + create (fk); if (c) os << endl @@ -268,33 +268,76 @@ namespace relational }; entry create_foreign_key_; - struct add_foreign_key: relational::add_foreign_key, context + struct create_table: relational::create_table, context { - add_foreign_key (base const& x): base (x) {} + create_table (base const& x): base (x) {} + + // See if there are any undefined foreign keys that are not + // deferrable. + // + bool + check_undefined_fk_deferrable_only (sema_rel::table& t) + { + for (sema_rel::table::names_iterator i (t.names_begin ()); + i != t.names_end (); ++i) + { + using sema_rel::foreign_key; + + if (foreign_key* fk = dynamic_cast (&i->nameable ())) + { + if (!fk->count ("mssql-fk-defined") && + fk->not_deferrable ()) + return false; + } + } + return true; + } virtual void - generate (sema_rel::table& t, sema_rel::foreign_key& fk) + traverse (sema_rel::table& t) { - // SQL Server has no deferrable constraints. - // - if (fk.not_deferrable ()) - base::generate (t, fk); + if (pass_ == 1) + base::traverse (t); else { - if (format_ != schema_format::sql) - return; + // Add undefined foreign keys. + // + if (check_undefined_fk (t)) + { + bool deferrable (check_undefined_fk_deferrable_only (t)); - os << "/*" << endl; - os << "ALTER TABLE " << quote_id (t.name ()) << endl - << " ADD CONSTRAINT "; - def_->create (fk); - os << endl - << "*/" << endl - << endl; + if (!deferrable || format_ == schema_format::sql) + { + if (deferrable) + { + os << "/*" << endl; + in_comment = true; + } + else + pre_statement (); + + os << "ALTER TABLE " << quote_id (t.name ()) << endl + << " ADD "; + + instance cfk (*this); + trav_rel::unames n (*cfk); + names (t, n); + os << endl; + + if (deferrable) + { + in_comment = false; + os << "*/" << endl + << endl; + } + else + post_statement (); + } + } } } }; - entry add_foreign_key_; + entry create_table_; struct drop_index: relational::drop_index, context { diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index be058ce..d1721fa 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -130,14 +130,14 @@ namespace relational create_foreign_key (base const& x): base (x) {} virtual void - generate (sema_rel::foreign_key& fk) + traverse_create (sema_rel::foreign_key& fk) { // 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); + base::traverse_create (fk); else { // Don't bloat C++ code with comment strings if we are @@ -149,17 +149,17 @@ namespace relational os << endl << " /*" << endl << " CONSTRAINT "; - base::create (fk); + create (fk); os << endl << " */"; } } virtual void - traverse (sema_rel::add_foreign_key& afk) + traverse_add (sema_rel::foreign_key& fk) { - if (afk.not_deferrable () || in_comment) - base::traverse (afk); + if (fk.not_deferrable () || in_comment) + base::traverse_add (fk); else { if (format_ != schema_format::sql) @@ -169,7 +169,7 @@ namespace relational << " /*" << endl; - add (afk); + add (fk); os << endl << " */"; @@ -184,37 +184,73 @@ namespace relational }; entry create_foreign_key_; - struct add_foreign_key: relational::add_foreign_key, context + struct create_table: relational::create_table, context { - add_foreign_key (base const& x): base (x) {} + create_table (base const& x): base (x) {} - virtual void - generate (sema_rel::table& t, sema_rel::foreign_key& fk) + // See if there are any undefined foreign keys that are not + // deferrable. + // + bool + check_undefined_fk_deferrable_only (sema_rel::table& t) { - // MySQL has no deferrable constraints. - // - if (fk.not_deferrable ()) - base::generate (t, fk); - else + for (sema_rel::table::names_iterator i (t.names_begin ()); + i != t.names_end (); ++i) { - if (format_ != schema_format::sql) - return; + using sema_rel::foreign_key; - os << "/*" << endl; - os << "ALTER TABLE " << quote_id (t.name ()) << endl - << " ADD CONSTRAINT "; - def_->create (fk); - os << endl - << "*/" << endl - << endl; + if (foreign_key* fk = dynamic_cast (&i->nameable ())) + { + if (!fk->count ("mysql-fk-defined") && + fk->not_deferrable ()) + return false; + } } + return true; } - }; - entry add_foreign_key_; - struct create_table: relational::create_table, context - { - create_table (base const& x): base (x) {} + virtual void + traverse (sema_rel::table& t) + { + if (pass_ == 1) + base::traverse (t); + else + { + // Add undefined foreign keys. + // + if (check_undefined_fk (t)) + { + bool deferrable (check_undefined_fk_deferrable_only (t)); + + if (!deferrable || format_ == schema_format::sql) + { + if (deferrable) + { + os << "/*" << endl; + in_comment = true; + } + else + pre_statement (); + + os << "ALTER TABLE " << quote_id (t.name ()); + + instance cfk (*this); + trav_rel::unames n (*cfk); + names (t, n); + os << endl; + + if (deferrable) + { + in_comment = false; + os << "*/" << endl + << endl; + } + else + post_statement (); + } + } + } + } virtual void create_post () diff --git a/odb/relational/oracle/schema.cxx b/odb/relational/oracle/schema.cxx index 5d0c107..3348d7d 100644 --- a/odb/relational/oracle/schema.cxx +++ b/odb/relational/oracle/schema.cxx @@ -242,11 +242,11 @@ namespace relational create_foreign_key (base const& x): base (x) {} virtual void - traverse (sema_rel::add_foreign_key& afk) + traverse_add (sema_rel::foreign_key& fk) { os << endl << " ADD CONSTRAINT "; - create (afk); + create (fk); } }; entry create_foreign_key_; diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index 146e526..7f1e1a0 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -634,7 +634,7 @@ namespace relational { typedef create_foreign_key base; - // Schema constructor. + // Schema constructor, pass 1. // create_foreign_key (common const& c, table_set& created, bool* first = 0) : common (c), @@ -644,7 +644,7 @@ namespace relational { } - // Migration constructor. + // Schema constructor, pass 2 and migration constructor. // create_foreign_key (common const& c, bool* first = 0) : common (c), @@ -667,20 +667,31 @@ namespace relational 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). - // - if (created_->find (fk.referenced_table ()) != created_->end ()) + if (created_ != 0) { - generate (fk); - fk.set (db.string () + "-fk-defined", true); // Mark it as defined. + // Pass 1. + // + // 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. + // + if (created_->find (fk.referenced_table ()) != created_->end ()) + { + traverse_create (fk); + fk.set (db.string () + "-fk-defined", true); // Mark it as defined. + } + } + else + { + // Pass 2. + // + if (!fk.count (db.string () + "-fk-defined")) + traverse_add (fk); } } virtual void - generate (sema_rel::foreign_key& fk) + traverse_create (sema_rel::foreign_key& fk) { if (first_) first_ = false; @@ -693,13 +704,7 @@ namespace relational } virtual void - add_header () - { - os << "ADD CONSTRAINT "; - } - - virtual void - traverse (sema_rel::add_foreign_key& afk) + traverse_add (sema_rel::foreign_key& fk) { if (first_) first_ = false; @@ -707,15 +712,27 @@ namespace relational os << ","; os << endl; - add (afk); + add (fk); } virtual void - add (sema_rel::add_foreign_key& afk) + traverse (sema_rel::add_foreign_key& afk) + { + traverse_add (afk); + } + + virtual void + add_header () + { + os << "ADD CONSTRAINT "; + } + + virtual void + add (sema_rel::foreign_key& fk) { os << " "; add_header (); - create (afk); + create (fk); } virtual void @@ -807,59 +824,6 @@ namespace relational 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 (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 def_; - }; - struct create_index: trav_rel::index, common { typedef create_index base; @@ -1006,6 +970,22 @@ namespace relational } } + // See if there are any undefined foreign keys that we need to + // add with ALTER TABLE. + // + bool + check_undefined_fk (sema_rel::table& t) + { + for (sema_rel::table::names_iterator i (t.names_begin ()); + i != t.names_end (); ++i) + { + if (i->nameable ().is_a () && + !i->nameable ().count (db.string () + "-fk-defined")) + return true; + } + return false; + } + virtual void traverse (sema_rel::table& t) { @@ -1023,11 +1003,20 @@ namespace relational } else { - // Add foreign keys. + // Add undefined foreign keys. // - instance fk (*this, created_); - trav_rel::unames n (*fk); - names (t, n); + if (check_undefined_fk (t)) + { + pre_statement (); + os << "ALTER TABLE " << quote_id (t.name ()); + + instance cfk (*this); + trav_rel::unames n (*cfk); + names (t, n); + os << endl; + + post_statement (); + } } } -- cgit v1.1