diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2012-07-17 09:10:03 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2012-07-17 09:10:03 +0200 |
commit | 8101ed727f48216e887183dc3c7b5d96e37c2650 (patch) | |
tree | ee81327eb02fe22ea9f61af1085b1314d9524354 /odb/relational/mssql/schema.cxx | |
parent | 2a2a9d9efb9209d3b251b62bf347df1dbb8dfae5 (diff) |
Implement multi-pass table dropping for SQL Server
We have to first drop constraints before dropping tables in case the
tables are dropped in a wrong order or there are circular dependencies.
Diffstat (limited to 'odb/relational/mssql/schema.cxx')
-rw-r--r-- | odb/relational/mssql/schema.cxx | 76 |
1 files changed, 73 insertions, 3 deletions
diff --git a/odb/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx index 52d36ed..91bb891 100644 --- a/odb/relational/mssql/schema.cxx +++ b/odb/relational/mssql/schema.cxx @@ -45,18 +45,88 @@ namespace relational drop_table (base const& x): base (x) {} virtual void - drop (sema_rel::qname const& table) + traverse (sema_rel::table&); + + 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) + : common (dt.emitter (), dt.stream ()), dt_ (dt) + { + } + + virtual void + traverse (sema_rel::foreign_key& fk) + { + // Deferred constraints are not supported by SQL Server. + // + 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. + // + sema_rel::qname const& rt (fk.referenced_table ()); + sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ())); + 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 ()) + { + + // In SQL Server, foreign key names are schema-global. Make them + // unique by prefixing the key name with table name. Note, however, + // that they cannot have a schema. + // + string n (t.name ().uname () + "_" + fk.name ()); + + pre_statement (); + os << "IF OBJECT_ID(" << quote_string (n) << ", " << + quote_string ("F") << ") IS NOT NULL" << endl + << " ALTER TABLE " << quote_id (t.name ()) << " DROP" << endl + << " CONSTRAINT " << quote_id (n) << endl; + post_statement (); + } + } + + private: + drop_table& dt_; + }; + + void drop_table:: + traverse (sema_rel::table& t) + { + qname const& table (t.name ()); + + if (pass_ == 1) + { + // Drop constraints. + // + tables_.insert (table); // Add it before to cover self-refs. + drop_foreign_key fk (*this); + trav_rel::unames n (fk); + names (t, n); + } + else if (pass_ == 2) { // SQL Server has no IF EXISTS conditional for dropping table. // The following approach appears to be the recommended way to // drop a table if it exists. // + pre_statement (); os << "IF OBJECT_ID(" << quote_string (table.string ()) << ", " << quote_string ("U") << ") IS NOT NULL" << endl << " DROP TABLE " << quote_id (table) << endl; + post_statement (); } - }; - entry<drop_table> drop_table_; + } // // Create. |