aboutsummaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-12-04 11:30:33 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-12-04 11:30:33 +0200
commit3417fc7c0df3b1b01750874587c4f3bb2ef02f45 (patch)
treea35f2439f7f72212d8ad05d3b8175e992d9a917f /odb
parenta0bf13c2522cc9c029af53f40970a3d0c19e178f (diff)
Implement on_delete pragma for object pointers
Translates to the ON DELETE SQL clause.
Diffstat (limited to 'odb')
-rw-r--r--odb/pragma.cxx42
-rw-r--r--odb/relational/mssql/schema.cxx19
-rw-r--r--odb/relational/mysql/schema.cxx18
-rw-r--r--odb/relational/schema.hxx10
-rw-r--r--odb/relational/validator.cxx38
-rw-r--r--odb/semantics/relational/foreign-key.cxx4
-rw-r--r--odb/semantics/relational/foreign-key.hxx3
-rw-r--r--odb/validator.cxx25
8 files changed, 153 insertions, 6 deletions
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 0c8881f..89cfd50 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -386,6 +386,7 @@ check_spec_decl_type (declaration const& d,
p == "auto" ||
p == "column" ||
p == "inverse" ||
+ p == "on_delete" ||
p == "section" ||
p == "load" ||
p == "update" ||
@@ -2151,6 +2152,39 @@ handle_pragma (cxx_lexer& l,
tt = l.next (tl, &tn);
}
+ else if (p == "on_delete")
+ {
+ // on_delete (cascade|set_null)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NAME || (tl != "cascade" && tl != "set_null"))
+ {
+ error (l) << "cascade or set_null expected after '('" << endl;
+ return;
+ }
+
+ using semantics::relational::foreign_key;
+ val = (tl == "cascade" ? foreign_key::cascade : foreign_key::set_null);
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
else if (p == "section")
{
// section (name)
@@ -3255,6 +3289,7 @@ handle_pragma_qualifier (cxx_lexer& l, string p)
p == "load" ||
p == "update" ||
p == "inverse" ||
+ p == "on_delete" ||
p == "unordered" ||
p == "readonly" ||
p == "transient" ||
@@ -3625,6 +3660,12 @@ handle_pragma_db_inverse (cpp_reader* r)
}
extern "C" void
+handle_pragma_db_on_delete (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "on_delete");
+}
+
+extern "C" void
handle_pragma_db_unordered (cpp_reader* r)
{
handle_pragma_qualifier (r, "unordered");
@@ -3738,6 +3779,7 @@ register_odb_pragmas (void*, void*)
c_register_pragma_with_expansion ("db", "load", handle_pragma_db_load);
c_register_pragma_with_expansion ("db", "update", handle_pragma_db_update);
c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
+ c_register_pragma_with_expansion ("db", "on_delete", handle_pragma_db_on_delete);
c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered);
c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly);
c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient);
diff --git a/odb/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx
index 2480e75..7b6c7ca 100644
--- a/odb/relational/mssql/schema.cxx
+++ b/odb/relational/mssql/schema.cxx
@@ -237,6 +237,20 @@ namespace relational
{
create_foreign_key (base const& x): base (x) {}
+ void
+ diagnose (sema_rel::foreign_key& fk)
+ {
+ if (fk.on_delete () != sema_rel::foreign_key::no_action)
+ {
+ cerr << "warning: foreign key '" << fk.name () << "' has " <<
+ "ON DELETE clause but is disabled in SQL Server due to lack "
+ "of deferrable constraint support" << endl;
+
+ cerr << "info: consider using non-deferrable foreign keys (" <<
+ "--fkeys-deferrable-mode)" << endl;
+ }
+ }
+
virtual void
traverse_create (sema_rel::foreign_key& fk)
{
@@ -248,6 +262,8 @@ namespace relational
base::traverse_create (fk);
else
{
+ diagnose (fk);
+
// Don't bloat C++ code with comment strings if we are
// generating embedded schema.
//
@@ -268,6 +284,9 @@ namespace relational
{
bool c (!fk.not_deferrable () && !in_comment);
+ if (c)
+ diagnose (fk);
+
if (c && format_ != schema_format::sql)
return;
diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx
index f06914a..473e746 100644
--- a/odb/relational/mysql/schema.cxx
+++ b/odb/relational/mysql/schema.cxx
@@ -145,6 +145,20 @@ namespace relational
{
create_foreign_key (base const& x): base (x) {}
+ void
+ diagnose (sema_rel::foreign_key& fk)
+ {
+ if (fk.on_delete () != sema_rel::foreign_key::no_action)
+ {
+ cerr << "warning: foreign key '" << fk.name () << "' has " <<
+ "ON DELETE clause but is disabled in MySQL due to lack "
+ "of deferrable constraint support" << endl;
+
+ cerr << "info: consider using non-deferrable foreign keys (" <<
+ "--fkeys-deferrable-mode)" << endl;
+ }
+ }
+
virtual void
traverse_create (sema_rel::foreign_key& fk)
{
@@ -156,6 +170,8 @@ namespace relational
base::traverse_create (fk);
else
{
+ diagnose (fk);
+
// Don't bloat C++ code with comment strings if we are
// generating embedded schema.
//
@@ -178,6 +194,8 @@ namespace relational
base::traverse_add (fk);
else
{
+ diagnose (fk);
+
if (format_ != schema_format::sql)
return;
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
index 0de7036..4903e3a 100644
--- a/odb/relational/schema.hxx
+++ b/odb/relational/schema.hxx
@@ -857,14 +857,20 @@ namespace relational
switch (a)
{
+ case foreign_key::no_action:
+ break;
case foreign_key::cascade:
{
os << endl
<< " ON DELETE CASCADE";
break;
}
- case foreign_key::no_action:
- break;
+ case foreign_key::set_null:
+ {
+ os << endl
+ << " ON DELETE SET NULL";
+ break;
+ }
}
}
diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx
index c620a58..3218b57 100644
--- a/odb/relational/validator.cxx
+++ b/odb/relational/validator.cxx
@@ -51,6 +51,44 @@ namespace relational
}
}
}
+
+ // Check on-delete.
+ //
+ if (m.count ("on-delete"))
+ {
+ const char* kp (container (m) ? "value" : "");
+ location l (m.location ());
+
+ // Make sure it is a pointer.
+ //
+ if (!object_pointer (member_utype (m, kp)))
+ {
+ error (l) << "on_delete specified for non-object pointer" << endl;
+ valid_ = false;
+ }
+
+ // Make sure it is not inverse.
+ //
+ if (inverse (m, kp))
+ {
+ error (l) << "on_delete specified for inverse object " <<
+ "pointer" << endl;
+ valid_ = false;
+ }
+
+ // Make sure the pointer is nullable if asked to set it to NULL.
+ //
+ using sema_rel::foreign_key;
+
+ if (m.get<foreign_key::action_type> ("on-delete") ==
+ foreign_key::set_null &&
+ !null (m, kp))
+ {
+ error (l) << "set_null specified for non-nullable object "
+ "pointer" << endl;
+ valid_ = false;
+ }
+ }
}
bool& valid_;
diff --git a/odb/semantics/relational/foreign-key.cxx b/odb/semantics/relational/foreign-key.cxx
index 8188e1f..59b401b 100644
--- a/odb/semantics/relational/foreign-key.cxx
+++ b/odb/semantics/relational/foreign-key.cxx
@@ -15,7 +15,7 @@ namespace semantics
{
namespace relational
{
- static const char* action_str[] = {"NO ACTION", "CASCADE"};
+ static const char* action_str[] = {"NO ACTION", "CASCADE", "SET NULL"};
ostream&
operator<< (ostream& os, foreign_key::action_type v)
@@ -38,6 +38,8 @@ namespace semantics
v = foreign_key::no_action;
else if (s == "CASCADE")
v = foreign_key::cascade;
+ else if (s == "SET NULL")
+ v = foreign_key::set_null;
else
is.setstate (istream::failbit);
}
diff --git a/odb/semantics/relational/foreign-key.hxx b/odb/semantics/relational/foreign-key.hxx
index 458683f..859b940 100644
--- a/odb/semantics/relational/foreign-key.hxx
+++ b/odb/semantics/relational/foreign-key.hxx
@@ -53,7 +53,8 @@ namespace semantics
enum action_type
{
no_action,
- cascade
+ cascade,
+ set_null
};
action_type
diff --git a/odb/validator.cxx b/odb/validator.cxx
index 172ca6d..f8f7408 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -1027,6 +1027,18 @@ namespace
// Pass 2.
//
+ struct data_member2: traversal::data_member, context
+ {
+ data_member2 (bool& valid): valid_ (valid) {}
+
+ virtual void
+ traverse (type&)
+ {
+ }
+
+ bool& valid_;
+ };
+
// Make sure soft-delete versions make sense for dependent entities.
// We don't seem to need anything for soft-add since if an entity is
// not added (e.g., an object), then we cannot reference it in the
@@ -1191,7 +1203,7 @@ namespace
struct class2: traversal::class_, context
{
class2 (bool& valid)
- : valid_ (valid), has_lt_operator_ (0), typedefs_ (true)
+ : valid_ (valid), has_lt_operator_ (0), typedefs_ (true), member_ (valid)
{
// Find the has_lt_operator function template.
//
@@ -1232,6 +1244,8 @@ namespace
*this >> defines_ >> *this;
*this >> typedefs_ >> *this;
+
+ names_member_ >> member_;
}
virtual void
@@ -1249,9 +1263,13 @@ namespace
case class_object: traverse_object (c); break;
case class_view: traverse_view (c); break;
case class_composite: traverse_composite (c); break;
- default: break;
+ default: return;
}
+ // Check members.
+ //
+ names (c, names_member_);
+
// Check version dependencies.
//
{
@@ -1440,6 +1458,9 @@ namespace
traversal::defines defines_;
typedefs typedefs_;
+
+ data_member1 member_;
+ traversal::names names_member_;
};
}