aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-07-17 09:10:03 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-07-17 09:10:03 +0200
commit8101ed727f48216e887183dc3c7b5d96e37c2650 (patch)
treeee81327eb02fe22ea9f61af1085b1314d9524354
parent2a2a9d9efb9209d3b251b62bf347df1dbb8dfae5 (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.
-rw-r--r--odb/relational/mssql/schema.cxx76
-rw-r--r--odb/relational/schema.hxx46
-rw-r--r--odb/semantics/relational/table.hxx5
3 files changed, 113 insertions, 14 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.
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
index b0ac01f..4f31dca 100644
--- a/odb/relational/schema.hxx
+++ b/odb/relational/schema.hxx
@@ -690,6 +690,13 @@ namespace relational
empty_ = true;
pass_ = p;
new_pass_ = true;
+
+ if (pass_ == 1)
+ empty_passes_ = 0; // New set of passes.
+
+ // Assume this pass is empty.
+ //
+ empty_passes_++;
}
// Did this pass produce anything?
@@ -716,28 +723,44 @@ namespace relational
{
first_ = false;
- // If this line starts a new pass, then output the
- // switch/case blocks.
+ // If this line starts a new pass, then output the switch/case
+ // blocks.
//
if (new_pass_)
{
new_pass_ = false;
empty_ = false;
+ empty_passes_--; // This pass is not empty.
- if (pass_ == 1)
+ // Output case statements for empty preceeding passes, if any.
+ //
+ if (empty_passes_ != 0)
{
+ unsigned short s (pass_ - empty_passes_);
+
+ if (s == 1)
+ os << "switch (pass)"
+ << "{";
+ else
+ os << "return true;" // One more pass.
+ << "}";
+
+ for (; s != pass_; ++s)
+ os << "case " << s << ":" << endl;
+
+ os << "{";
+ empty_passes_ = 0;
+ }
+
+ if (pass_ == 1)
os << "switch (pass)"
- << "{"
- << "case 1:" << endl
<< "{";
- }
else
- {
os << "return true;" // One more pass.
- << "}"
- << "case " << pass_ << ":" << endl
- << "{";
- }
+ << "}";
+
+ os << "case " << pass_ << ":" << endl
+ << "{";
}
os << "db.execute (";
@@ -761,6 +784,7 @@ namespace relational
bool empty_;
bool new_pass_;
unsigned short pass_;
+ unsigned short empty_passes_; // Number of preceding empty passes.
};
struct cxx_object: virtual context
diff --git a/odb/semantics/relational/table.hxx b/odb/semantics/relational/table.hxx
index d499ccc..49476bb 100644
--- a/odb/semantics/relational/table.hxx
+++ b/odb/semantics/relational/table.hxx
@@ -13,6 +13,11 @@ namespace semantics
{
class table: public qnameable, public uscope
{
+ public:
+ // Resolve ambiguity.
+ //
+ using qnameable::scope;
+
protected:
table (string const& id)
: qnameable (id)