aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-08-28 07:52:50 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-08-28 07:52:50 +0200
commitc1f534db5d6bc29f9be0e7498e4971c7132d013a (patch)
tree198aef1cf6a2b84b929fbd54274875448f7e9a2e
parentc4b6357a9908fb05261efa8764e5c8a6d8727b96 (diff)
Support for added and deleted data member pragmas
-rw-r--r--odb/context.cxx18
-rw-r--r--odb/context.hxx54
-rw-r--r--odb/pragma.cxx31
-rw-r--r--odb/relational/model.hxx9
-rw-r--r--odb/relational/source.cxx102
-rw-r--r--odb/relational/source.hxx23
-rw-r--r--odb/validator.cxx170
7 files changed, 343 insertions, 64 deletions
diff --git a/odb/context.cxx b/odb/context.cxx
index a4609df..35433c9 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -2471,6 +2471,12 @@ namespace
if (m.count ("polymorphic-ref"))
return;
+ // Ignore added/deleted members if so requested.
+ //
+ if (((flags_ & exclude_added) != 0 && added (member_path_)) ||
+ ((flags_ & exclude_deleted) != 0 && deleted (member_path_)))
+ return;
+
if (context::is_a (member_path_, member_scope_, flags_))
r_++;
@@ -2480,6 +2486,12 @@ namespace
virtual void
traverse_simple (semantics::data_member&)
{
+ // Ignore added/deleted members if so requested.
+ //
+ if (((flags_ & exclude_added) != 0 && added (member_path_)) ||
+ ((flags_ & exclude_deleted) != 0 && deleted (member_path_)))
+ return;
+
if (context::is_a (member_path_, member_scope_, flags_))
r_++;
}
@@ -2487,6 +2499,12 @@ namespace
virtual void
traverse_container (semantics::data_member&, semantics::type& c)
{
+ // Ignore added/deleted members if so requested.
+ //
+ if (((flags_ & exclude_added) != 0 && added (member_path_)) ||
+ ((flags_ & exclude_deleted) != 0 && deleted (member_path_)))
+ return;
+
// We don't cross the container boundaries (separate table).
//
unsigned short f (flags_ & (context::test_container |
diff --git a/odb/context.hxx b/odb/context.hxx
index 11a6713..be612f7 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -726,7 +726,7 @@ public:
return m.count ("transient");
}
- // Return the deletion version or 0 if not deleted.
+ // Return the deletion version or 0 if not fort-deleted.
//
static unsigned long long
deleted (semantics::class_& c)
@@ -735,9 +735,41 @@ public:
}
static unsigned long long
- deleted (semantics::data_member& m)
+ deleted (data_member_path const& mp)
{
- return m.get<unsigned long long> ("deleted", 0);
+ unsigned long long r (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && v < r)
+ r = v;
+ }
+
+ return r;
+ }
+
+ // Return the addition version or 0 if not soft-added.
+ //
+ static unsigned long long
+ added (data_member_path const& mp)
+ {
+ unsigned long long r (0);
+
+ // Find the latest version since this member was added.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ r = v;
+ }
+
+ return r;
}
static bool
@@ -868,9 +900,12 @@ public:
static object_section&
section (data_member_path const& mp)
{
- // The direct member of the object specifies the section.
+ // The direct member of the object specifies the section. If the
+ // path is empty (which can happen, for example, for a container
+ // element), assume it is the main section.
+ //
//
- return section (*mp.front ());
+ return mp.empty () ? main_section : section (*mp.front ());
}
// Member belongs to a section that is loaded separately.
@@ -1250,7 +1285,12 @@ public:
// Treat eager loaded members as belonging to the main section.
// If this flag is specified, then section must be main_section.
//
- static unsigned short const include_eager_load = 0x2000;
+ static unsigned short const include_eager_load = 0x800;
+
+ // Exclude added/deleted members.
+ //
+ static unsigned short const exclude_added = 0x1000;
+ static unsigned short const exclude_deleted = 0x2000;
// By default the test goes into bases for non-polymorphic
// hierarchies and doesn't go for polymorphic. The following
@@ -1274,7 +1314,7 @@ public:
semantics::type&,
string const& key_prefix);
- // Return the number of matching entities. Can be uses as a just
+ // Return the number of matching entities. Can be used as a just
// a bool value (0 means no match).
//
size_t
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 8e55530..0c8881f 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -415,9 +415,21 @@ check_spec_decl_type (declaration const& d,
return false;
}
}
+ else if (p == "added")
+ {
+ // Added can be used for data members only.
+ //
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
else if (p == "deleted")
{
- // Deleted can be used for both data members and classes (object).
+ // Deleted can be used for both data members and classes (object,
+ // view of composite value).
//
if (tc != FIELD_DECL && tc != RECORD_TYPE)
{
@@ -2290,8 +2302,9 @@ handle_pragma (cxx_lexer& l,
tt = l.next (tl, &tn);
}
- else if (p == "deleted")
+ else if (p == "added" || p == "deleted")
{
+ // added (unsigned long long version)
// deleted (unsigned long long version)
//
@@ -2300,6 +2313,8 @@ handle_pragma (cxx_lexer& l,
if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
return;
+ char const* n (p == "added" ? "addition" : "deletion");
+
if (l.next (tl, &tn) != CPP_OPEN_PAREN)
{
error (l) << "'(' expected after db pragma " << p << endl;
@@ -2308,7 +2323,7 @@ handle_pragma (cxx_lexer& l,
if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
{
- error (l) << "unsigned integer expected as deletion version" << endl;
+ error (l) << "unsigned integer expected as " << n << " version" << endl;
return;
}
@@ -2316,7 +2331,7 @@ handle_pragma (cxx_lexer& l,
if (v == 0)
{
- error (l) << "deletion version cannot be zero" << endl;
+ error (l) << n << " version cannot be zero" << endl;
return;
}
@@ -3243,6 +3258,7 @@ handle_pragma_qualifier (cxx_lexer& l, string p)
p == "unordered" ||
p == "readonly" ||
p == "transient" ||
+ p == "added" ||
p == "deleted" ||
p == "version" ||
p == "virtual")
@@ -3627,6 +3643,12 @@ handle_pragma_db_transient (cpp_reader* r)
}
extern "C" void
+handle_pragma_db_added (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "added");
+}
+
+extern "C" void
handle_pragma_db_deleted (cpp_reader* r)
{
handle_pragma_qualifier (r, "deleted");
@@ -3719,6 +3741,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", "added", handle_pragma_db_added);
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 d4e1792..e345f83 100644
--- a/odb/relational/model.hxx
+++ b/odb/relational/model.hxx
@@ -102,6 +102,9 @@ namespace relational
virtual bool
traverse_column (semantics::data_member& m, string const& name, bool)
{
+ if (deleted (member_path_))
+ return false;
+
string col_id (id_prefix_ +
(key_prefix_.empty () ? m.name () : key_prefix_));
@@ -230,6 +233,9 @@ namespace relational
using sema_rel::column;
using sema_rel::foreign_key;
+ if (deleted (member_path_))
+ return;
+
// Ignore inverse object pointers.
//
if (inverse (m, key_prefix_))
@@ -488,6 +494,9 @@ namespace relational
using sema_rel::column;
+ if (deleted (member_path_))
+ return;
+
// Ignore inverse containers of object pointers.
//
if (inverse (m, "value"))
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index ba5a464..df804d2 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -49,6 +49,12 @@ traverse_object (type& c)
(opt ? context::grow (*opt) : false);
}
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
string const& type (class_fq_name (c));
string traits ("access::object_traits_impl< " + type + ", id_" +
db.string () + " >");
@@ -59,31 +65,6 @@ traverse_object (type& c)
? &poly_base->get<user_sections> ("user-sections")
: 0);
- // See what kind of containers we've got.
- //
- bool containers (has_a (c, test_container));
- bool straight_containers (false);
- bool smart_containers (false);
-
- // Eager load and update containers.
- //
- bool load_containers (false);
- bool update_containers (false);
-
- if (containers)
- {
- load_containers = has_a (
- c, test_container | include_eager_load, &main_section);
-
- if ((straight_containers = has_a (c, test_straight_container)))
- {
- // Only straight containers can be readwrite or smart.
- //
- smart_containers = has_a (c, test_smart_container);
- update_containers = has_a (c, test_readwrite_container, &main_section);
- }
- }
-
os << "// " << class_name (c) << endl
<< "//" << endl
<< endl;
@@ -103,6 +84,9 @@ traverse_object (type& c)
//
if (!reuse_abst && id != 0)
{
+ bool sections (false);
+ bool containers (has_a (c, test_container));
+
os << "struct " << traits << "::extra_statement_cache_type"
<< "{";
@@ -112,7 +96,6 @@ traverse_object (type& c)
if (containers)
os << endl;
- bool sections (false);
instance<section_cache_members> sm;
for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
{
@@ -170,7 +153,6 @@ traverse_object (type& c)
// Containers (abstract and concrete).
//
- if (containers)
{
instance<container_traits> t (c);
t->traverse (c);
@@ -1011,6 +993,8 @@ traverse_object (type& c)
// persist ()
//
+ bool persist_containers (has_a (c, test_straight_container));
+
os << "void " << traits << "::" << endl
<< "persist (database& db, " << (auto_id ? "" : "const ") <<
"object_type& obj";
@@ -1199,13 +1183,13 @@ traverse_object (type& c)
// Initialize id_image and binding if we are a root of a polymorphic
// hierarchy or if we have non-inverse containers.
//
- if (!poly_derived && (poly || straight_containers))
+ if (!poly_derived && (poly || persist_containers))
{
// If this is a polymorphic root without containers, then we only
// need to do this if we are not a top-level call. If we are poly-
// abstract, then top will always be false.
//
- if (poly && !straight_containers && !abst)
+ if (poly && !persist_containers && !abst)
os << "if (!top)"
<< "{";
@@ -1226,11 +1210,11 @@ traverse_object (type& c)
os << "sts.optimistic_id_image_binding ().version++;";
os << "}";
- if (poly && !straight_containers && !abst)
+ if (poly && !persist_containers && !abst)
os << "}";
}
- if (straight_containers)
+ if (persist_containers)
{
os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
<< endl;
@@ -1283,6 +1267,9 @@ traverse_object (type& c)
//
if (id != 0 && (!readonly || poly))
{
+ bool update_containers (
+ has_a (c, test_readwrite_container, &main_section));
+
// See if we have any sections that we might have to update.
//
bool sections (false);
@@ -1877,6 +1864,11 @@ traverse_object (type& c)
// erase (id)
//
+ size_t erase_containers (has_a (c, test_straight_container));
+ bool erase_versioned_containers (
+ erase_containers >
+ has_a (c, test_straight_container | exclude_deleted | exclude_added));
+
if (id != 0)
{
os << "void " << traits << "::" << endl
@@ -1963,10 +1955,15 @@ traverse_object (type& c)
// here since in case of a custom schema, it might not be
// there).
//
- if (straight_containers)
+ if (erase_containers)
{
- os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
- << endl;
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "db.schema_version_migration (" << schema_name << "));";
+
+ os << endl;
instance<container_calls> t (container_calls::erase_id_call);
t->traverse (c);
@@ -2000,7 +1997,9 @@ traverse_object (type& c)
// erase (object)
//
- if (id != 0 && (poly || opt != 0 || smart_containers))
+ bool erase_smart_containers (has_a (c, test_smart_container));
+
+ if (id != 0 && (poly || opt != 0 || erase_smart_containers))
{
os << "void " << traits << "::" << endl
<< "erase (database& db, const object_type& obj";
@@ -2038,7 +2037,7 @@ traverse_object (type& c)
<< "throw abstract_class ();"
<< endl;
- if (opt != 0 || smart_containers)
+ if (opt != 0 || erase_smart_containers)
{
string rsts (poly_derived ? "rsts" : "sts");
@@ -2067,7 +2066,7 @@ traverse_object (type& c)
<< endl;
}
- if (!abst || straight_containers)
+ if (!abst || erase_containers)
{
if (!id_ma->synthesized)
os << "// From " << location_string (id_ma->loc, true) << endl;
@@ -2113,11 +2112,16 @@ traverse_object (type& c)
// there).
//
- if (straight_containers)
+ if (erase_containers)
{
os << "extra_statement_cache_type& esc (" <<
- "sts.extra_statement_cache ());"
- << endl;
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "db.schema_version_migration (" << schema_name << "));";
+
+ os << endl;
instance<container_calls> t (container_calls::erase_obj_call);
t->traverse (c);
@@ -2168,7 +2172,7 @@ traverse_object (type& c)
// containers since it is more efficient than the find_() method
// below.
//
- if (poly_derived || (poly && straight_containers))
+ if (poly_derived || (poly && erase_containers))
{
// Only do the check in the top-level call.
//
@@ -2188,7 +2192,7 @@ traverse_object (type& c)
<< "}";
}
}
- else if (straight_containers)
+ else if (erase_containers)
{
// Things get complicated here: we don't want to trash the
// containers and then find out that the versions don't match
@@ -2228,11 +2232,16 @@ traverse_object (type& c)
// here since in case of a custom schema, it might not be
// there).
//
- if (straight_containers)
+ if (erase_containers)
{
os << "extra_statement_cache_type& esc (" <<
- "sts.extra_statement_cache ());"
- << endl;
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "db.schema_version_migration (" << schema_name << "));";
+
+ os << endl;
instance<container_calls> t (container_calls::erase_obj_call);
t->traverse (c);
@@ -3111,6 +3120,9 @@ traverse_object (type& c)
//
// Load containers, reset/reload sections.
//
+ bool load_containers (
+ has_a (c, test_container | include_eager_load, &main_section));
+
if (poly_derived ||
load_containers ||
uss.count (user_sections::count_new |
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index 9dbb4e9..3bf7c95 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -957,6 +957,9 @@ namespace relational
if (var_override_.empty ())
{
+ // Ignore inverse, separately-loaded members in the main
+ // section (nothing to persist).
+ //
if (section_ == 0 && separate_load (mi.m) && inverse (mi.m))
return false;
@@ -3635,6 +3638,26 @@ namespace relational
throw operation_failed ();
}
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 || dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
if (call_ != erase_id_call && (call_ != erase_obj_call || smart))
{
os << "{";
diff --git a/odb/validator.cxx b/odb/validator.cxx
index 2b0b46e..86fcf55 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -57,6 +57,7 @@ namespace
traverse (type& m)
{
semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+ bool obj (object (c));
// If the class is marked transient, then mark each non-virtual
// data member as transient.
@@ -74,7 +75,6 @@ namespace
return;
}
- count_++;
semantics::names* hint;
semantics::type& t (utype (m, hint));
@@ -115,7 +115,9 @@ namespace
// Make sure a member of a section is an immediate member of an object.
// The same for the section member itself.
//
- if (!object (c))
+ bool section (t.fq_name () == "::odb::section");
+
+ if (!obj)
{
if (m.count ("section-member"))
{
@@ -125,7 +127,7 @@ namespace
valid_ = false;
}
- if (t.fq_name () == "::odb::section")
+ if (section)
{
os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
"error: section data member can only be a direct member of a " <<
@@ -137,7 +139,7 @@ namespace
// Make sure the load and update pragmas are only specified on
// section members.
//
- if (t.fq_name () != "::odb::section")
+ if (!section)
{
if (m.count ("section-load"))
{
@@ -156,6 +158,130 @@ namespace
}
}
+ // Check that the addition version makes sense.
+ //
+ unsigned long long av (m.get<unsigned long long> ("added", 0));
+ if (av != 0)
+ {
+ location_t l (m.get<location_t> ("added-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ {
+ error (l) << "section cannod be soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "added data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (av > mv.current)
+ {
+ error (l) << "addition version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (av <= mv.base)
+ {
+ error (l) << "addition version is less than or equal to the " <<
+ "base model version" << endl;
+ info (l) << "delete this pragma since migration to version " <<
+ av << " is no longer possible" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check that the deletion version makes sense.
+ //
+ unsigned long long dv (m.get<unsigned long long> ("deleted", 0));
+ if (dv != 0)
+ {
+ location_t l (m.get<location_t> ("deleted-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ {
+ error (l) << "section cannod be soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "deleted data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (dv > mv.current)
+ {
+ error (l) << "deletion version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (dv <= mv.base)
+ {
+ error (l) << "deletion version is less than or equal to the " <<
+ "base model version" << endl;
+ info (c.location ()) << "delete this data member since " <<
+ "migration to version " << dv << " is no longer possible" <<
+ endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Make sure that addition and deletion versions are properly ordered.
+ // We can have both the [av, dv] (added then deleted) and [dv, av]
+ // (deleted then re-added) intervals.
+ //
+ if (av != 0 && dv != 0 && av == dv)
+ {
+ location_t al (m.get<location_t> ("added-location"));
+ location_t dl (m.get<location_t> ("deleted-location"));
+
+ error (al) << "addition and deletion versions are the same" << endl;
+ info (dl) << "deletion version is specified here" << endl;
+ valid_ = false;
+ }
+
+ if (dv == 0)
+ count_++; // Don't include deleted members in the count.
+
// Resolve null overrides.
//
override_null (m);
@@ -326,15 +452,15 @@ namespace
virtual void
traverse_object (type& c)
{
- // Check the the deletion version makes sense.
+ // Check that the deletion version makes sense.
//
- if (unsigned long long v = deleted (c))
+ if (unsigned long long v = c.get<unsigned long long> ("deleted", 0))
{
location_t l (c.get<location_t> ("deleted-location"));
if (!versioned ())
{
- error (l) << "deleted member in non-versioned object model" << endl;
+ error (l) << "deleted class in a non-versioned object model" << endl;
valid_ = false;
}
else
@@ -937,10 +1063,23 @@ namespace
// Pass 2.
//
+ struct data_member2: traversal::data_member, context
+ {
+ data_member2 (bool& valid): valid_ (valid) {}
+
+ virtual void
+ traverse (type&)
+ {
+ // Enable the names() calls below if adding any tests here.
+ }
+
+ bool& valid_;
+ };
+
struct class2: traversal::class_, context
{
class2 (bool& valid)
- : valid_ (valid), has_lt_operator_ (0)
+ : valid_ (valid), has_lt_operator_ (0), member_ (valid)
{
// Find the has_lt_operator function template.
//
@@ -978,6 +1117,8 @@ namespace
if (has_lt_operator_ == 0)
valid_ = false;
+
+ *this >> names_ >> member_;
}
virtual void
@@ -1117,20 +1258,33 @@ namespace
valid_ = false;
}
}
+
+ // Check members.
+ //
+ //names (c);
}
virtual void
traverse_view (type&)
{
+ // Check members.
+ //
+ //names (c);
}
virtual void
traverse_composite (type&)
{
+ // Check members.
+ //
+ //names (c);
}
bool& valid_;
tree has_lt_operator_;
+
+ data_member1 member_;
+ traversal::names names_;
};
}