aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-09-15 12:46:09 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-09-15 12:46:09 +0200
commit51956f409ec7ebea8b6790b0c5d4f0b51513d683 (patch)
tree66f30e68fa3c8080d0d3d8d50c30e11914fc82b9
parentd9ea777539b166c7347c44a6b7784626405bc029 (diff)
Cleanup polymorphic base tables when dropping derived one
-rw-r--r--odb/relational/model.hxx12
-rw-r--r--odb/relational/mssql/schema.cxx8
-rw-r--r--odb/relational/oracle/schema.cxx29
-rw-r--r--odb/relational/pgsql/schema.cxx11
-rw-r--r--odb/relational/schema.hxx81
-rw-r--r--odb/relational/sqlite/schema.cxx43
-rw-r--r--odb/semantics/relational/primary-key.hxx2
-rw-r--r--odb/semantics/relational/table.cxx17
-rw-r--r--odb/semantics/relational/table.hxx12
9 files changed, 196 insertions, 19 deletions
diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx
index 7b5fb78..325e614 100644
--- a/odb/relational/model.hxx
+++ b/odb/relational/model.hxx
@@ -536,6 +536,7 @@ namespace relational
model_.new_edge<sema_rel::qnames> (model_, t, name);
t.options (table_options (m, ct));
+ t.extra ()["kind"] = "container";
// object_id
//
@@ -545,7 +546,9 @@ namespace relational
oc->traverse (m, container_idt (m), "id", "object_id");
}
- // Foreign key and index for the object id.
+ // Foreign key and index for the object id. Keep this foreign
+ // key first since we reply on this information to lookup the
+ // corresponding object table.
//
{
// Derive the name prefix. See the comment for the other foreign
@@ -734,7 +737,9 @@ namespace relational
if (!object (c))
return;
- if (abstract (c) && !polymorphic (c))
+ semantics::class_* poly (polymorphic (c));
+
+ if (abstract (c) && poly == 0)
return;
qname const& name (table_name (c));
@@ -761,6 +766,9 @@ namespace relational
t.options (table_options (c));
+ if (poly != 0 && poly != &c)
+ t.extra ()["kind"] = "polymorphic";
+
// Add columns.
//
{
diff --git a/odb/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx
index 18c1f87..2480e75 100644
--- a/odb/relational/mssql/schema.cxx
+++ b/odb/relational/mssql/schema.cxx
@@ -183,20 +183,22 @@ namespace relational
drop_table (base const& x): base (x) {}
virtual void
- drop (sema_rel::qname const& table, bool migration)
+ drop (sema_rel::table& t, bool migration)
{
// 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.
//
+ sema_rel::qname const& name (t.name ());
+
pre_statement ();
if (!migration)
- os << "IF OBJECT_ID(" << quote_string (table.string ()) <<
+ os << "IF OBJECT_ID(" << quote_string (name.string ()) <<
", " << quote_string ("U") << ") IS NOT NULL" << endl
<< " ";
- os << "DROP TABLE " << quote_id (table) << endl;
+ os << "DROP TABLE " << quote_id (name) << endl;
post_statement ();
}
diff --git a/odb/relational/oracle/schema.cxx b/odb/relational/oracle/schema.cxx
index 5856dd2..829a37a 100644
--- a/odb/relational/oracle/schema.cxx
+++ b/odb/relational/oracle/schema.cxx
@@ -140,13 +140,8 @@ namespace relational
drop_table (base const& x): base (x) {}
virtual void
- traverse (sema_rel::table& t, bool migration)
+ drop (sema_rel::table& t, bool migration)
{
- // For Oracle we use the CASCADE clause to drop foreign keys.
- //
- if (pass_ != 2)
- return;
-
using sema_rel::primary_key;
sema_rel::table::names_iterator i (t.find ("")); // Special name.
@@ -162,7 +157,7 @@ namespace relational
if (migration)
{
pre_statement ();
- os << "DROP TABLE " << qt << " CASCADE CONSTRAINTS" << endl;
+ os << "DROP TABLE " << qt << endl;
post_statement ();
// Drop the sequence if we have auto primary key.
@@ -207,6 +202,26 @@ namespace relational
post_statement ();
}
}
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // For migration drop foreign keys explicitly in pre-migration.
+ //
+ if (migration)
+ {
+ base::traverse (t, migration);
+ return;
+ }
+
+ // For schema creation we use the CASCADE clause to drop foreign
+ // keys.
+ //
+ if (pass_ != 2)
+ return;
+
+ drop (t, migration);
+ }
};
entry<drop_table> drop_table_;
diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx
index f127b95..1cf57f1 100644
--- a/odb/relational/pgsql/schema.cxx
+++ b/odb/relational/pgsql/schema.cxx
@@ -29,7 +29,16 @@ namespace relational
virtual void
traverse (sema_rel::table& t, bool migration)
{
- // For PostgreSQL we use the CASCADE clause to drop foreign keys.
+ // For migration drop foreign keys explicitly in pre-migration.
+ //
+ if (migration)
+ {
+ base::traverse (t, migration);
+ return;
+ }
+
+ // For schema creation we use the CASCADE clause to drop foreign
+ // keys.
//
if (pass_ != 2)
return;
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
index 85dca09..3edb6b2 100644
--- a/odb/relational/schema.hxx
+++ b/odb/relational/schema.hxx
@@ -311,11 +311,42 @@ namespace relational
: common (e, os, f) {}
virtual void
- drop (sema_rel::qname const& table, bool migration)
+ drop (sema_rel::table& t, bool migration)
{
pre_statement ();
os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
- quote_id (table) << endl;
+ quote_id (t.name ()) << endl;
+ post_statement ();
+ }
+
+ virtual void
+ delete_ (sema_rel::qname const& rtable,
+ sema_rel::qname const& dtable,
+ sema_rel::primary_key& rkey,
+ sema_rel::primary_key& dkey)
+ {
+ pre_statement ();
+
+ // This might not be the most efficient way for every database.
+ //
+ os << "DELETE FROM " << quote_id (rtable) << endl
+ << " WHERE EXISTS (SELECT 1 FROM " << quote_id (dtable) << endl
+ << " WHERE ";
+
+ for (size_t i (0); i != rkey.contains_size (); ++i)
+ {
+ if (i != 0)
+ os << endl
+ << " AND ";
+
+ os << quote_id (rtable) << "." <<
+ quote_id (rkey.contains_at (i).column ().name ()) << " = " <<
+ quote_id (dtable) << "." <<
+ quote_id (dkey.contains_at (i).column ().name ());
+ }
+
+ os << ")" << endl;
+
post_statement ();
}
@@ -345,7 +376,51 @@ namespace relational
}
}
else
- drop (t.name (), migration);
+ {
+ if (migration &&
+ t.extra ().count ("kind") != 0 &&
+ t.extra ()["kind"] == "polymorphic")
+ {
+ // If we are dropping a polymorphic derived object, then we
+ // also have to clean the base tables. Note that this won't
+ // trigger cascade deletion since we have dropped all the
+ // keys on pass 1. But we still need to do this in the base
+ // to root order in order not to trigger other cascades.
+ //
+ using sema_rel::model;
+ using sema_rel::table;
+ using sema_rel::primary_key;
+ using sema_rel::foreign_key;
+
+ model& m (dynamic_cast<model&> (t.scope ()));
+
+ table* p (&t);
+ do
+ {
+ // The polymorphic link is the first primary key.
+ //
+ for (table::names_iterator i (p->names_begin ());
+ i != p->names_end (); ++i)
+ {
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (
+ &i->nameable ()))
+ {
+ p = m.find<table> (fk->referenced_table ());
+ assert (p != 0); // Base table should be there.
+ break;
+ }
+ }
+
+ primary_key& rkey (*p->find<primary_key> (""));
+ primary_key& dkey (*t.find<primary_key> (""));
+ assert (rkey.contains_size () == dkey.contains_size ());
+ delete_ (p->name (), t.name (), rkey, dkey);
+ }
+ while (p->extra ().count ("kind") != 0);
+ }
+
+ drop (t, migration);
+ }
}
virtual void
diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx
index c4d852d..24bc2ef 100644
--- a/odb/relational/sqlite/schema.cxx
+++ b/odb/relational/sqlite/schema.cxx
@@ -49,7 +49,48 @@ namespace relational
if (pass_ != 2)
return;
- drop (t.name (), migration);
+ // Polymorphic base cleanup code. Because we cannot drop foreign
+ // keys, we will trigger cascade deletion. The only way to work
+ // around this problem is to delete from the root table and rely
+ // on the cascade to clean up the rest.
+ //
+ if (migration &&
+ t.extra ().count ("kind") != 0 &&
+ t.extra ()["kind"] == "polymorphic")
+ {
+ using sema_rel::model;
+ using sema_rel::table;
+ using sema_rel::primary_key;
+ using sema_rel::foreign_key;
+
+ model& m (dynamic_cast<model&> (t.scope ()));
+
+ table* p (&t);
+ do
+ {
+ // The polymorphic link is the first primary key.
+ //
+ for (table::names_iterator i (p->names_begin ());
+ i != p->names_end (); ++i)
+ {
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (
+ &i->nameable ()))
+ {
+ p = m.find<table> (fk->referenced_table ());
+ assert (p != 0); // Base table should be there.
+ break;
+ }
+ }
+ }
+ while (p->extra ().count ("kind") != 0);
+
+ primary_key& rkey (*p->find<primary_key> (""));
+ primary_key& dkey (*t.find<primary_key> (""));
+ assert (rkey.contains_size () == dkey.contains_size ());
+ delete_ (p->name (), t.name (), rkey, dkey);
+ }
+
+ drop (t, migration);
}
};
entry<drop_table> drop_table_;
diff --git a/odb/semantics/relational/primary-key.hxx b/odb/semantics/relational/primary-key.hxx
index 0f28966..e41ffc3 100644
--- a/odb/semantics/relational/primary-key.hxx
+++ b/odb/semantics/relational/primary-key.hxx
@@ -20,7 +20,7 @@ namespace semantics
bool
auto_ () const {return auto__;}
- // Extra database information.
+ // Extra information.
//
public:
typedef std::map<string, string> extra_map;
diff --git a/odb/semantics/relational/table.cxx b/odb/semantics/relational/table.cxx
index 80add9f..8346d4c 100644
--- a/odb/semantics/relational/table.cxx
+++ b/odb/semantics/relational/table.cxx
@@ -16,7 +16,8 @@ namespace semantics
table (table const& t, qscope& s, graph& g, bool b)
: qnameable (t, g),
uscope (t, (b ? s.lookup<table, drop_table> (t.name ()) : 0), g),
- options_ (t.options_)
+ options_ (t.options_),
+ extra_map_ (t.extra_map_)
{
}
@@ -30,6 +31,16 @@ namespace semantics
g),
options_ (p.attribute ("options", string ()))
{
+ // All unhandled attributes go into the extra map.
+ //
+ typedef xml::parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
+ {
+ if (!i->second.handled)
+ extra_map_[i->first.name ()] = i->second.value;
+ }
}
table& table::
@@ -45,6 +56,10 @@ namespace semantics
if (!options_.empty ())
s.attribute ("options", options_);
+
+ for (extra_map::const_iterator i (extra_map_.begin ());
+ i != extra_map_.end (); ++i)
+ s.attribute (i->first, i->second);
}
void table::
diff --git a/odb/semantics/relational/table.hxx b/odb/semantics/relational/table.hxx
index f46eec1..99c65b7 100644
--- a/odb/semantics/relational/table.hxx
+++ b/odb/semantics/relational/table.hxx
@@ -20,6 +20,17 @@ namespace semantics
virtual void
options (string const& o) {options_ = o;}
+ // Extra information.
+ //
+ public:
+ typedef std::map<string, string> extra_map;
+
+ extra_map&
+ extra () {return extra_map_;}
+
+ extra_map const&
+ extra () const {return extra_map_;}
+
public:
table (string const& id): qnameable (id) {}
table (table const&, qscope&, graph&, bool base = false);
@@ -44,6 +55,7 @@ namespace semantics
protected:
string options_;
+ extra_map extra_map_;
};
class add_table: public table