From 3417fc7c0df3b1b01750874587c4f3bb2ef02f45 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 4 Dec 2013 11:30:33 +0200 Subject: Implement on_delete pragma for object pointers Translates to the ON DELETE SQL clause. --- odb/pragma.cxx | 42 ++++++++++++++++++++++++++++++++ odb/relational/mssql/schema.cxx | 19 +++++++++++++++ odb/relational/mysql/schema.cxx | 18 ++++++++++++++ odb/relational/schema.hxx | 10 ++++++-- odb/relational/validator.cxx | 38 +++++++++++++++++++++++++++++ odb/semantics/relational/foreign-key.cxx | 4 ++- odb/semantics/relational/foreign-key.hxx | 3 ++- odb/validator.cxx | 25 +++++++++++++++++-- 8 files changed, 153 insertions(+), 6 deletions(-) (limited to 'odb') 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 ("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_; }; } -- cgit v1.1