From c4b6357a9908fb05261efa8764e5c8a6d8727b96 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 16 Aug 2013 10:57:32 +0200 Subject: Implement soft delete for persistent classes --- odb/context.hxx | 14 +++++++++++ odb/pragma.cxx | 64 +++++++++++++++++++++++++++++++++++++++++++++--- odb/relational/model.hxx | 3 +++ odb/validator.cxx | 32 ++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/odb/context.hxx b/odb/context.hxx index 862b74e..11a6713 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -726,6 +726,20 @@ public: return m.count ("transient"); } + // Return the deletion version or 0 if not deleted. + // + static unsigned long long + deleted (semantics::class_& c) + { + return c.get ("deleted", 0); + } + + static unsigned long long + deleted (semantics::data_member& m) + { + return m.get ("deleted", 0); + } + static bool id (semantics::data_member& m) { diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 2b4dc3d..8e55530 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -415,6 +415,17 @@ check_spec_decl_type (declaration const& d, return false; } } + else if (p == "deleted") + { + // Deleted can be used for both data members and classes (object). + // + if (tc != FIELD_DECL && tc != RECORD_TYPE) + { + error (l) << "name '" << name << "' in db pragma " << p << " does " + << "not refer to a data member or class" << endl; + return false; + } + } else if (p == "readonly") { // Readonly can be used for both data members and classes (object or @@ -2279,6 +2290,45 @@ handle_pragma (cxx_lexer& l, tt = l.next (tl, &tn); } + else if (p == "deleted") + { + // deleted (unsigned long long version) + // + + // 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_NUMBER || TREE_CODE (tn) != INTEGER_CST) + { + error (l) << "unsigned integer expected as deletion version" << endl; + return; + } + + unsigned long long v (integer (tn)); + + if (v == 0) + { + error (l) << "deletion version cannot be zero" << endl; + return; + } + + if (l.next (tl, &tn) != CPP_CLOSE_PAREN) + { + error (l) << "')' expected at the end of db pragma " << p << endl; + return; + } + + val = v; + tt = l.next (tl, &tn); + } else if (p == "version") { // version @@ -3193,6 +3243,7 @@ handle_pragma_qualifier (cxx_lexer& l, string p) p == "unordered" || p == "readonly" || p == "transient" || + p == "deleted" || p == "version" || p == "virtual") { @@ -3299,9 +3350,9 @@ handle_pragma_qualifier (cxx_lexer& l, string p) // // Should this class be considered composite value type in other // databases (because that's what would happen by default)? Probably - // not. So to overcome this we are going detect and ignore cases where - // (a) some specifiers followed the value qualifier but (b) none of - // them are for the database that we are compiling. + // not. So to overcome this we are going to detect and ignore cases + // where (a) some specifiers followed the value qualifier but (b) + // none of them are for the database that we are compiling for. // if (p == "value") { @@ -3576,6 +3627,12 @@ handle_pragma_db_transient (cpp_reader* r) } extern "C" void +handle_pragma_db_deleted (cpp_reader* r) +{ + handle_pragma_qualifier (r, "deleted"); +} + +extern "C" void handle_pragma_db_version (cpp_reader* r) { handle_pragma_qualifier (r, "version"); @@ -3662,6 +3719,7 @@ register_odb_pragmas (void*, void*) 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); + c_register_pragma_with_expansion ("db", "deleted", handle_pragma_db_deleted); c_register_pragma_with_expansion ("db", "version", handle_pragma_db_version); c_register_pragma_with_expansion ("db", "virtual", handle_pragma_db_virtual); */ diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index af88eb5..d4e1792 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -709,6 +709,9 @@ namespace relational if (abstract (c) && !polymorphic (c)) return; + if (deleted (c)) + return; + qname const& name (table_name (c)); // If the table with this name was already created, assume the diff --git a/odb/validator.cxx b/odb/validator.cxx index 080aaa4..2b0b46e 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -326,6 +326,38 @@ namespace virtual void traverse_object (type& c) { + // Check the the deletion version makes sense. + // + if (unsigned long long v = deleted (c)) + { + location_t l (c.get ("deleted-location")); + + if (!versioned ()) + { + error (l) << "deleted member in non-versioned object model" << endl; + valid_ = false; + } + else + { + model_version const& mv (version ()); + + if (v > mv.current) + { + error (l) << "deletion version is greater than the current " << + "model version" << endl; + valid_ = false; + } + else if (v <= mv.base) + { + error (l) << "deletion version is less than or equal to the " << + "base model version" << endl; + info (c.location ()) << "delete this class since migration to " << + "version " << v << " is no longer possible" << endl; + valid_ = false; + } + } + } + // Check that the callback function exist. // if (c.count ("callback")) -- cgit v1.1