aboutsummaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-05-06 12:05:39 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-08-14 15:19:49 +0200
commit09d7377f81aeb8fde4aa1698e946457f03380d45 (patch)
treeeaedf7045fde8354a3693ce77edc7d5f86824e4e /odb
parent548f0b10aa3adfc722198bf31f773ba85047f344 (diff)
Add support for object sections
Sections are an optimization mechanism that allows the partitioning of data members of a persistent class into groups that can be separately loaded and/or updated.
Diffstat (limited to 'odb')
-rw-r--r--odb/common.cxx48
-rw-r--r--odb/common.hxx38
-rw-r--r--odb/context.cxx247
-rw-r--r--odb/context.hxx305
-rw-r--r--odb/context.ixx27
-rw-r--r--odb/features.hxx1
-rw-r--r--odb/header.cxx44
-rw-r--r--odb/inline.cxx26
-rw-r--r--odb/pragma.cxx157
-rw-r--r--odb/processor.cxx372
-rw-r--r--odb/relational/common.hxx13
-rw-r--r--odb/relational/context.cxx2
-rw-r--r--odb/relational/context.hxx8
-rw-r--r--odb/relational/context.ixx4
-rw-r--r--odb/relational/header.cxx84
-rw-r--r--odb/relational/header.hxx198
-rw-r--r--odb/relational/inline.hxx89
-rw-r--r--odb/relational/mssql/header.cxx26
-rw-r--r--odb/relational/mssql/source.cxx51
-rw-r--r--odb/relational/mysql/context.cxx32
-rw-r--r--odb/relational/mysql/context.hxx2
-rw-r--r--odb/relational/oracle/source.cxx12
-rw-r--r--odb/relational/pgsql/context.cxx46
-rw-r--r--odb/relational/pgsql/context.hxx2
-rw-r--r--odb/relational/pgsql/header.cxx48
-rw-r--r--odb/relational/pgsql/source.cxx163
-rw-r--r--odb/relational/source.cxx1093
-rw-r--r--odb/relational/source.hxx1306
-rw-r--r--odb/relational/sqlite/context.cxx32
-rw-r--r--odb/relational/sqlite/context.hxx2
-rw-r--r--odb/validator.cxx156
31 files changed, 4211 insertions, 423 deletions
diff --git a/odb/common.cxx b/odb/common.cxx
index 01df033..365fe19 100644
--- a/odb/common.cxx
+++ b/odb/common.cxx
@@ -193,6 +193,14 @@ traverse_member (semantics::data_member& m, semantics::type& t)
traverse_simple (m);
}
+bool object_members_base::
+section_test (data_member_path const& mp)
+{
+ // By default ignore members from the wrong section.
+ //
+ return section_ == 0 || *section_ == section (mp);
+}
+
void object_members_base::member::
traverse (semantics::data_member& m)
{
@@ -201,14 +209,19 @@ traverse (semantics::data_member& m)
om_.member_path_.push_back (&m);
- semantics::type& t (utype (m));
+ // Ignore members from the wrong section.
+ //
+ if (om_.section_test (om_.member_path_))
+ {
+ semantics::type& t (utype (m));
- if (semantics::type* c = context::container (m))
- om_.traverse_container (m, *c);
- else if (semantics::class_* c = object_pointer (t))
- om_.traverse_pointer (m, *c);
- else
- om_.traverse_member (m, t);
+ if (semantics::type* c = context::container (m))
+ om_.traverse_container (m, *c);
+ else if (semantics::class_* c = object_pointer (t))
+ om_.traverse_pointer (m, *c);
+ else
+ om_.traverse_member (m, t);
+ }
om_.member_path_.pop_back ();
}
@@ -393,6 +406,14 @@ traverse_member (semantics::data_member& m, semantics::type& t)
}
}
+bool object_columns_base::
+section_test (data_member_path const& mp)
+{
+ // By default ignore members from the wrong section.
+ //
+ return section_ == 0 || *section_ == section (mp);
+}
+
void object_columns_base::member::
traverse (semantics::data_member& m)
{
@@ -406,12 +427,15 @@ traverse (semantics::data_member& m)
oc_.member_path_.push_back (&m);
- semantics::type& t (utype (m));
+ if (oc_.section_test (oc_.member_path_))
+ {
+ semantics::type& t (utype (m));
- if (semantics::class_* c = object_pointer (t))
- oc_.traverse_pointer (m, *c);
- else
- oc_.traverse_member (m, t);
+ if (semantics::class_* c = object_pointer (t))
+ oc_.traverse_pointer (m, *c);
+ else
+ oc_.traverse_member (m, t);
+ }
oc_.member_path_.pop_back ();
}
diff --git a/odb/common.hxx b/odb/common.hxx
index 5ca5107..c4ecbaf 100644
--- a/odb/common.hxx
+++ b/odb/common.hxx
@@ -67,9 +67,13 @@ struct object_members_base: traversal::class_, virtual context
virtual void
traverse_view (semantics::class_&);
+ virtual bool
+ section_test (data_member_path const&);
+
public:
- object_members_base (bool traverse_poly_base = false)
- : top_level_ (true), member_ (*this)
+ object_members_base (bool traverse_poly_base = false,
+ object_section* section = 0)
+ : section_ (section), top_level_ (true), member_ (*this)
{
init (false, false, false, traverse_poly_base);
}
@@ -77,8 +81,9 @@ public:
object_members_base (bool build_flat_prefix,
bool build_table_prefix,
bool build_member_prefix,
- bool traverse_poly_base = false)
- : top_level_ (true), member_ (*this)
+ bool traverse_poly_base = false,
+ object_section* section = 0)
+ : section_ (section), top_level_ (true), member_ (*this)
{
init (build_flat_prefix,
build_table_prefix,
@@ -88,6 +93,7 @@ public:
object_members_base (object_members_base const& x)
: context (), //@@ -Wextra
+ section_ (x.section_),
top_level_ (true),
member_ (*this)
{
@@ -108,6 +114,8 @@ protected:
data_member_path member_path_;
data_member_scope member_scope_;
+ object_section* section_;
+
protected:
semantics::data_member*
id () const
@@ -138,10 +146,7 @@ private:
struct member: traversal::data_member
{
- member (object_members_base& om)
- : om_ (om)
- {
- }
+ member (object_members_base& om): om_ (om) {}
virtual void
traverse (semantics::data_member&);
@@ -211,10 +216,15 @@ struct object_columns_base: traversal::class_, virtual context
virtual void
flush ();
+ virtual bool
+ section_test (data_member_path const&);
+
public:
object_columns_base (bool first = true,
- column_prefix const& cp = column_prefix ())
+ column_prefix const& cp = column_prefix (),
+ object_section* section = 0)
: column_prefix_ (cp),
+ section_ (section),
root_ (0),
traverse_poly_base_ (false),
first_ (first),
@@ -224,8 +234,11 @@ public:
init ();
}
- object_columns_base (bool first, bool traverse_poly_base)
- : root_ (0),
+ object_columns_base (bool first,
+ bool traverse_poly_base,
+ object_section* section = 0)
+ : section_ (section),
+ root_ (0),
traverse_poly_base_ (traverse_poly_base),
first_ (first),
top_level_ (true),
@@ -237,6 +250,7 @@ public:
object_columns_base (object_columns_base const& x)
: context (), //@@ -Wextra
column_prefix_ (x.column_prefix_),
+ section_ (x.section_),
root_ (0),
traverse_poly_base_ (x.traverse_poly_base_),
first_ (x.first_),
@@ -274,6 +288,8 @@ protected:
data_member_path member_path_;
data_member_scope member_scope_;
+ object_section* section_;
+
protected:
semantics::data_member*
id () const
diff --git a/odb/context.cxx b/odb/context.cxx
index 760d653..a4609df 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -291,6 +291,179 @@ translate (string const& obj, string const& val) const
return r;
}
+// Sections.
+//
+main_section_type main_section;
+
+bool main_section_type::
+compare (object_section const& s) const
+{
+ main_section_type const* ms (dynamic_cast<main_section_type const*> (&s));
+ return ms != 0 && *this == *ms;
+}
+
+bool user_section::
+compare (object_section const& s) const
+{
+ user_section const* us (dynamic_cast<user_section const*> (&s));
+ return us != 0 && *this == *us;
+}
+
+user_section* user_section::
+total_base () const
+{
+ if (base != 0)
+ {
+ semantics::class_* poly_root (context::polymorphic (*object));
+ if (poly_root != 0 && poly_root != *object)
+ return base;
+ }
+
+ return 0;
+}
+
+size_t user_sections::
+count (unsigned short f) const
+{
+ size_t r (0);
+
+ semantics::class_* poly_root (context::polymorphic (*object));
+ bool poly_derived (poly_root != 0 && poly_root != object);
+
+ if (poly_derived && (f & count_total) != 0)
+ r = context::polymorphic_base (*object).get<user_sections> (
+ "user-sections").count (f);
+
+ for (const_iterator i (begin ()); i != end (); ++i)
+ {
+ // Skip special sections unless we were explicitly asked to count them.
+ //
+ if (i->special == user_section::special_version &&
+ (f & count_special_version) == 0)
+ continue;
+
+ bool ovd (i->base != 0 && poly_derived);
+
+ if (i->load != user_section::load_eager)
+ {
+ if (i->load_empty ())
+ {
+ if ((f & count_load_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue; // Count each section only once.
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_load) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ if (i->update_empty ())
+ {
+ if ((f & count_update_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_update) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (i->optimistic ())
+ {
+ if ((f & count_optimistic) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ return r;
+}
+
//
// context
//
@@ -2181,6 +2354,11 @@ namespace
{
struct column_count_impl: object_members_base
{
+ column_count_impl (object_section* section = 0)
+ : object_members_base (false, section)
+ {
+ }
+
virtual void
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
@@ -2189,7 +2367,14 @@ namespace
object_members_base::traverse_pointer (m, c);
if (context::inverse (m))
- c_.inverse += (c_.total - t);
+ {
+ size_t n (c_.total - t);
+
+ c_.inverse += n;
+
+ if (separate_update (member_path_))
+ c_.separate_update -= n;
+ }
}
virtual void
@@ -2197,9 +2382,11 @@ namespace
{
c_.total++;
+ bool ro (context::readonly (member_path_, member_scope_));
+
if (id ())
c_.id++;
- else if (context::readonly (member_path_, member_scope_))
+ else if (ro)
c_.readonly++;
else if (context::version (m))
c_.optimistic_managed++;
@@ -2208,6 +2395,12 @@ namespace
//
if (discriminator (m))
c_.discriminator++;
+
+ if (separate_load (member_path_))
+ c_.separate_load++;
+
+ if (separate_update (member_path_) && !ro)
+ c_.separate_update++;
}
context::column_count_type c_;
@@ -2215,24 +2408,35 @@ namespace
}
context::column_count_type context::
-column_count (semantics::class_& c)
+column_count (semantics::class_& c, object_section* s)
{
- if (!c.count ("column-count"))
+ if (s == 0)
{
- column_count_impl t;
+ // Whole class.
+ //
+ if (!c.count ("column-count"))
+ {
+ column_count_impl t;
+ t.traverse (c);
+ c.set ("column-count", t.c_);
+ }
+
+ return c.get<column_count_type> ("column-count");
+ }
+ else
+ {
+ column_count_impl t (s);
t.traverse (c);
- c.set ("column-count", t.c_);
+ return t.c_;
}
-
- return c.get<column_count_type> ("column-count");
}
namespace
{
struct has_a_impl: object_members_base
{
- has_a_impl (unsigned short flags)
- : object_members_base ((flags & context::include_base) != 0),
+ has_a_impl (unsigned short flags, object_section* s)
+ : object_members_base ((flags & context::include_base) != 0, s),
r_ (0),
flags_ (flags)
{
@@ -2244,6 +2448,20 @@ namespace
return r_;
}
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section if requested.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ ((flags_ & include_eager_load) != 0 &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
virtual void
traverse_pointer (semantics::data_member& m, semantics::class_&)
{
@@ -2275,6 +2493,7 @@ namespace
context::test_straight_container |
context::test_inverse_container |
context::test_readonly_container |
+ context::test_readwrite_container |
context::test_smart_container));
if (context::is_a (member_path_,
@@ -2325,6 +2544,7 @@ is_a (data_member_path const& mp,
test_straight_container |
test_inverse_container |
test_readonly_container |
+ test_readwrite_container |
test_smart_container)) != 0 &&
(c = container (m)) != 0)
{
@@ -2340,6 +2560,9 @@ is_a (data_member_path const& mp,
if (f & test_readonly_container)
r = r || readonly (mp, ms);
+ if (f & test_readwrite_container)
+ r = r || (!inverse (m, kp) && !readonly (mp, ms));
+
if (f & test_smart_container)
r = r || (!inverse (m, kp) && !unordered (m) && container_smart (*c));
}
@@ -2348,9 +2571,9 @@ is_a (data_member_path const& mp,
}
size_t context::
-has_a (semantics::class_& c, unsigned short flags)
+has_a (semantics::class_& c, unsigned short flags, object_section* s)
{
- has_a_impl impl (flags);
+ has_a_impl impl (flags, s);
impl.dispatch (c);
return impl.result ();
}
diff --git a/odb/context.hxx b/odb/context.hxx
index 306d2e4..862b74e 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -9,6 +9,7 @@
#include <map>
#include <set>
+#include <list>
#include <stack>
#include <vector>
#include <string>
@@ -267,6 +268,236 @@ struct model_version
bool open;
};
+// Sections.
+//
+struct object_section
+{
+ virtual bool
+ compare (object_section const&) const = 0;
+
+ virtual bool
+ separate_load () const = 0;
+
+ virtual bool
+ separate_update () const = 0;
+};
+
+inline bool
+operator== (object_section const& x, object_section const& y)
+{
+ return x.compare (y);
+}
+
+inline bool
+operator!= (object_section const& x, object_section const& y)
+{
+ return !x.compare (y);
+}
+
+// Main section.
+//
+struct main_section_type: object_section
+{
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return false;}
+
+ virtual bool
+ separate_update () const {return false;}
+};
+
+inline bool
+operator== (main_section_type const&, main_section_type const&)
+{
+ return true; // There is only one main section.
+}
+
+extern main_section_type main_section;
+
+// User-defined section.
+//
+struct user_section: object_section
+{
+ enum load_type
+ {
+ load_eager,
+ load_lazy
+ };
+
+ enum update_type
+ {
+ update_always,
+ update_change,
+ update_manual
+ };
+
+ enum special_type
+ {
+ special_ordinary,
+ special_version // Fake section for optimistic version update.
+ };
+
+ user_section (semantics::data_member& m,
+ semantics::class_& o,
+ std::size_t i,
+ load_type l,
+ update_type u,
+ special_type s = special_ordinary)
+ : member (&m), object (&o), base (0), index (i),
+ load (l), update (u), special (s),
+ total (0), inverse (0), readonly (0),
+ containers (false), readwrite_containers (false) {}
+
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return load != load_eager;}
+
+ virtual bool
+ separate_update () const
+ {
+ // A separately-loaded section is always separately-updated since
+ // it might not be loaded when update is requested.
+ //
+ return separate_load () || update != update_always;
+ }
+
+ bool
+ load_empty () const;
+
+ bool
+ update_empty () const;
+
+ bool
+ empty () const
+ {
+ return load_empty () && update_empty ();
+ }
+
+ // A section is optimistic if the object that contains it is optimistic.
+ // For polymorphic hierarchies, only sections contained in the root are
+ // considered optimistic.
+ //
+ bool
+ optimistic () const;
+
+ semantics::data_member* member; // Data member of this section.
+ semantics::class_* object; // Object containing this section.
+ user_section* base; // Base of this section.
+ std::size_t index; // Index of this sections.
+
+ load_type load;
+ update_type update;
+ special_type special;
+
+ // Column counts.
+ //
+ std::size_t total;
+ std::size_t inverse;
+ std::size_t readonly;
+
+ bool containers;
+ bool readwrite_containers;
+
+ // Total counts across overrides.
+ //
+ std::size_t
+ total_total () const
+ {
+ user_section* b (total_base ());
+ return total + (b == 0 ? 0 : b->total_total ());
+ }
+
+ std::size_t
+ total_inverse () const
+ {
+ user_section* b (total_base ());
+ return inverse + (b == 0 ? 0 : b->total_inverse ());
+ }
+
+ std::size_t
+ total_readonly () const
+ {
+ user_section* b (total_base ());
+ return readonly + (b == 0 ? 0 : b->total_readonly ());
+ }
+
+ bool
+ total_containers ()
+ {
+ user_section* b (total_base ());
+ return containers || (b != 0 && b->total_containers ());
+ }
+
+ bool
+ total_readwrite_containers ()
+ {
+ user_section* b (total_base ());
+ return readwrite_containers ||
+ (b != 0 && b->total_readwrite_containers ());
+ }
+
+private:
+ user_section*
+ total_base () const;
+};
+
+inline bool
+operator== (user_section const& x, user_section const& y)
+{
+ return x.member == y.member;
+}
+
+// Using list for pointer for element stability (see user_section::base).
+//
+struct user_sections: std::list<user_section>
+{
+ // Count sections that have something to load.
+ //
+ static unsigned short const count_load = 0x01;
+
+ // Count sections that are non-eager but have nothing to load.
+ //
+ static unsigned short const count_load_empty = 0x02;
+
+ // Count sections that have something to update.
+ //
+ static unsigned short const count_update = 0x04;
+
+ // Count sections that have nothing to update.
+ //
+ static unsigned short const count_update_empty = 0x08;
+
+ // Count sections that are optimistic.
+ //
+ static unsigned short const count_optimistic = 0x10;
+
+ // Don't exclude fake optimistic version update section from the count.
+ //
+ static unsigned short const count_special_version = 0x20;
+
+ // Count all sections, including special.
+ //
+ static unsigned short const count_all = count_update |
+ count_update_empty |
+ count_special_version;
+
+ static unsigned short const count_new = 0x1000;
+ static unsigned short const count_override = 0x2000;
+ static unsigned short const count_total = 0x4000;
+
+ std::size_t
+ count (unsigned short flags) const;
+
+ user_sections (semantics::class_& o): object (&o) {};
+ semantics::class_* object;
+};
+
+// Context.
+//
class context
{
public:
@@ -555,6 +786,12 @@ public:
return m.count ("version");
}
+ static bool
+ version (const data_member_path& mp)
+ {
+ return mp.size () == 1 && mp.back ()->count ("version");
+ }
+
// Polymorphic inheritance. Return root of the hierarchy or NULL if
// not polymorphic.
//
@@ -605,6 +842,51 @@ public:
return unit.get<model_version> ("model-version");
}
+ // Object sections.
+ //
+ static object_section&
+ section (semantics::data_member& m)
+ {
+ object_section* s (m.get<object_section*> ("section", 0));
+ return s == 0 ? main_section : *s;
+ }
+
+ static object_section&
+ section (data_member_path const& mp)
+ {
+ // The direct member of the object specifies the section.
+ //
+ return section (*mp.front ());
+ }
+
+ // Member belongs to a section that is loaded separately.
+ //
+ static bool
+ separate_load (semantics::data_member& m)
+ {
+ return section (m).separate_load ();
+ }
+
+ static bool
+ separate_load (data_member_path const& mp)
+ {
+ return section (mp).separate_load ();
+ }
+
+ // Member belongs to a section that is updated separately.
+ //
+ static bool
+ separate_update (semantics::data_member& m)
+ {
+ return section (m).separate_update ();
+ }
+
+ static bool
+ separate_update (data_member_path const& mp)
+ {
+ return section (mp).separate_update ();
+ }
+
//
//
typedef ::class_kind class_kind_type;
@@ -813,7 +1095,9 @@ public:
inverse (0),
readonly (0),
optimistic_managed (0),
- discriminator (0)
+ discriminator (0),
+ separate_load (0),
+ separate_update (0)
{
}
@@ -823,10 +1107,13 @@ public:
size_t readonly;
size_t optimistic_managed;
size_t discriminator;
+
+ size_t separate_load;
+ size_t separate_update; // Only readwrite.
};
static column_count_type
- column_count (semantics::class_&);
+ column_count (semantics::class_&, object_section* = 0);
static semantics::data_member*
id_member (semantics::class_& c)
@@ -932,7 +1219,7 @@ public:
return false;
}
- // The 'is a' and 'has a' tests. The has_a test currently does not
+ // The 'is a' and 'has a' tests. The has_a() test currently does not
// cross the container boundaries.
//
public:
@@ -943,7 +1230,13 @@ public:
static unsigned short const test_straight_container = 0x10;
static unsigned short const test_inverse_container = 0x20;
static unsigned short const test_readonly_container = 0x40;
- static unsigned short const test_smart_container = 0x80;
+ static unsigned short const test_readwrite_container = 0x80;
+ static unsigned short const test_smart_container = 0x100;
+
+ // 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;
// By default the test goes into bases for non-polymorphic
// hierarchies and doesn't go for polymorphic. The following
@@ -971,7 +1264,7 @@ public:
// a bool value (0 means no match).
//
size_t
- has_a (semantics::class_&, unsigned short flags);
+ has_a (semantics::class_&, unsigned short flags, object_section* = 0);
public:
// Process include path by adding the prefix, putting it through
@@ -1193,4 +1486,6 @@ has (Y& y)
return false;
}
+#include <odb/context.ixx>
+
#endif // ODB_CONTEXT_HXX
diff --git a/odb/context.ixx b/odb/context.ixx
new file mode 100644
index 0000000..285d364
--- /dev/null
+++ b/odb/context.ixx
@@ -0,0 +1,27 @@
+// file : odb/context.ixx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+inline bool user_section::
+load_empty () const
+{
+ return !separate_load () || (total == 0 && !containers && !optimistic ());
+}
+
+inline bool user_section::
+update_empty () const
+{
+ return total == inverse + readonly && !readwrite_containers;
+}
+
+inline bool user_section::
+optimistic () const
+{
+ if (!context::optimistic (*object))
+ return false;
+ else
+ {
+ semantics::class_* poly_root (context::polymorphic (*object));
+ return poly_root == 0 || poly_root == object;
+ }
+}
diff --git a/odb/features.hxx b/odb/features.hxx
index bc3a9c8..e7c1cdb 100644
--- a/odb/features.hxx
+++ b/odb/features.hxx
@@ -19,6 +19,7 @@ struct features
bool polymorphic_object;
bool no_id_object;
bool session_object;
+ bool section;
bool view;
};
diff --git a/odb/header.cxx b/odb/header.cxx
index 9af82c8..f09c1e8 100644
--- a/odb/header.cxx
+++ b/odb/header.cxx
@@ -60,6 +60,8 @@ traverse_object (type& c)
bool abst (abstract (c));
bool reuse_abst (abst && !poly);
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
string const& type (class_fq_name (c));
os << "// " << class_name (c) << endl
@@ -384,27 +386,37 @@ traverse_object (type& c)
os << "void (*erase2) (database&, const object_type&" <<
(poly ? ", bool, bool" : "") << ");";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "bool (*load_section) (connection&, object_type&, section&" <<
+ (poly ? ", const info_type*" : "") << ");";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "bool (*update_section) (connection&, const object_type&, " <<
+ "const section&" << (poly ? ", const info_type*" : "") << ");";
}
if (options.generate_query ())
{
if (!options.omit_unprepared ())
- os << "result<object_type> (*query) (database&, const query_base_type&);"
- << endl;
+ os << "result<object_type> (*query) (database&, const query_base_type&);";
os << "unsigned long long (*erase_query) (database&, " <<
- "const query_base_type&);"
- << endl;
+ "const query_base_type&);";
if (options.generate_prepared ())
{
os << "odb::details::shared_ptr<prepared_query_impl> " <<
- "(*prepare_query) (connection&, const char*, const query_base_type&);"
- << endl;
+ "(*prepare_query) (connection&, const char*, const query_base_type&);";
os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
- "prepared_query_impl&);"
- << endl;
+ "prepared_query_impl&);";
}
}
@@ -461,6 +473,22 @@ traverse_object (type& c)
os << "static void" << endl
<< "erase (database&, const object_type&);"
<< endl;
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "load (connection&, object_type&, section&);"
+ << endl;
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "update (connection&, const object_type&, const section&);"
+ << endl;
}
if (options.generate_query ())
diff --git a/odb/inline.cxx b/odb/inline.cxx
index d6b8c32..036a728 100644
--- a/odb/inline.cxx
+++ b/odb/inline.cxx
@@ -107,6 +107,8 @@ traverse_object (type& c)
bool abst (abstract (c));
bool reuse_abst (abst && !poly);
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
string const& type (class_fq_name (c));
string traits ("access::object_traits< " + type + " >");
@@ -269,6 +271,30 @@ traverse_object (type& c)
<< "function_table[db.id ()]->erase2 (db, o" <<
(poly ? ", true, true" : "") << ");"
<< "}";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "load (connection& c, object_type& o, section& s)"
+ << "{"
+ << "return function_table[c.database ().id ()]->load_section (" <<
+ "c, o, s" << (poly ? ", 0" : "") << ");"
+ << "}";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "update (connection& c, const object_type& o, const section& s)"
+ << "{"
+ << "return function_table[c.database ().id ()]->update_section (" <<
+ "c, o, s" << (poly ? ", 0" : "") << ");"
+ << "}";
}
if (options.generate_query ())
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 13ebeb0..2b4dc3d 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -386,6 +386,9 @@ check_spec_decl_type (declaration const& d,
p == "auto" ||
p == "column" ||
p == "inverse" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
p == "version" ||
p == "index" ||
p == "unique" ||
@@ -430,7 +433,8 @@ check_spec_decl_type (declaration const& d,
p == "object" ||
p == "optimistic" ||
p == "polymorphic" ||
- p == "definition")
+ p == "definition" ||
+ p == "sectionable")
{
if (tc != RECORD_TYPE)
{
@@ -1405,6 +1409,18 @@ handle_pragma (cxx_lexer& l,
val = l.location ();
tt = l.next (tl, &tn);
}
+ else if (p == "sectionable")
+ {
+ // sectionable
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
else if (p == "callback")
{
// callback (name)
@@ -2112,6 +2128,121 @@ handle_pragma (cxx_lexer& l,
tt = l.next (tl, &tn);
}
+ else if (p == "section")
+ {
+ // section (name)
+ //
+
+ // 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;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-member";
+ val = tl;
+
+ 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 == "load")
+ {
+ // load (eager|lazy)
+ //
+
+ // 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;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME || (tl != "eager" && tl != "lazy"))
+ {
+ error (l) << "eager or lazy expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-load";
+ val = (tl == "eager"
+ ? user_section::load_eager
+ : user_section::load_lazy);
+
+ 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 == "update")
+ {
+ // update (always|change|manual)
+ //
+
+ // 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;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME ||
+ (tl != "always" && tl != "change" && tl != "manual"))
+ {
+ error (l) << "always, change, or manual expected in db pragma " <<
+ p << endl;
+ return;
+ }
+
+ name = "section-update";
+
+ if (tl == "always")
+ val = user_section::update_always;
+ else if (tl == "change")
+ val = user_section::update_change;
+ else
+ val = user_section::update_manual;
+
+ 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 == "unordered")
{
// unordered
@@ -3055,6 +3186,9 @@ handle_pragma_qualifier (cxx_lexer& l, string p)
p == "value_null" ||
p == "value_not_null" ||
p == "default" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
p == "inverse" ||
p == "unordered" ||
p == "readonly" ||
@@ -3400,6 +3534,24 @@ handle_pragma_db_default (cpp_reader* r)
}
extern "C" void
+handle_pragma_db_section (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "section");
+}
+
+extern "C" void
+handle_pragma_db_load (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "load");
+}
+
+extern "C" void
+handle_pragma_db_update (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "update");
+}
+
+extern "C" void
handle_pragma_db_inverse (cpp_reader* r)
{
handle_pragma_qualifier (r, "inverse");
@@ -3503,6 +3655,9 @@ register_odb_pragmas (void*, void*)
c_register_pragma_with_expansion ("db", "value_null", handle_pragma_db_value_null);
c_register_pragma_with_expansion ("db", "value_not_null", handle_pragma_db_value_not_null);
c_register_pragma_with_expansion ("db", "default", handle_pragma_db_default);
+ c_register_pragma_with_expansion ("db", "section", handle_pragma_db_section);
+ 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", "unordered", handle_pragma_db_unordered);
c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly);
diff --git a/odb/processor.cxx b/odb/processor.cxx
index 4e50efe..bc6c408 100644
--- a/odb/processor.cxx
+++ b/odb/processor.cxx
@@ -5,6 +5,7 @@
#include <odb/gcc.hxx>
#include <iostream>
+#include <algorithm> // std::find
#include <odb/common.hxx>
#include <odb/lookup.hxx>
@@ -37,8 +38,65 @@ namespace
if (transient (m))
return;
- process_access (m, "get");
- process_access (m, "set");
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // See if this member is a section.
+ //
+ if (t.fq_name () == "::odb::section")
+ {
+ using semantics::class_;
+
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+ class_* poly_root (polymorphic (c));
+ semantics::data_member* opt (optimistic (c));
+
+ // If we have sections in a polymorphic optimistic hierarchy,
+ // then the version member should be in the root.
+ //
+ if (poly_root == &c && opt != 0 && &opt->scope () != &c)
+ {
+ error (m.location ()) << "version must be a direct data member " <<
+ "of a class that contains sections" << endl;
+ info (opt->location ()) << "version member is declared here" << endl;
+ throw operation_failed ();
+ }
+
+ process_user_section (m, c);
+
+ // We don't need a modifier but the accessor should be by-reference.
+ //
+ process_access (m, "get");
+
+ member_access& ma (m.get<member_access> ("get"));
+ if (ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a section" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // Mark this member as transient since we don't store it in the
+ // database.
+ //
+ m.set ("transient", true);
+
+ features.section = true;
+ return;
+ }
+ else
+ {
+ process_access (m, "get");
+ process_access (m, "set");
+ }
+
+ // See if this member belongs to a section.
+ //
+ if (m.count ("section-member") != 0)
+ process_section_member (m);
// We don't need to do any further processing for common if we
// are generating static multi-database code.
@@ -46,9 +104,6 @@ namespace
if (multi_static && options.database ()[0] == database::common)
return;
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
// Handle wrappers.
//
semantics::type* wt (0), *qwt (0);
@@ -587,6 +642,182 @@ namespace
}
//
+ // Process section.
+ //
+
+ user_section&
+ process_user_section (semantics::data_member& m, semantics::class_& c)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ user_section::load_type l (
+ m.get ("section-load", user_section::load_eager));
+
+ user_section::update_type u (
+ m.get ("section-update", user_section::update_always));
+
+ if (l == user_section::load_eager && u == user_section::update_always)
+ {
+ location const& l (m.location ());
+
+ error (l) << "eager-loaded, always-updated section is pointless"
+ << endl;
+
+ info (l) << "use '#pragma db load' and/or '#pragma db update' to "
+ "specify an alternative loading and/or updating strategy" << endl;
+
+ info (l) << "or remove the section altogether" << endl;
+
+ throw operation_failed ();
+ }
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all));
+ user_section us (m, c, n, l, u);
+
+ // We may already have seen this section (e.g., forward reference
+ // from a member of this section).
+ //
+ user_sections::iterator i (find (uss.begin (), uss.end (), us));
+
+ if (i != uss.end ())
+ return *i;
+
+ // If we are adding a new section to an optimistic class with
+ // version in a base, make sure the base is sectionable.
+ //
+ semantics::data_member* opt (optimistic (c));
+ if (opt != 0 && &opt->scope () != &c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ semantics::node* base (poly_root ? poly_root : &opt->scope ());
+
+ if (!base->count ("sectionable"))
+ {
+ error (m.location ()) << "adding new section to a derived class " <<
+ "in an optimistic hierarchy requires sectionable base class" <<
+ endl;
+
+ info (base->location ()) << "use '#pragma db object sectionable' " <<
+ "to make the base class of this hierarchy sectionable" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ uss.push_back (us);
+ return uss.back ();
+ }
+
+ void
+ process_section_member (semantics::data_member& m)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ string name (m.get<string> ("section-member"));
+ location_t loc (m.get<location_t> ("section-member-location"));
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ try
+ {
+ data_member& us (c.lookup<data_member> (name, class_::include_hidden));
+
+ // Make sure we are referencing a section.
+ //
+ if (utype (us).fq_name () != "::odb::section")
+ {
+ error (loc) << "data member '" << name << "' in '#pragma db " <<
+ "section' is not of the odb::section type" << endl;
+ throw operation_failed ();
+ }
+
+ // If the section is in the base, handle polymorphic inheritance.
+ //
+ class_& b (dynamic_cast<class_&> (us.scope ()));
+ object_section* s (0);
+
+ if (&c != &b && poly_derived)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // This is a section override. See if we have already handled
+ // this section.
+ //
+ for (user_sections::iterator i (uss.begin ());
+ s == 0 && i != uss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ s = &*i;
+ }
+
+ // Otherwise, find and copy the nearest override in the base.
+ // The result should be a chain of overrides leading all the
+ // way to the original section.
+ //
+ if (s == 0)
+ {
+ for (class_* b (&polymorphic_base (c));;
+ b = &polymorphic_base (*b))
+ {
+ user_sections& buss (b->get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (buss.begin ());
+ s == 0 && i != buss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ {
+ uss.push_back (*i);
+ uss.back ().object = &c;
+ uss.back ().base = &*i;
+ s = &uss.back ();
+ }
+ }
+
+ if (s != 0)
+ break;
+
+ assert (b != poly_root); // We should have found it by now.
+ }
+ }
+ }
+ else
+ s = &process_user_section (us, c);
+
+ m.set ("section", s); // Insert as object_section.
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (loc) << "name '" << name << "' in '#pragma db section' " <<
+ "does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << name << "' " <<
+ "specified with '#pragma db section'" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << name << "' specified " <<
+ "with '#pragma db section' is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to " <<
+ "this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ //
// Process wrapper.
//
@@ -1040,10 +1271,28 @@ namespace
data_member& im (
c->lookup<data_member> (name, class_::include_hidden));
+ if (im.count ("transient"))
+ {
+ error (loc) << "data member '" << name << "' specified with " <<
+ "'#pragma db inverse' is transient" << endl;
+ info (im.location ()) << "data member '" << name << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (im.count ("inverse") || im.count ("value-inverse"))
+ {
+ error (loc) << "data member '" << name << "' specified with " <<
+ "'#pragma db inverse' is inverse" << endl;
+ info (im.location ()) << "data member '" << name << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+
// @@ Would be good to check that the other end is actually
- // an object pointer, is not marked as transient or inverse,
- // and points to the correct object. But the other class may
- // not have been processed yet.
+ // an object pointer and points to the correct object. But
+ // the other class may not have been processed yet. Need to
+ // do in validator, pass 2.
//
m.remove ("inverse");
m.set (kp + (kp.empty () ? "": "-") + "inverse", &im);
@@ -1413,6 +1662,8 @@ namespace
//
m.set ("id-tree-type", &id_tree_type);
+ // Has to be first to handle inverse.
+ //
process_container_value (*vt, m, "value", true);
if (it != 0)
@@ -1643,11 +1894,14 @@ namespace
assign_pointer (c);
if (k == class_object)
- traverse_object (c);
+ traverse_object_pre (c);
else if (k == class_view)
traverse_view (c);
names (c);
+
+ if (k == class_object)
+ traverse_object_post (c);
}
//
@@ -1655,10 +1909,45 @@ namespace
//
virtual void
- traverse_object (type& c)
+ traverse_object_pre (type& c)
{
semantics::class_* poly_root (polymorphic (c));
+ // Sections.
+ //
+ user_sections& uss (c.set ("user-sections", user_sections (c)));
+
+ // Copy sections from reuse bases. For polymorphic classes, sections
+ // are overridden.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ user_sections& buss (b.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator j (buss.begin ());
+ j != buss.end ();
+ ++j)
+ {
+ // Don't copy the special version update section.
+ //
+ if (j->special == user_section::special_version)
+ continue;
+
+ uss.push_back (*j);
+ uss.back ().object = &c;
+ uss.back ().base = &*j;
+ }
+ }
+ }
+ }
+
// Determine whether it is a session object.
//
if (!c.count ("session"))
@@ -1792,6 +2081,69 @@ namespace
}
}
+ virtual void
+ traverse_object_post (type& c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ semantics::data_member* opt (optimistic (c));
+
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // See if we need to add a special fake section for version update.
+ //
+ if (c.count ("sectionable"))
+ {
+ uss.push_back (
+ user_section (*opt,
+ c,
+ uss.count (user_sections::count_total |
+ user_sections::count_all),
+ user_section::load_lazy,
+ user_section::update_manual,
+ user_section::special_version));
+
+ // If we are a root of a polymorphic hierarchy and the version is in
+ // a reuse-base, then we need to make sure that base is sectionable
+ // and derive from its special version update section.
+ //
+ semantics::node& opt_base (opt->scope ());
+ if (poly_root == &c && &opt_base != &c)
+ {
+ if (!opt_base.count ("sectionable"))
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+
+ error (l) << "reuse base class of a sectionable polymorphic " <<
+ "root class must be sectionable" << endl;
+
+ info (opt_base.location ()) << "use '#pragma db object " <<
+ "sectionable' to make the base class of this hierarchy " <<
+ "sectionable" << endl;
+
+ throw operation_failed ();
+ }
+
+ uss.back ().base =
+ &opt_base.get<user_sections> ("user-sections").back ();
+ }
+ }
+
+ // Calculate column counts for sections.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ column_count_type cc (column_count (c, &*i));
+ i->total = cc.total;
+ i->inverse = cc.inverse;
+ i->readonly = cc.readonly;
+
+ if ((i->containers = has_a (c, test_container, &*i)))
+ i->readwrite_containers = has_a (c, test_readwrite_container, &*i);
+ }
+ }
+
//
// View.
//
diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx
index 1e6a319..abe5e31 100644
--- a/odb/relational/common.hxx
+++ b/odb/relational/common.hxx
@@ -19,21 +19,25 @@ namespace relational
member_base (semantics::type* type,
string const& fq_type,
- string const& key_prefix)
+ string const& key_prefix,
+ object_section* section = 0)
: type_override_ (type),
fq_type_override_ (fq_type),
- key_prefix_ (key_prefix)
+ key_prefix_ (key_prefix),
+ section_ (section)
{
}
member_base (string const& var,
semantics::type* type,
string const& fq_type,
- string const& key_prefix)
+ string const& key_prefix,
+ object_section* section = 0)
: var_override_ (var),
type_override_ (type),
fq_type_override_ (fq_type),
- key_prefix_ (key_prefix)
+ key_prefix_ (key_prefix),
+ section_ (section)
{
}
@@ -47,6 +51,7 @@ namespace relational
semantics::type* type_override_;
string fq_type_override_;
string key_prefix_;
+ object_section* section_;
};
// Template argument is the database SQL type (sql_type).
diff --git a/odb/relational/context.cxx b/odb/relational/context.cxx
index c8c0ae2..b525802 100644
--- a/odb/relational/context.cxx
+++ b/odb/relational/context.cxx
@@ -103,7 +103,7 @@ namespace relational
}
bool context::
- grow_impl (semantics::class_&)
+ grow_impl (semantics::class_&, user_section*)
{
return false;
}
diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx
index 02c4b9c..d0211c6 100644
--- a/odb/relational/context.hxx
+++ b/odb/relational/context.hxx
@@ -75,10 +75,12 @@ namespace relational
{
public:
// Return true if an object or value type has members for which
- // the image can grow.
+ // the image can grow. If section is not specified, then ignore
+ // separately loaded members. Otherwise ignore members that do
+ // not belong to the section.
//
bool
- grow (semantics::class_&);
+ grow (semantics::class_&, user_section* = 0);
// The same for a member's value type.
//
@@ -206,7 +208,7 @@ namespace relational
// The default implementation returns false.
//
virtual bool
- grow_impl (semantics::class_&);
+ grow_impl (semantics::class_&, user_section*);
virtual bool
grow_impl (semantics::data_member&);
diff --git a/odb/relational/context.ixx b/odb/relational/context.ixx
index afc93da..1f959de 100644
--- a/odb/relational/context.ixx
+++ b/odb/relational/context.ixx
@@ -5,9 +5,9 @@
namespace relational
{
inline bool context::
- grow (semantics::class_& c)
+ grow (semantics::class_& c, user_section* s)
{
- return current ().grow_impl (c);
+ return current ().grow_impl (c, s);
}
inline bool context::
diff --git a/odb/relational/header.cxx b/odb/relational/header.cxx
index 04a73a2..da7f80c 100644
--- a/odb/relational/header.cxx
+++ b/odb/relational/header.cxx
@@ -29,6 +29,10 @@ traverse_object (type& c)
string const& type (class_fq_name (c));
column_count_type const& cc (column_count (c));
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
os << "// " << class_name (c) << endl
<< "//" << endl;
@@ -109,13 +113,18 @@ traverse_object (type& c)
{
if (base_id)
{
- semantics::class_& b (
- dynamic_cast<semantics::class_&> (id->scope ()));
- string const& type ();
+ if (poly_derived)
+ os << "typedef root_traits::id_image_type id_image_type;"
+ << endl;
+ else
+ {
+ semantics::class_& b (
+ dynamic_cast<semantics::class_&> (id->scope ()));
- os << "typedef object_traits_impl< " << class_fq_name (b) << ", " <<
- "id_" << db << " >::id_image_type id_image_type;"
- << endl;
+ os << "typedef object_traits_impl< " << class_fq_name (b) << ", " <<
+ "id_" << db << " >::id_image_type id_image_type;"
+ << endl;
+ }
}
else
{
@@ -147,6 +156,12 @@ traverse_object (type& c)
//
image_type_->traverse (c);
+ // Extra (container, section) statement cache (forward declaration).
+ //
+ if (!reuse_abst && id != 0)
+ os << "struct extra_statement_cache_type;"
+ << endl;
+
//
// Containers (abstract and concrete).
//
@@ -157,6 +172,16 @@ traverse_object (type& c)
}
//
+ // Sections (abstract and concrete).
+ //
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ instance<section_traits> t (c);
+ t->traverse (*i);
+ }
+
+ //
// Query (abstract and concrete).
//
@@ -352,11 +377,9 @@ traverse_object (type& c)
// Containers (concrete).
//
- // Statement cache (forward declaration).
//
- if (id != 0)
- os << "struct container_statement_cache_type;"
- << endl;
+ // Sections (concrete).
+ //
// column_count
//
@@ -373,7 +396,12 @@ traverse_object (type& c)
os << "static const std::size_t discriminator_column_count = " <<
cc.discriminator << "UL;";
- os << endl;
+ os << endl
+ << "static const std::size_t separate_load_column_count = " <<
+ cc.separate_load << "UL;"
+ << "static const std::size_t separate_update_column_count = " <<
+ cc.separate_update << "UL;"
+ << endl;
// Statements.
//
@@ -396,7 +424,7 @@ traverse_object (type& c)
os << "static const char find_discriminator_statement[];";
}
- if (cc.total != cc.id + cc.inverse + cc.readonly)
+ if (cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update)
os << "static const char update_statement[];";
os << "static const char erase_statement[];";
@@ -499,6 +527,28 @@ traverse_object (type& c)
os << ");"
<< endl;
+
+ // Sections.
+ //
+ // We treat all polymorphic sections as (potentially) having something
+ // to load or to update since we cannot predict what will be added to
+ // them in overrides.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "load (connection&, object_type&, section&" <<
+ (poly ? ", const info_type* = 0" : "") << ");"
+ << endl;
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "update (connection&, const object_type&, const section&" <<
+ (poly ? ", const info_type* = 0" : "") << ");"
+ << endl;
}
// query ()
@@ -578,14 +628,16 @@ traverse_object (type& c)
<< "load_ (";
if (poly && !poly_derived)
- os << "base_statements_type&, ";
+ os << "base_statements_type&," << endl;
else
- os << "statements_type&, ";
+ os << "statements_type&," << endl;
- os << "object_type&";
+ os << "object_type&," << endl
+ << "bool reload = false";
if (poly_derived)
- os << ", std::size_t = depth";
+ os << "," << endl
+ << "std::size_t = depth";
os << ");"
<< endl;
diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx
index de2cfe8..4616543 100644
--- a/odb/relational/header.hxx
+++ b/odb/relational/header.hxx
@@ -854,6 +854,204 @@ namespace relational
semantics::class_& c_;
};
+ //
+ //
+ struct section_traits: virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c): c_ (c) {}
+
+ virtual void
+ section_public_extra_pre (user_section&)
+ {
+ }
+
+ virtual void
+ section_public_extra_post (user_section&)
+ {
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+
+ semantics::data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // hierarchy, then pretend it inherits from the special version update
+ // section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (*s.member) + "_traits");
+
+ os << "// " << s.member->name () << endl
+ << "//" << endl
+ << "struct " << name
+ << "{";
+
+ os << "typedef object_traits_impl<object_type, id_" << db <<
+ ">::image_type image_type;"
+ << endl;
+
+ section_public_extra_pre (s);
+
+ // bind (id, image_type)
+ //
+ // If id is NULL, then id is ignored (select). Otherwise, it is
+ // copied at the end (update).
+ //
+ if (load || load_opt || update || update_opt)
+ os << "static std::size_t" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "image_type&," << endl
+ << db << "::statement_kind);"
+ << endl;
+
+ // grow ()
+ //
+ // We have to have out own version because the truncated vector
+ // will have different number of elements.
+ //
+ if (generate_grow && (load || load_opt))
+ os << "static bool" << endl
+ << "grow (image_type&, " << truncated_vector << ");"
+ << endl;
+
+ // init (object, image)
+ //
+ if (load)
+ os << "static void" << endl
+ << "init (object_type&, const image_type&, database*);"
+ << endl;
+
+ // init (image, object)
+ //
+ if (update)
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&, const object_type&);"
+ << endl;
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_public_extra_post (s);
+ os << "};";
+ return;
+ }
+
+ // column_count
+ //
+ column_count_type const& cc (column_count (poly ? *poly_root : c_));
+
+ // Generate load and update column counts even when they are zero so
+ // that we can instantiate section_statements.
+ //
+ os << "static const std::size_t id_column_count = " << cc.id << "UL;";
+
+ os << "static const std::size_t managed_optimistic_load_column_count" <<
+ " = " << cc.optimistic_managed << "UL;"
+ << "static const std::size_t load_column_count = " <<
+ (load ? s.total_total () : 0) << "UL;";
+
+ os << "static const std::size_t managed_optimistic_update_column_count" <<
+ " = " << (poly_derived ? 0 : cc.optimistic_managed) << "UL;"
+ << "static const std::size_t update_column_count = " <<
+ (update ? s.total - s.inverse - s.readonly : 0) << "UL;"
+ << endl;
+
+ // Statements.
+ //
+ if (load || load_opt)
+ os << "static const char select_statement[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_statement[];"
+ << endl;
+
+ // Section statements.
+ //
+ if (load || load_opt || update || update_opt)
+ os << "typedef " << db << "::section_statements< object_type, " <<
+ name << " > statements_type;"
+ << endl;
+
+ // We pass statement cache instead of just statements because
+ // we may also need statements for containers.
+ //
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ os << "static void" << endl
+ << "load (extra_statement_cache_type&, object_type&" <<
+ (poly ? ", bool top = true" : "") << ");"
+ << endl;
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ os << "static void" << endl
+ << "update (extra_statement_cache_type&, const object_type&" <<
+ (poly_derived && s.base != 0 ? ", bool base = true" : "") << ");"
+ << endl;
+
+ section_public_extra_post (s);
+
+ os << "};";
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ protected:
+ semantics::class_& c_;
+ };
+
// First pass over objects, views, and composites. Some code must be
// split into two parts to deal with yet undefined types.
//
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
index 5f1caa7..928982e 100644
--- a/odb/relational/inline.hxx
+++ b/odb/relational/inline.hxx
@@ -165,10 +165,11 @@ namespace relational
virtual void
traverse_object (type& c)
{
- semantics::data_member* id (id_member (c));
- bool base_id (id && &id->scope () != &c); // Comes from base.
+ using semantics::data_member;
- semantics::data_member* optimistic (context::optimistic (c));
+ data_member* id (id_member (c));
+ bool base_id (id && &id->scope () != &c); // Comes from base.
+ data_member* optimistic (context::optimistic (c));
// Base class that contains the object id and version for optimistic
// concurrency.
@@ -187,6 +188,8 @@ namespace relational
string traits ("access::object_traits_impl< " + type + ", id_" +
db.string () + " >");
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
os << "// " << class_name (c) << endl
<< "//" << endl
<< endl;
@@ -313,14 +316,60 @@ namespace relational
<< "erase (database& db, const object_type& obj)"
<< "{"
<< "callback (db, obj, callback_event::pre_erase);"
- << "erase (db, id (obj));"
- << "callback (db, obj, callback_event::post_erase);"
+ << "erase (db, id (obj));";
+
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
+ os << "callback (db, obj, callback_event::post_erase);"
+ << "}";
+ }
+
+ // load (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s, " <<
+ "const info_type* pi)"
+ << "{"
+ << "return base_traits::load (conn, obj, s, pi);"
+ << "}";
+ }
+
+ // update (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s, const info_type* pi)"
+ << "{"
+ << "return base_traits::update (conn, obj, s, pi);"
<< "}";
}
// load_()
//
- if (id != 0 && !(poly_derived || has_a (c, test_container)))
+ if (id != 0 &&
+ !(poly_derived ||
+ has_a (c, test_container | include_eager_load, &main_section) ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0))
{
os << "inline" << endl
<< "void " << traits << "::" << endl
@@ -331,9 +380,33 @@ namespace relational
else
os << "statements_type&, ";
- os << "object_type&)"
+ os << "object_type& obj, bool)"
<< "{"
- << "}";
+ << "ODB_POTENTIALLY_UNUSED (obj);"
+ << endl;
+
+ // Mark eager sections as loaded.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ os << "}";
}
if (poly && need_image_clone && options.generate_query ())
diff --git a/odb/relational/mssql/header.cxx b/odb/relational/mssql/header.cxx
index 1b868fc..5601755 100644
--- a/odb/relational/mssql/header.cxx
+++ b/odb/relational/mssql/header.cxx
@@ -47,6 +47,32 @@ namespace relational
};
entry<class1> class1_entry_;
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_pre (user_section&)
+ {
+ if (abstract (c_) && !polymorphic (c_))
+ return;
+
+ // rowvesion
+ //
+ bool rv (false);
+ if (semantics::data_member* m = optimistic (c_))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ rv = (t.type == sql_type::ROWVERSION);
+ }
+
+ os << "static const bool rowversion = " <<
+ (rv ? "true" : "false") << ";"
+ << endl;
+ }
+ };
+ entry<section_traits> section_traits_;
+
struct image_type: relational::image_type, context
{
image_type (base const& x): base (x) {};
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
index efd71f1..837313a 100644
--- a/odb/relational/mssql/source.cxx
+++ b/odb/relational/mssql/source.cxx
@@ -846,6 +846,53 @@ namespace relational
};
entry<container_traits> container_traits_;
+ struct section_traits: relational::section_traits,
+ statement_columns_common
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+
+ virtual string
+ optimistic_version_increment (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : "sts.update_statement ().version ()";
+ }
+
+ virtual void
+ update_statement_extra (user_section&)
+ {
+ semantics::data_member* ver (optimistic (c_));
+
+ if (ver == 0 ||
+ parse_sql_type (column_type (*ver), *ver).type !=
+ sql_type::ROWVERSION)
+ return;
+
+ // Long data & SQL Server 2005 incompatibility is detected
+ // in persist_statement_extra.
+ //
+ os << strlit (
+ " OUTPUT INSERTED." + convert_from (
+ column_qname (*ver, column_prefix ()), *ver)) << endl;
+ }
+ };
+ entry<section_traits> section_traits_;
+
struct class_: relational::class_, statement_columns_common
{
class_ (base const& x): base (x) {}
@@ -1018,14 +1065,14 @@ namespace relational
}
virtual string
- optimimistic_version_init (semantics::data_member& m)
+ optimistic_version_init (semantics::data_member& m)
{
sql_type t (parse_sql_type (column_type (m), m));
return t.type != sql_type::ROWVERSION ? "1" : "st.version ()";
}
virtual string
- optimimistic_version_increment (semantics::data_member& m)
+ optimistic_version_increment (semantics::data_member& m)
{
sql_type t (parse_sql_type (column_type (m), m));
return t.type != sql_type::ROWVERSION
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx
index 52c1dcc..75b71e9 100644
--- a/odb/relational/mysql/context.cxx
+++ b/odb/relational/mysql/context.cxx
@@ -145,8 +145,8 @@ namespace relational
{
struct has_grow: traversal::class_
{
- has_grow (bool& r)
- : r_ (r)
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
{
*this >> inherits_ >> *this;
}
@@ -161,7 +161,7 @@ namespace relational
if (!(context::object (c) || view || context::composite (c)))
return;
- if (c.count ("mysql-grow"))
+ if (section_ == 0 && c.count ("mysql-grow"))
r_ = c.get<bool> ("mysql-grow");
else
{
@@ -173,29 +173,41 @@ namespace relational
if (!r_)
names (c);
- c.set ("mysql-grow", r_);
+ if (section_ == 0)
+ c.set ("mysql-grow", r_);
}
}
private:
bool& r_;
+ user_section* section_;
traversal::inherits inherits_;
};
struct has_grow_member: member_base
{
has_grow_member (bool& r,
+ user_section* section = 0,
semantics::type* type = 0,
string const& key_prefix = string ())
- : relational::member_base (type, string (), key_prefix),
+ : relational::member_base (type, string (), key_prefix, section),
r_ (r)
{
}
+ virtual bool
+ pre (member_info& mi)
+ {
+ return (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
virtual void
traverse_composite (member_info& mi)
{
// By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
//
r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
}
@@ -236,14 +248,14 @@ namespace relational
}
bool context::
- grow_impl (semantics::class_& c)
+ grow_impl (semantics::class_& c, user_section* section)
{
- if (c.count ("mysql-grow"))
+ if (section == 0 && c.count ("mysql-grow"))
return c.get<bool> ("mysql-grow");
bool r (false);
- has_grow ct (r);
- has_grow_member mt (r);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
traversal::names names;
ct >> names >> mt;
ct.traverse (c);
@@ -263,7 +275,7 @@ namespace relational
grow_impl (semantics::data_member& m, semantics::type& t, string const& kp)
{
bool r (false);
- has_grow_member mt (r, &t, kp);
+ has_grow_member mt (r, 0, &t, kp);
mt.traverse (m);
return r;
}
diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx
index 5d5571e..681a56e 100644
--- a/odb/relational/mysql/context.hxx
+++ b/odb/relational/mysql/context.hxx
@@ -113,7 +113,7 @@ namespace relational
convert_expr (string const&, semantics::data_member&, bool);
virtual bool
- grow_impl (semantics::class_&);
+ grow_impl (semantics::class_&, user_section*);
virtual bool
grow_impl (semantics::data_member&);
diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx
index a0ef339..2fc2ad2 100644
--- a/odb/relational/oracle/source.cxx
+++ b/odb/relational/oracle/source.cxx
@@ -546,6 +546,18 @@ namespace relational
};
entry<container_traits> container_traits_;
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+ };
+ entry<section_traits> section_traits_;
+
struct class_: relational::class_, context
{
class_ (base const& x): base (x) {}
diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx
index 3d71144..947c6bd 100644
--- a/odb/relational/pgsql/context.cxx
+++ b/odb/relational/pgsql/context.cxx
@@ -114,8 +114,8 @@ namespace relational
{
struct has_grow: traversal::class_
{
- has_grow (bool& r)
- : r_ (r)
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
{
*this >> inherits_ >> *this;
}
@@ -128,7 +128,7 @@ namespace relational
if (!(context::object (c) || context::composite (c)))
return;
- if (c.count ("pgsql-grow"))
+ if (section_ == 0 && c.count ("pgsql-grow"))
r_ = c.get<bool> ("pgsql-grow");
else
{
@@ -139,29 +139,41 @@ namespace relational
if (!r_)
names (c);
- c.set ("pgsql-grow", r_);
+ if (section_ == 0)
+ c.set ("pgsql-grow", r_);
}
}
private:
bool& r_;
+ user_section* section_;
traversal::inherits inherits_;
};
struct has_grow_member: member_base
{
has_grow_member (bool& r,
+ user_section* section = 0,
semantics::type* type = 0,
string const& key_prefix = string ())
- : relational::member_base (type, string (), key_prefix),
+ : relational::member_base (type, string (), key_prefix, section),
r_ (r)
{
}
+ virtual bool
+ pre (member_info& mi)
+ {
+ return (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
virtual void
traverse_composite (member_info& mi)
{
// By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
//
r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
}
@@ -189,22 +201,15 @@ namespace relational
};
}
- string const& context::
- convert_expr (string const& sqlt, semantics::data_member& m, bool to)
- {
- sql_type const& t (parse_sql_type (sqlt, m));
- return to ? t.to : t.from;
- }
-
bool context::
- grow_impl (semantics::class_& c)
+ grow_impl (semantics::class_& c, user_section* section)
{
- if (c.count ("pgsql-grow"))
+ if (section == 0 && c.count ("pgsql-grow"))
return c.get<bool> ("pgsql-grow");
bool r (false);
- has_grow ct (r);
- has_grow_member mt (r);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
traversal::names names;
ct >> names >> mt;
ct.traverse (c);
@@ -224,11 +229,18 @@ namespace relational
grow_impl (semantics::data_member& m, semantics::type& t, string const& kp)
{
bool r (false);
- has_grow_member mt (r, &t, kp);
+ has_grow_member mt (r, 0, &t, kp);
mt.traverse (m);
return r;
}
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
string context::
database_type_impl (semantics::type& t,
semantics::names* hint,
diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx
index d06e932..b7ec873 100644
--- a/odb/relational/pgsql/context.hxx
+++ b/odb/relational/pgsql/context.hxx
@@ -103,7 +103,7 @@ namespace relational
convert_expr (string const&, semantics::data_member&, bool);
virtual bool
- grow_impl (semantics::class_&);
+ grow_impl (semantics::class_&, user_section*);
virtual bool
grow_impl (semantics::data_member&);
diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx
index b924a42..a23ec9e 100644
--- a/odb/relational/pgsql/header.cxx
+++ b/odb/relational/pgsql/header.cxx
@@ -36,6 +36,9 @@ namespace relational
column_count_type const& cc (column_count (c));
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
// Statement names.
//
os << "static const char persist_statement_name[];";
@@ -51,7 +54,7 @@ namespace relational
if (poly && !poly_derived)
os << "static const char find_discriminator_statement_name[];";
- if (cc.total != cc.id + cc.inverse + cc.readonly)
+ if (update_columns != 0)
os << "static const char update_statement_name[];";
os << "static const char erase_statement_name[];";
@@ -76,7 +79,7 @@ namespace relational
{
os << "static const unsigned int find_statement_types[];";
- if (cc.total != cc.id + cc.inverse + cc.readonly)
+ if (update_columns != 0)
os << "static const unsigned int update_statement_types[];";
if (optimistic != 0)
@@ -125,8 +128,7 @@ namespace relational
// Container statement types.
//
- os << "static const unsigned int select_types[];"
- << "static const unsigned int insert_types[];";
+ os << "static const unsigned int insert_types[];";
if (smart)
os << "static const unsigned int update_types[];"
@@ -137,6 +139,44 @@ namespace relational
};
entry<container_traits> container_traits_;
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_post (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Statement names.
+ //
+ if (load || load_opt)
+ os << "static const char select_name[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_name[];"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ os << "static const unsigned int update_types[];";
+ }
+ };
+ entry<section_traits> section_traits_;
+
struct image_member: relational::image_member, member_base
{
image_member (base const& x)
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
index ceda512..d5fe8fd 100644
--- a/odb/relational/pgsql/source.cxx
+++ b/odb/relational/pgsql/source.cxx
@@ -98,11 +98,28 @@ namespace relational
struct statement_oids: object_columns_base, context
{
- statement_oids (statement_kind sk, bool first = true)
- : object_columns_base (first), sk_ (sk)
+ statement_oids (statement_kind sk,
+ bool first = true,
+ object_section* section = 0)
+ : object_columns_base (first, column_prefix (), section), sk_ (sk)
{
}
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
virtual void
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
@@ -265,6 +282,17 @@ namespace relational
if (container (mi))
return false;
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
+ if (var_override_.empty ())
+ {
+ // Ignore separately loaded members.
+ //
+ if (section_ == 0 && separate_load (mi.m))
+ return false;
+ }
+
// Ignore polymorphic id references; they are not returned by
// the select statement.
//
@@ -641,8 +669,12 @@ namespace relational
semantics::data_member* id (id_member (c));
semantics::data_member* optimistic (context::optimistic (c));
+
column_count_type const& cc (column_count (c));
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
string const& n (class_fq_name (c));
string const& fn (flat_name (n));
string traits ("access::object_traits_impl< " + n + ", id_pgsql >");
@@ -684,7 +716,7 @@ namespace relational
strlit (fn + "_find_discriminator") << ";"
<< endl;
- if (cc.total != cc.id + cc.inverse + cc.readonly)
+ if (update_columns != 0)
os << "const char " << traits << "::" << endl
<< "update_statement_name[] = " << strlit (fn + "_update") <<
";"
@@ -745,7 +777,7 @@ namespace relational
<< "find_statement_types[] ="
<< "{";
- statement_oids st (statement_select);
+ statement_oids st (statement_select, true);
st.traverse (*id);
os << "};";
@@ -753,19 +785,21 @@ namespace relational
// update_statement_types.
//
- if (id != 0 && cc.total != cc.id + cc.inverse + cc.readonly)
+ if (id != 0 && update_columns != 0)
{
os << "const unsigned int " << traits << "::" << endl
<< "update_statement_types[] ="
<< "{";
{
- statement_oids st (statement_update);
+ statement_oids st (statement_update, true, &main_section);
st.traverse (c);
}
+ // Not the same as update_columns.
+ //
bool first (cc.total == cc.id + cc.inverse + cc.readonly +
- cc.optimistic_managed);
+ cc.separate_update + cc.optimistic_managed);
statement_oids st (statement_where, first);
st.traverse (*id);
@@ -791,11 +825,13 @@ namespace relational
}
virtual void
- container_cache_extra_args (bool used)
+ extra_statement_cache_extra_args (bool c, bool s)
{
+ bool u (c || s);
+
os << "," << endl
- << db << "::native_binding&" << (used ? " idn" : "") << "," << endl
- << "const unsigned int*" << (used ? " idt" : "");
+ << db << "::native_binding&" << (u ? " idn" : "") << "," << endl
+ << "const unsigned int*" << (u ? " idt" : "");
}
virtual void
@@ -916,33 +952,6 @@ namespace relational
semantics::type& vt (container_vt (t));
semantics::type& idt (container_idt (m));
- // select statement types.
- //
- {
- os << "const unsigned int " << scope << "::" << endl
- << "select_types[] ="
- << "{";
-
- statement_oids so (statement_where);
-
- if (inv)
- {
- // many(i)-to-many
- //
- if (container (*inv_m))
- so.traverse (*inv_m, idt, "value", "value");
-
- // many(i)-to-one
- //
- else
- so.traverse (*inv_m);
- }
- else
- so.traverse (m, idt, "id", "object_id");
-
- os << "};";
- }
-
// insert statement types.
//
{
@@ -1065,6 +1074,74 @@ namespace relational
};
entry<container_traits> container_traits_;
+ struct section_traits : relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_extra (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ semantics::data_member* opt (optimistic (c_));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ string name (public_name (*s.member));
+ string scope (scope_ + "::" + name + "_traits");
+
+ // Statment names.
+ //
+
+ // Prefix object name to avoid conflicts with inherited member
+ // statement names.
+ //
+ string fn (flat_name (class_fq_name (c_) + "_" + name));
+
+ if (load || load_opt)
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " << strlit (fn + "_select") << ";"
+ << endl;
+
+ if (update || update_opt)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " << strlit (fn + "_update") << ";"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &s);
+ st.traverse (c_);
+ }
+
+ statement_oids st (statement_where, !update);
+ st.traverse (*id_member (c_));
+
+ if (s.optimistic ()) // Note: not update_opt.
+ st.traverse (*opt);
+
+ os << "};";
+ }
+ }
+ };
+ entry<section_traits> section_traits_;
+
struct container_cache_init_members:
relational::container_cache_init_members
{
@@ -1078,6 +1155,18 @@ namespace relational
};
entry<container_cache_init_members> container_cache_init_members_;
+ struct section_cache_init_members:
+ relational::section_cache_init_members
+ {
+ section_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<section_cache_init_members> section_cache_init_members_;
}
}
}
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index 8d7c913..ba5a464 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -2,6 +2,8 @@
// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
+#include <map>
+
#include <odb/gcc.hxx>
#include <odb/lookup.hxx>
@@ -52,6 +54,36 @@ traverse_object (type& c)
db.string () + " >");
column_count_type const& cc (column_count (c));
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+ user_sections* buss (poly_base != 0
+ ? &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;
@@ -67,32 +99,77 @@ traverse_object (type& c)
if (options.generate_query ())
query_columns_type_->traverse (c);
+ // Statement cache (definition).
//
- // Containers (abstract and concrete).
- //
- bool containers (has_a (c, test_container));
- bool straight_containers (false);
- bool straight_readwrite_containers (false);
- bool smart_containers (false);
-
- if (containers)
+ if (!reuse_abst && id != 0)
{
- size_t scn (has_a (c, test_straight_container));
+ os << "struct " << traits << "::extra_statement_cache_type"
+ << "{";
- if (scn != 0)
- {
- straight_containers = true;
+ instance<container_cache_members> cm;
+ cm->traverse (c);
+
+ if (containers)
+ os << endl;
- // Inverse containers cannot be marked readonly.
+ bool sections (false);
+ instance<section_cache_members> sm;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip the special version update section in reuse inheritance (we
+ // always treat it as abstract).
//
- straight_readwrite_containers = scn > has_a (c, test_readonly_container);
+ if (i->special == user_section::special_version && !poly)
+ continue;
- // Inverse containers cannot be smart.
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
//
- smart_containers = has_a (c, test_smart_container);
+ if (!i->empty () || (poly && i->optimistic ()))
+ {
+ sm->traverse (*i);
+ sections = true;
+ }
}
+
+ if (sections)
+ os << endl;
+
+ os << "extra_statement_cache_type (" << endl
+ << db << "::connection&" << (containers || sections ? " c" : "") <<
+ "," << endl
+ << "image_type&" << (sections ? " im" : "") << "," << endl
+ << db << "::binding&" << (containers || sections ? " id" : "") <<
+ "," << endl
+ << db << "::binding&" << (sections ? " idv" : "");
+
+ extra_statement_cache_extra_args (containers, sections);
+
+ os << ")";
+
+ instance<container_cache_init_members> cim;
+ cim->traverse (c);
+
+ instance<section_cache_init_members> sim (!containers);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->special == user_section::special_version && !poly)
+ continue;
+
+ if (!i->empty () || (poly && i->optimistic ()))
+ sim->traverse (*i);
+ }
+
+ os << "{"
+ << "}"
+ << "};";
}
+ //
+ // Containers (abstract and concrete).
+ //
+
if (containers)
{
instance<container_traits> t (c);
@@ -100,6 +177,16 @@ traverse_object (type& c)
}
//
+ // Sections (abstract and concrete).
+ //
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ instance<section_traits> t (c);
+ t->traverse (*i);
+ }
+
+ //
// Functions (abstract and concrete).
//
@@ -207,7 +294,7 @@ traverse_object (type& c)
<< "bind (" << bind_vector << " b," << endl;
// If we are a derived type in a polymorphic hierarchy, then
- // we get the the external id binding.
+ // we get the external id binding.
//
if (poly_derived)
os << "const " << bind_vector << " id," << endl
@@ -238,7 +325,7 @@ traverse_object (type& c)
<< "{"
<< "if (id != 0)" << endl
<< "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
<< "}";
}
else
@@ -257,7 +344,7 @@ traverse_object (type& c)
<< "{"
<< "if (id != 0)" << endl
<< "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
<< "}";
// Bind the image chain for the select statement. Seeing that
@@ -412,32 +499,9 @@ traverse_object (type& c)
// Containers (concrete).
//
- // Statement cache (definition).
//
- if (id != 0)
- {
- os << "struct " << traits << "::container_statement_cache_type"
- << "{";
-
- instance<container_cache_members> cm;
- cm->traverse (c);
-
- os << (containers ? "\n" : "")
- << "container_statement_cache_type (" << endl
- << db << "::connection&" << (containers ? " c" : "") << "," << endl
- << db << "::binding&" << (containers ? " id" : "");
-
- container_cache_extra_args (containers);
-
- os << ")";
-
- instance<container_cache_init_members> im;
- im->traverse (c);
-
- os << "{"
- << "}"
- << "};";
- }
+ // Sections (concrete).
+ //
// Polymorphic map.
//
@@ -448,6 +512,73 @@ traverse_object (type& c)
<< traits << "::map;"
<< endl;
+ // Sections. Do we have any new sections or overrides?
+ //
+ bool sections (
+ uss.count (user_sections::count_new |
+ user_sections::count_override |
+ user_sections::count_load |
+ user_sections::count_update |
+ user_sections::count_special_version |
+ (poly ? user_sections::count_optimistic : 0)) != 0);
+ if (sections)
+ {
+ map<size_t, user_section*> m;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ m[i->index] = &*i;
+
+ os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type::section_functions" << endl
+ << "section_table_for_" << flat_name (type) << "[] ="
+ << "{";
+
+ for (size_t i (0), n (uss.count (user_sections::count_total |
+ user_sections::count_all));
+ i != n; ++i)
+ {
+ os << (i != 0 ? "," : "") << "{";
+
+ map<size_t, user_section*>::iterator j (m.find (i));
+
+ if (j != m.end ())
+ {
+ user_section& s (*j->second);
+ string n (public_name (*s.member));
+
+ // load
+ //
+ if (s.load_empty ())
+ os << "0";
+ else
+ os << "&odb::section_load_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+
+ os << "," << endl;
+
+ // update
+ //
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
+ //
+ if (s.update_empty () && !(poly && s.optimistic ()))
+ os << "0";
+ else
+ os << "&odb::section_update_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+ }
+ else
+ os << "0," << endl
+ << "0";
+
+ os << "}";
+ }
+
+ os << "};";
+ }
+
+ //
+ //
os << "const " << traits << "::" << (abst ? "abstract_" : "") <<
"info_type" << endl
<< traits << "::info (" << endl
@@ -455,18 +586,21 @@ traverse_object (type& c)
if (poly_derived)
os << "&object_traits_impl< " << class_fq_name (*poly_base) <<
- ", id_" << db << " >::info";
+ ", id_" << db << " >::info," << endl;
else
- os << "0";
+ os << "0," << endl;
- string n;
+ // Sections.
+ //
+ if (sections)
+ os << "section_table_for_" << flat_name (type);
+ else
+ os << "0";
if (!abst)
{
- n = class_fq_name (c);
-
os << "," << endl
- << strlit (string (n, 2, string::npos)) << "," << endl
+ << strlit (string (type, 2, string::npos)) << "," << endl
<< "&odb::create_impl< " << type << " >," << endl
<< "&odb::dispatch_impl< " << type << ", id_" << db << " >," << endl;
@@ -481,13 +615,15 @@ traverse_object (type& c)
if (!abst)
os << "static const " << traits << "::entry_type" << endl
- << "polymorphic_entry_for_" << flat_name (n) << ";"
+ << "polymorphic_entry_for_" << flat_name (type) << ";"
<< endl;
}
//
// Statements.
//
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
qname table (table_name (c));
string qtable (quote_id (table));
@@ -562,7 +698,8 @@ traverse_object (type& c)
statement_columns sc;
{
statement_kind sk (statement_select); // Imperfect forwarding.
- instance<object_columns> t (qtable, sk, sc, d);
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, d, s);
t->traverse (c);
process_statement_columns (sc, statement_select);
find_column_counts[poly_depth - d] = sc.size ();
@@ -588,9 +725,14 @@ traverse_object (type& c)
j->traverse (polymorphic_base (c));
}
- bool f (false); // @@ (im)perfect forwarding
- instance<object_joins> j (c, f, d); // @@ (im)perfect forwarding
- j->traverse (c);
+ {
+ // For the find statement, don't join section members.
+ //
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* s (&main_section); // @@ (im)perfect forwarding
+ instance<object_joins> j (c, f, d, s);
+ j->traverse (c);
+ }
instance<query_parameters> qp (table);
for (object_columns_list::iterator b (id_cols->begin ()), i (b);
@@ -690,7 +832,7 @@ traverse_object (type& c)
// update_statement
//
- if (cc.total != cc.id + cc.inverse + cc.readonly)
+ if (update_columns != 0)
{
instance<query_parameters> qp (table);
@@ -698,7 +840,8 @@ traverse_object (type& c)
{
query_parameters* p (qp.get ()); // Imperfect forwarding.
statement_kind sk (statement_update); // Imperfect forwarding.
- instance<object_columns> t (sk, sc, p);
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, s);
t->traverse (c);
process_statement_columns (sc, statement_update);
}
@@ -713,6 +856,18 @@ traverse_object (type& c)
os << strlit (c + (++i != e ? "," : "")) << endl;
}
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
update_statement_extra (c);
for (object_columns_list::iterator b (id_cols->begin ()), i (b);
@@ -792,8 +947,9 @@ traverse_object (type& c)
//
statement_columns sc;
{
- statement_kind sk (statement_select); // Imperfect forwarding.
- instance<object_columns> oc (qtable, sk, sc, poly_depth);
+ statement_kind sk (statement_select); //@@ Imperfect forwarding.
+ object_section* s (&main_section); //@@ Imperfect forwarding.
+ instance<object_columns> oc (qtable, sk, sc, poly_depth, s);
oc->traverse (c);
process_statement_columns (sc, statement_select);
}
@@ -819,8 +975,11 @@ traverse_object (type& c)
if (id != 0)
{
+ // For the query statement we also join section members so that they
+ // can be used in the WHERE clause.
+ //
bool t (true); //@@ (im)perfect forwarding
- instance<object_joins> oj (c, t, poly_depth); //@@ (im)perfect forwarding
+ instance<object_joins> oj (c, t, poly_depth);
oj->traverse (c);
}
@@ -1009,7 +1168,7 @@ traverse_object (type& c)
// If we don't have auto id, then obj is a const reference.
//
string obj (auto_id ? "obj" : "const_cast< object_type& > (obj)");
- string init (optimimistic_version_init (*opt));
+ string init (optimistic_version_init (*opt));
if (!opt_ma_set->synthesized)
os << "// From " << location_string (opt_ma_set->loc, true) << endl;
@@ -1062,8 +1221,10 @@ traverse_object (type& c)
<< "{"
<< "bind (idb.bind, i);"
<< "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}";
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
if (poly && !straight_containers && !abst)
os << "}";
@@ -1071,10 +1232,39 @@ traverse_object (type& c)
if (straight_containers)
{
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
instance<container_calls> t (container_calls::persist_call);
t->traverse (c);
}
+ // Reset sections: loaded, unchanged.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they have been reset by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
// Call callback (post_persist).
//
if (!abst) // If we are poly-abstract, then top will always be false.
@@ -1093,6 +1283,17 @@ traverse_object (type& c)
//
if (id != 0 && (!readonly || poly))
{
+ // See if we have any sections that we might have to update.
+ //
+ bool sections (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // This test will automatically skip the special version update section.
+ //
+ if (!i->update_empty () && i->update != user_section::update_manual)
+ sections = true;
+ }
+
os << "void " << traits << "::" << endl
<< "update (database& db, const object_type& obj";
@@ -1146,16 +1347,71 @@ traverse_object (type& c)
<< endl;
}
+ // If we have change-updated sections that contain change-tracking
+ // containers, then mark such sections as changed if any of the
+ // containers was changed. Do this before calling the base so that
+ // we account for the whole hierarchy before we actually start
+ // updating any sections (important for optimistic concurrency
+ // version).
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ if (s.update != user_section::update_change)
+ continue;
+
+ if (!has_a (c, test_smart_container, &s))
+ continue;
+
+ data_member& m (*s.member);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << "{";
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // VC++ cannot grok the constructor syntax.
+ //
+ os << "const odb::section& s = " << ma.translate ("obj") << ";"
+ << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ if (s.load != user_section::load_eager)
+ os << "s.loaded () && ";
+
+ // And for not already being changed.
+ //
+ os << "!s.changed ())"
+ << "{";
+
+ instance<container_calls> t (container_calls::section_call, &s);
+ t->traverse (c);
+
+ os << "}" // if
+ << "}";
+ }
+
if (poly_derived)
{
bool readonly_base (context::readonly (*poly_base));
if (readonly_base ||
- cc.total != cc.id + cc.inverse + cc.readonly ||
- straight_readwrite_containers)
+ update_columns != 0 ||
+ update_containers ||
+ sections)
{
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection ());"
<< "statements_type& sts (" << endl
<< "conn.statement_cache ().find_object<object_type> ());"
<< endl;
@@ -1171,13 +1427,11 @@ traverse_object (type& c)
else
{
// Otherwise, we have to initialize the id image ourselves. If
- // we don't have any columns or containers to update, then we
- // only have to do it if this is not a top-level call. If we
- // are abstract, then top is always false.
+ // we don't have any columns, containers or sections to update,
+ // then we only have to do it if this is not a top-level call.
+ // If we are abstract, then top is always false.
//
- if (cc.total == cc.id + cc.inverse + cc.readonly &&
- !straight_readwrite_containers &&
- !abst)
+ if (update_columns == 0 && !update_containers && !sections && !abst)
os << "if (!top)";
os << "{"
@@ -1195,17 +1449,16 @@ traverse_object (type& c)
<< "bind (idb.bind, i);"
<< "sts.id_image_version (i.version);"
<< "idb.version++;"
+ // Optimistic poly base cannot be readonly.
<< "}"
<< "}";
}
- if (cc.total != cc.id + cc.inverse + cc.readonly)
- {
+ if (update_columns != 0)
os << "const binding& idb (sts.id_image_binding ());"
<< endl;
- }
- if (cc.total != cc.id + cc.inverse + cc.readonly)
+ if (update_columns != 0)
{
// Initialize the object image.
//
@@ -1255,16 +1508,23 @@ traverse_object (type& c)
// to update.
//
}
- else if (cc.total != cc.id + cc.inverse + cc.readonly)
+ else if (update_columns != 0)
{
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection ());"
<< "statements_type& sts (" << endl
<< "conn.statement_cache ().find_object<object_type> ());"
<< endl;
- // Initialize object and id images.
+ // Initialize id image.
//
+ if (!id_ma->synthesized)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "const id_type& id (" << endl
+ << id_ma->translate ("obj") << ");";
+
if (opt != 0)
{
if (!opt_ma_get->synthesized)
@@ -1274,31 +1534,30 @@ traverse_object (type& c)
<< opt_ma_get->translate ("obj") << ");";
}
- os << "id_image_type& i (sts.id_image ());";
-
- if (!id_ma->synthesized)
- os << "// From " << location_string (id_ma->loc, true) << endl;
-
- os << "init (i, " << id_ma->translate ("obj");
+ os << "id_image_type& idi (sts.id_image ());"
+ << "init (idi, id";
if (opt != 0)
os << ", &v";
os << ");"
- << endl
- << "image_type& im (sts.image ());";
+ << endl;
+
+ // Initialize object image.
+ //
+ os << "image_type& im (sts.image ());";
if (generate_grow)
- os << "if (";
+ os << "if (";
- os << "init (im, obj, statement_update)";
+ os << "init (im, obj, statement_update)";
- if (generate_grow)
- os << ")" << endl
- << "im.version++";
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
- os << ";"
- << endl;
+ os << ";"
+ << endl;
// Update binding is bound to two images (object and id)
// so we have to track both versions.
@@ -1319,23 +1578,25 @@ traverse_object (type& c)
// suffix of the update bind array (see object_statements).
//
os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.update_id_image_version () ||" << endl
+ << "if (idi.version != sts.update_id_image_version () ||" << endl
<< "idb.version == 0)"
<< "{"
// If the id binding is up-to-date, then that means update
// binding is too and we just need to update the versions.
//
- << "if (i.version != sts.id_image_version () ||" << endl
+ << "if (idi.version != sts.id_image_version () ||" << endl
<< "idb.version == 0)"
<< "{"
- << "bind (idb.bind, i);"
+ << "bind (idb.bind, idi);"
// Update the id binding versions since we may use them later
// to update containers.
//
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}"
- << "sts.update_id_image_version (i.version);"
+ << "sts.id_image_version (idi.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}"
+ << "sts.update_id_image_version (idi.version);"
<< endl
<< "if (!u)" << endl
<< "imb.version++;"
@@ -1354,11 +1615,14 @@ traverse_object (type& c)
{
// We don't have any columns to update. Note that we still have
// to make sure this object exists in the database. For that we
- // will run the SELECT query using the find_() function.
- //
+ // will run the SELECT query using the find_() function. Note
+ // that this case cannot be optimistic (we always update the
+ // version column) and we don't need to worry about initializing
+ // version in id image for sections below.
//
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection ());"
<< "statements_type& sts (" << endl
<< "conn.statement_cache ().find_object<object_type> ());"
<< endl;
@@ -1379,7 +1643,9 @@ traverse_object (type& c)
os << "discriminator_ (sts, id, 0);"
<< endl;
- if (!abst)
+ // The same rationale as a couple of screens up.
+ //
+ if (!update_containers && !sections && !abst)
os << "if (!top)";
os << "{"
@@ -1394,6 +1660,7 @@ traverse_object (type& c)
<< "bind (idb.bind, i);"
<< "sts.id_image_version (i.version);"
<< "idb.version++;"
+ // Cannot be optimistic.
<< "}"
<< "}";
}
@@ -1408,13 +1675,20 @@ traverse_object (type& c)
}
}
- if (straight_readwrite_containers)
+ if (update_containers || sections)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (update_containers)
{
- instance<container_calls> t (container_calls::update_call);
+ instance<container_calls> t (container_calls::update_call,
+ &main_section);
t->traverse (c);
}
// Update the optimistic concurrency version in the object member.
+ // Do it before updating sections since they will update it as well.
+ // The same code as in section_traits.
//
if (opt != 0 && !poly_derived)
{
@@ -1422,7 +1696,7 @@ traverse_object (type& c)
// constness.
//
string obj ("const_cast< object_type& > (obj)");
- string inc (optimimistic_version_increment (*opt));
+ string inc (optimistic_version_increment (*opt));
if (!opt_ma_set->synthesized)
os << "// From " << location_string (opt_ma_set->loc, true) << endl;
@@ -1465,6 +1739,123 @@ traverse_object (type& c)
}
}
+ // Update sections that are loaded and need updating.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->update_empty () || i->update == user_section::update_manual)
+ continue;
+
+ // Here is the deal: for polymorphic section overrides, we could
+ // have used the same approach as in load_() where the class that
+ // defines the section data member initiates the section update.
+ // However, if we used this approach, then we would get what can
+ // be called "interleaved" update statements. That is, a section
+ // update would update the section data in "derived" tables before
+ // updating the main section in these derived tables. While this
+ // does not feel right, it can also cause more real problems if,
+ // for example, there are constraints or some such. Generally,
+ // we always want to update the main section data first for each
+ // table in the hierarchy.
+ //
+ // So what we are going to do instead is call section traits
+ // update at each override point (and indicate to it not to
+ // call base). One tricky aspect is who is going to reset the
+ // changed state. We have to do it at the top since otherwise
+ // overrides won't know the section has changed. Finding out
+ // whether we are the final override (in section terms, not
+ // object terms) is tricky.
+ //
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ string sep;
+ if (i->load != user_section::load_eager)
+ {
+ os << ma.translate ("obj") << ".loaded ()";
+ sep = " && ";
+ }
+
+ // If change-updated section, check that it has changed.
+ //
+ if (i->update == user_section::update_change)
+ os << sep << ma.translate ("obj") << ".changed ()";
+
+ os << ")"
+ << "{";
+
+ // Re-initialize the id+ver image with new version which may
+ // have changed. Here we take a bit of a shortcut and not
+ // re-bind the image since we know only version may have
+ // changed and that is always an integer (cannot grow).
+ //
+ if (opt != 0)
+ {
+ if (!opt_ma_get->synthesized)
+ os << "// From " << location_string (opt_ma_get->loc, true) << endl;
+
+ os << "const version_type& v (" << endl
+ << opt_ma_get->translate ("obj") << ");";
+
+ if (!id_ma->synthesized)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "init (sts.id_image (), " << id_ma->translate ("obj") <<
+ ", &v);"
+ << endl;
+ }
+
+ os << public_name (m) << "_traits::update (esc, obj";
+
+ if (poly_derived && i->base != 0)
+ {
+ // Normally we don't want to call base (base object's update()
+ // would have called it; see comment above). There is one
+ // exception, however, and that is a readonly base section
+ // in optimistic class. In this case, base object's update()
+ // won't call it (it is readonly) but it needs to be called
+ // in order to increment the version.
+ //
+ bool base (false);
+ for (user_section* b (i->base); !base && b != 0; b = b->base)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers)
+ break; // We have a readwrite base.
+ else if (b->optimistic ())
+ base = true; // We have a readonly optimistic root.
+ }
+
+ os << ", " << (base ? "true" : "false");
+ }
+
+ os << ");";
+
+ // Clear the change flag and arm the transaction rollback callback.
+ //
+ if (i->update == user_section::update_change)
+ {
+ // If polymorphic, do it only if we are the final override.
+ //
+ if (poly)
+ os << "if (root_traits::map->find (typeid (obj))." <<
+ "final_section_update (info, " << i->index << "UL))" << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false, &tr);";
+ }
+
+ os << "}";
+ }
+
// Call callback (post_update).
//
if (!abst) // If we are poly-abstract, then top will always be false.
@@ -1558,8 +1949,10 @@ traverse_object (type& c)
<< "{"
<< "bind (idb.bind, i);"
<< "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}";
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
if (poly)
os << "}";
@@ -1572,6 +1965,9 @@ traverse_object (type& c)
//
if (straight_containers)
{
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
instance<container_calls> t (container_calls::erase_id_call);
t->traverse (c);
}
@@ -1654,7 +2050,9 @@ traverse_object (type& c)
<< "conn.statement_cache ().find_object<object_type> ());";
if (poly_derived)
- os << "root_statements_type& rsts (sts.root_statements ());";
+ os << endl
+ << "root_statements_type& rsts (sts.root_statements ());"
+ << "ODB_POTENTIALLY_UNUSED (rsts);";
os << endl;
@@ -1702,6 +2100,7 @@ traverse_object (type& c)
<< "bind (idb.bind, i);"
<< rsts << ".id_image_version (i.version);"
<< "idb.version++;"
+ // Cannot be optimistic.
<< "}";
if (poly)
@@ -1713,8 +2112,16 @@ traverse_object (type& c)
// here since in case of a custom schema, it might not be
// there).
//
- instance<container_calls> t (container_calls::erase_obj_call);
- t->traverse (c);
+
+ if (straight_containers)
+ {
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());"
+ << endl;
+
+ instance<container_calls> t (container_calls::erase_obj_call);
+ t->traverse (c);
+ }
os << "if (sts.erase_statement ().execute () != 1)" << endl
<< "throw object_not_persistent ();"
@@ -1741,32 +2148,14 @@ traverse_object (type& c)
<< "init (i, id, &v);"
<< endl;
- // To update the id part of the optimistic id binding we have
- // to do it indirectly via the id binding, since both id and
- // optimistic id bindings just point to the suffix of the
- // update bind array (see object_statements).
- //
- os << "binding& oidb (" << rsts << ".optimistic_id_image_binding ());"
- << "if (i.version != " << rsts <<
- ".optimistic_id_image_version () ||" << endl
- << "oidb.version == 0)"
- << "{"
- // If the id binding is up-to-date, then that means optimistic
- // id binding is too and we just need to update the versions.
- //
- << "binding& idb (" << rsts << ".id_image_binding ());"
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
<< "if (i.version != " << rsts << ".id_image_version () ||" << endl
<< "idb.version == 0)"
<< "{"
<< "bind (idb.bind, i);"
- // Update the id binding versions since we may use them later
- // to delete containers.
- //
<< rsts << ".id_image_version (i.version);"
<< "idb.version++;"
- << "}"
- << rsts << ".optimistic_id_image_version (i.version);"
- << "oidb.version++;"
+ << rsts << ".optimistic_id_image_binding ().version++;"
<< "}";
if (poly)
@@ -1841,6 +2230,10 @@ traverse_object (type& c)
//
if (straight_containers)
{
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());"
+ << endl;
+
instance<container_calls> t (container_calls::erase_obj_call);
t->traverse (c);
}
@@ -1868,6 +2261,10 @@ traverse_object (type& c)
os << "if (top)"
<< "{";
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
// Remove from the object cache.
//
os << "pointer_cache_traits::erase (db, id);";
@@ -1880,10 +2277,6 @@ traverse_object (type& c)
os << "}";
}
}
- else if (smart_containers)
- {
-
- }
else
{
os << "callback (db, obj, callback_event::pre_erase);"
@@ -2272,7 +2665,7 @@ traverse_object (type& c)
if (delay_freeing_statement_result)
os << "ar.free ();";
- os << "load_ (sts, obj);"
+ os << "load_ (sts, obj, true);"
<< rsts << ".load_delayed ();"
<< "l.unlock ();"
<< "callback (db, obj, callback_event::post_load);"
@@ -2282,6 +2675,307 @@ traverse_object (type& c)
os << "}";
}
+ // load (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s" <<
+ (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "if (pi == 0)" // Top-level call.
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // If our poly-base has load sections, then call the base version
+ // first. Besides checking for sections, it will also initialize
+ // the id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_load |
+ user_sections::count_load_empty) != 0)
+ {
+ os << "if (base_traits::load (conn, obj, s, pi))" << endl
+ << "return true;"
+ << endl;
+ }
+ else
+ {
+ // Resolve extra statements if we are doing direct calls.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ os << endl;
+
+ // Initialize id image. This is not necessarily the root of the
+ // polymorphic hierarchy.
+ //
+ os << "id_image_type& i (sts.id_image ());";
+
+ if (!id_ma->synthesized)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "init (i, " << id_ma->translate ("obj") << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Dispatch.
+ //
+ bool e (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ if (i->load == user_section::load_eager)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (e)
+ os << "else ";
+ else
+ e = true;
+
+ os << "if (&s == &" << ma.translate ("obj") << ")" << endl;
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ // If this is an empty section, then there may not be any
+ // overrides.
+ //
+ os << "{"
+ << "info_type::section_load sl (" <<
+ "pi->find_section_load (" << i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (conn, obj, true);"
+ << "}";
+ }
+ }
+
+ os << "else" << endl
+ << "return false;"
+ << endl
+ << "return true;"
+ << "}";
+ }
+
+ // update (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s" << (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "if (pi == 0)" // Top-level call.
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // If our poly-base has update sections, then call the base version
+ // first. Besides checking for sections, it will also initialize the
+ // id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_update |
+ user_sections::count_update_empty) != 0)
+ {
+ os << "if (base_traits::update (conn, obj, s, pi))" << endl
+ << "return true;"
+ << endl;
+ }
+ else
+ {
+ // Resolve extra statements if we are doing direct calls.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ os << endl;
+
+ // Initialize id image. This is not necessarily the root of the
+ // polymorphic hierarchy.
+ //
+ if (opt != 0)
+ {
+ if (!opt_ma_get->synthesized)
+ os << "// From " << location_string (opt_ma_get->loc, true) << endl;
+
+ os << "const version_type& v (" << endl
+ << opt_ma_get->translate ("obj") << ");";
+ }
+
+ os << "id_image_type& i (sts.id_image ());";
+
+ if (!id_ma->synthesized)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "init (i, " << id_ma->translate ("obj") <<
+ (opt != 0 ? ", &v" : "") << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Dispatch.
+ //
+ bool e (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ // Skip read-only sections. Polymorphic sections are always
+ // (potentially) read-write.
+ //
+ if (!poly && i->update_empty ())
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (e)
+ os << "else ";
+ else
+ e = true;
+
+ os << "if (&s == &" << ma.translate ("obj") << ")";
+
+ if (!poly)
+ os << public_name (m) << "_traits::update (esc, obj);";
+ else
+ {
+ // If this is a readonly section, then there may not be any
+ // overrides.
+ //
+ os << "{"
+ << "info_type::section_update su (" <<
+ "pi->find_section_update (" << i->index << "UL));";
+
+ if (i->update_empty ())
+ {
+ // For optimistic section, also check that we are not the
+ // final override, since otherwise we will increment the
+ // version without updating anything.
+ //
+ if (i->optimistic ())
+ os << "if (su != 0 && su != info.sections[" << i->index <<
+ "UL].update)" << endl;
+ else
+ os << "if (su != 0)" << endl;
+ }
+
+ os << "su (conn, obj);"
+ << "}";
+ }
+ }
+
+ os << "else" << endl
+ << "return false;"
+ << endl
+ << "return true;"
+ << "}";
+ }
+
// find_ ()
//
if (id != 0)
@@ -2319,8 +3013,10 @@ traverse_object (type& c)
<< "{"
<< "bind (idb.bind, i);"
<< "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}";
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
if (poly_derived && !abst)
os << "}";
@@ -2411,38 +3107,113 @@ traverse_object (type& c)
os << "}";
}
- // load_() containers
+ // load_()
+ //
+ // Load containers, reset/reload sections.
//
- if (containers || poly_derived)
+ if (poly_derived ||
+ load_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
{
os << "void " << traits << "::" << endl
<< "load_ (";
if (poly && !poly_derived)
- os << "base_statements_type& sts, ";
+ os << "base_statements_type& sts," << endl;
else
- os << "statements_type& sts, ";
+ os << "statements_type& sts," << endl;
- os << "object_type& obj";
+ os << "object_type& obj," << endl
+ << "bool reload";
if (poly_derived)
- os << ", std::size_t d";
+ os << "," << endl
+ << "std::size_t d";
os << ")"
- << "{";
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (reload);"
+ << endl;
if (poly_derived)
os << "if (--d != 0)" << endl
- << "base_traits::load_ (sts.base_statements (), obj" <<
+ << "base_traits::load_ (sts.base_statements (), obj, reload" <<
(poly_base != poly_root ? ", d" : "") << ");"
<< endl;
- if (containers)
+ if (load_containers ||
+ (!poly && uss.count (user_sections::count_new |
+ user_sections::count_load) != 0))
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (load_containers)
{
- instance<container_calls> t (container_calls::load_call);
+ instance<container_calls> t (container_calls::load_call, &main_section);
t->traverse (c);
}
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they are handled by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (i->load == user_section::load_eager)
+ {
+ // Mark an eager section as loaded.
+ //
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ continue;
+ }
+
+ os << "if (reload)"
+ << "{"
+ // Reload sections that have been loaded, clear change state.
+ //
+ << "if (" << ma.translate ("obj") << ".loaded ())"
+ << "{";
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ os << "info_type::section_load sl (" << endl
+ << "root_traits::map->find (typeid (obj)).find_section_load (" <<
+ i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (sts.connection (), obj, true);";
+ }
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << "}"
+ << "}"
+ << "else" << endl
+ // Reset to unloaded, unchanged state.
+ << ma.translate ("obj") << ".reset ();"
+ << endl;
+ }
+
os << "}";
}
@@ -2494,7 +3265,7 @@ traverse_object (type& c)
if (empty_depth != 0)
os << "}";
- os << "load_ (sts, obj, d);"
+ os << "load_ (sts, obj, false, d);"
<< "}";
}
@@ -2959,6 +3730,20 @@ traverse_object (type& c)
os << "," << endl
<< "&" << traits << "::erase";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::load";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::update";
}
if (options.generate_query ())
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index d27110e..9dbb4e9 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -79,8 +79,9 @@ namespace relational
object_columns (statement_kind sk,
statement_columns& sc,
- query_parameters* param = 0)
- : object_columns_base (true, true),
+ query_parameters* param = 0,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
sk_ (sk), sc_ (sc), param_ (param), depth_ (1)
{
}
@@ -88,8 +89,9 @@ namespace relational
object_columns (std::string const& table_qname,
statement_kind sk,
statement_columns& sc,
- size_t depth = 1)
- : object_columns_base (true, true),
+ size_t depth = 1,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
sk_ (sk),
sc_ (sc),
param_ (0),
@@ -98,6 +100,24 @@ namespace relational
{
}
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements. Also include optimistic version into
+ // section's SELECT and UPDATE statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ()) ||
+ (version (mp) &&
+ (sk_ == statement_update || sk_ == statement_select));
+ }
+
virtual void
traverse_object (semantics::class_& c)
{
@@ -477,56 +497,101 @@ namespace relational
: object_columns_base (true, true),
obj_ (obj),
depth_ (depth),
+ section_ (0),
alias_ (alias),
prefix_ (prefix),
suffix_ (suffix)
{
+ init ();
+ }
+
+ polymorphic_object_joins (semantics::class_& obj,
+ size_t depth,
+ user_section& section)
+ : object_columns_base (true, true),
+ obj_ (obj),
+ depth_ (depth),
+ section_ (&section),
+ suffix_ ("\n")
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
// Get the table and id columns.
//
table_ = alias_.empty ()
- ? table_qname (obj)
- : quote_id (alias_ + "_" + table_name (obj).uname ());
+ ? table_qname (obj_)
+ : quote_id (alias_ + "_" + table_name (obj_).uname ());
- cols_->traverse (*id_member (obj));
+ cols_->traverse (*id_member (obj_));
}
virtual void
traverse_object (semantics::class_& c)
{
- std::ostringstream cond;
+ // If section is specified, skip bases that don't add anything
+ // to load.
+ //
+ bool skip (false), stop (false);
+ if (section_ != 0)
+ {
+ skip = true;
- qname table (table_name (c));
- string alias (alias_.empty ()
- ? quote_id (table)
- : quote_id (alias_ + "_" + table.uname ()));
+ if (section_->object == &c)
+ {
+ user_section& s (*section_);
- for (object_columns_list::iterator b (cols_->begin ()), i (b);
- i != cols_->end ();
- ++i)
- {
- if (i != b)
- cond << " AND ";
+ if (s.total != 0 || s.optimistic ())
+ skip = false;
- string qn (quote_id (i->name));
- cond << alias << '.' << qn << '=' << table_ << '.' << qn;
+ section_ = s.base; // Move to the next base.
+
+ if (section_ == 0)
+ stop = true; // Stop at this base if there are no more overrides.
+ }
}
- string line (" LEFT JOIN " + quote_id (table));
+ if (!skip)
+ {
+ std::ostringstream cond;
- if (!alias_.empty ())
- line += (need_alias_as ? " AS " : " ") + alias;
+ qname table (table_name (c));
+ string alias (alias_.empty ()
+ ? quote_id (table)
+ : quote_id (alias_ + "_" + table.uname ()));
+
+ for (object_columns_list::iterator b (cols_->begin ()), i (b);
+ i != cols_->end ();
+ ++i)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ string qn (quote_id (i->name));
+ cond << alias << '.' << qn << '=' << table_ << '.' << qn;
+ }
- line += " ON " + cond.str ();
+ string line (" LEFT JOIN " + quote_id (table));
- os << prefix_ << strlit (line) << suffix_;
+ if (!alias_.empty ())
+ line += (need_alias_as ? " AS " : " ") + alias;
- if (--depth_ != 0)
+ line += " ON " + cond.str ();
+
+ os << prefix_ << strlit (line) << suffix_;
+ }
+
+ if (!stop && --depth_ != 0)
inherits (c);
}
private:
semantics::class_& obj_;
size_t depth_;
+ user_section* section_;
string alias_;
string prefix_;
string suffix_;
@@ -540,8 +605,11 @@ namespace relational
//@@ context::{cur,top}_object; might have to be created every time.
//
- object_joins (semantics::class_& scope, bool query, size_t depth = 1)
- : object_columns_base (true, true),
+ object_joins (semantics::class_& scope,
+ bool query,
+ size_t depth,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
query_ (query),
depth_ (depth),
table_ (table_qname (scope)),
@@ -550,6 +618,18 @@ namespace relational
id_cols_->traverse (id_);
}
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (*section_ == main_section && !s.separate_load ());
+ }
+
virtual void
traverse_object (semantics::class_& c)
{
@@ -816,9 +896,12 @@ namespace relational
{
typedef bind_member base;
+ // NULL section means we are generating object bind().
+ //
bind_member (string const& var = string (),
- string const& arg = string ())
- : member_base (var, 0, string (), string ()),
+ string const& arg = string (),
+ object_section* section = 0)
+ : member_base (var, 0, string (), string (), section),
arg_override_ (arg)
{
}
@@ -855,6 +938,11 @@ namespace relational
if (container (mi))
return false;
+ // Treat version as present in every section.
+ //
+ if (section_ != 0 && !version (mi.m) && *section_ != section (mi.m))
+ return false;
+
// Ignore polymorphic id references; they are bound in a special
// way.
//
@@ -869,12 +957,20 @@ namespace relational
if (var_override_.empty ())
{
+ if (section_ == 0 && separate_load (mi.m) && inverse (mi.m))
+ return false;
+
os << "// " << mi.m.name () << endl
<< "//" << endl;
+ // Order of these tests is important.
+ //
if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
os << "if (sk != statement_insert && sk != statement_update)"
<< "{";
+ else if (section_ == 0 && separate_load (mi.m))
+ os << "if (sk == statement_insert)"
+ << "{";
else if (inverse (mi.m, key_prefix_) || version (mi.m))
os << "if (sk == statement_select)"
<< "{";
@@ -887,7 +983,8 @@ namespace relational
if (id (mi.m) ||
readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
+ ((c = composite (mi.t)) && readonly (*c)) ||
+ (section_ == 0 && separate_update (mi.m)))
os << "if (sk != statement_update)"
<< "{";
}
@@ -946,6 +1043,8 @@ namespace relational
//
if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
block = true;
+ else if (section_ == 0 && separate_load (mi.m))
+ block = true;
else if (inverse (mi.m, key_prefix_) || version (mi.m))
block = true;
else if (!readonly (*context::top_object))
@@ -954,7 +1053,8 @@ namespace relational
if (id (mi.m) ||
readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
+ ((c = composite (mi.t)) && readonly (*c)) ||
+ (section_ == 0 && separate_update (mi.m)))
block = true;
}
@@ -1014,42 +1114,33 @@ namespace relational
column_count_type const& cc (column_count (c));
- os << "n += " << cc.total << "UL";
+ os << "n += ";
- // select = total
- // insert = total - inverse - optimistic_managed
- // update = total - inverse - optimistic_managed - id - readonly
+ // select = total - separate_load
+ // insert = total - inverse - optimistic_managed - id(auto & !sending)
+ // update = total - inverse - optimistic_managed - id - readonly -
+ // separate_update
//
- if (cc.inverse != 0 ||
- cc.optimistic_managed != 0 ||
- (!ro && (cc.id != 0 || cc.readonly != 0)))
- {
- os << " - (" << endl
- << "sk == statement_select ? 0 : ";
-
- if (cc.inverse != 0 || cc.optimistic_managed != 0)
- os << (cc.inverse + cc.optimistic_managed) << "UL";
-
- if (!ro && (cc.id != 0 || cc.readonly != 0))
- {
- if (cc.inverse != 0 || cc.optimistic_managed != 0)
- os << " + ";
-
- os << "(" << endl
- << "sk == statement_insert ? ";
-
- if (insert_send_auto_id || !auto_ (*id_member (c)))
- os << "0";
- else
- os << cc.id << "UL";
-
- os << " : " << cc.id + cc.readonly << "UL)";
- }
-
- os << ")";
- }
-
- os << ";";
+ size_t select (cc.total - cc.separate_load);
+ size_t insert (cc.total - cc.inverse - cc.optimistic_managed);
+ size_t update (insert - cc.id - cc.readonly - cc.separate_update);
+
+ semantics::data_member* id;
+ if (!insert_send_auto_id && (id = id_member (c)) != 0 && auto_ (*id))
+ insert -= cc.id;
+
+ if (select == insert && insert == update)
+ os << select << "UL;";
+ else if (select != insert && insert == update)
+ os << "sk == statement_select ? " << select << "UL : " <<
+ insert << "UL;";
+ else if (select == insert && insert != update)
+ os << "sk == statement_update ? " << update << "UL : " <<
+ select << "UL;";
+ else
+ os << "sk == statement_select ? " << select << "UL : " <<
+ "sk == statement_insert ? " << insert << "UL : " <<
+ update << "UL;";
if (check)
os << "}";
@@ -1066,8 +1157,11 @@ namespace relational
{
typedef grow_member base;
- grow_member (size_t& index, string const& var = string ())
- : member_base (var, 0, string (), string ()), index_ (index)
+ grow_member (size_t& index,
+ string const& var = string (),
+ user_section* section = 0)
+ : member_base (var, 0, string (), string (), section),
+ index_ (index)
{
}
@@ -1131,8 +1225,9 @@ namespace relational
typedef init_image_member base;
init_image_member (string const& var = string (),
- string const& member = string ())
- : member_base (var, 0, string (), string ()),
+ string const& member = string (),
+ user_section* section = 0)
+ : member_base (var, 0, string (), string (), section),
member_override_ (member)
{
}
@@ -1182,6 +1277,9 @@ namespace relational
if (container (mi) || inverse (mi.m, key_prefix_))
return false;
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
// Ignore polymorphic id references; they are initialized in a
// special way.
//
@@ -1221,8 +1319,15 @@ namespace relational
if (id (mi.m) ||
readonly (mi.m) ||
+ (section_ == 0 && separate_update (mi.m)) ||
((c = composite (mi.t)) && readonly (*c))) // Can't be id.
- os << "if (sk == statement_insert)";
+ {
+ // If we are generating section init(), then sk can only be
+ // statement_update.
+ //
+ if (section_ == 0)
+ os << "if (sk == statement_insert)";
+ }
}
os << "{";
@@ -1478,8 +1583,9 @@ namespace relational
init_value_member (string const& member = string (),
string const& var = string (),
- bool ignore_implicit_discriminator = true)
- : member_base (var, 0, string (), string ()),
+ bool ignore_implicit_discriminator = true,
+ user_section* section = 0)
+ : member_base (var, 0, string (), string (), section),
member_override_ (member),
ignore_implicit_discriminator_ (ignore_implicit_discriminator)
{
@@ -1529,6 +1635,9 @@ namespace relational
if (container (mi))
return false;
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
// Ignore polymorphic id references; they are initialized in a
// special way.
//
@@ -1549,6 +1658,11 @@ namespace relational
}
else
{
+ // Ignore separately loaded members.
+ //
+ if (section_ == 0 && separate_load (mi.m))
+ return false;
+
os << "// " << mi.m.name () << endl
<< "//" << endl
<< "{";
@@ -2335,7 +2449,7 @@ namespace relational
<< "//" << endl
<< "if (id != 0)" << endl
<< "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
<< endl;
// We don't need to update the bind index since this is the
@@ -2403,7 +2517,7 @@ namespace relational
<< "//" << endl
<< "if (id != 0)" << endl
<< "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
<< endl;
switch (ck)
@@ -2491,7 +2605,7 @@ namespace relational
<< "//" << endl
<< "if (id != 0)" << endl
<< "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
<< endl;
// We don't need to update the bind index since this is the
@@ -3256,7 +3370,7 @@ namespace relational
semantics::class_& c_;
};
- // Container statement cache members.
+ // Extra statement cache members for containers.
//
struct container_cache_members: object_members_base, virtual context
{
@@ -3316,6 +3430,54 @@ namespace relational
bool first_;
};
+ // Extra statement cache members for sections.
+ //
+ struct section_cache_members: virtual context
+ {
+ typedef section_cache_members base;
+
+ virtual void
+ traverse (user_section& s)
+ {
+ string traits (public_name (*s.member) + "_traits");
+
+ os << db << "::" << "section_statements< " <<
+ class_fq_name (*s.object) << ", " << traits << " > " <<
+ s.member->name () << ";";
+ }
+ };
+
+ struct section_cache_init_members: virtual context
+ {
+ typedef section_cache_init_members base;
+
+ section_cache_init_members (bool first): first_ (first) {}
+
+ virtual void
+ traverse (user_section& s)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << s.member->name () << " (c, im, id, idv";
+ extra_members ();
+ os << ")";
+ }
+
+ virtual void
+ extra_members () {}
+
+ protected:
+ bool first_;
+ };
+
// Calls for container members.
//
struct container_calls: object_members_base, virtual context
@@ -3328,17 +3490,33 @@ namespace relational
load_call,
update_call,
erase_obj_call,
- erase_id_call
+ erase_id_call,
+ section_call
};
- container_calls (call_type call)
- : object_members_base (true, false, true),
+ container_calls (call_type call, object_section* section = 0)
+ : object_members_base (true, false, true, false, section),
call_ (call),
obj_prefix_ ("obj"),
modifier_ (0)
{
}
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // load calls.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (call_ == load_call &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
virtual void
traverse_composite_wrapper (semantics::data_member* m,
semantics::class_& c,
@@ -3428,6 +3606,7 @@ namespace relational
// In certain cases we don't need to do anything.
//
if ((call_ != load_call && inverse) ||
+ (call_ == section_call && !smart) ||
(call_ == update_call && readonly (member_path_, member_scope_)))
return;
@@ -3522,21 +3701,21 @@ namespace relational
{
os << traits << "::persist (" << endl
<< var << "," << endl
- << "sts.container_statment_cache ()." << sts_name << ");";
+ << "esc." << sts_name << ");";
break;
}
case load_call:
{
os << traits << "::load (" << endl
<< var << "," << endl
- << "sts.container_statment_cache ()." << sts_name << ");";
+ << "esc." << sts_name << ");";
break;
}
case update_call:
{
os << traits << "::update (" << endl
<< var << "," << endl
- << "sts.container_statment_cache ()." << sts_name << ");";
+ << "esc." << sts_name << ");";
break;
}
case erase_obj_call:
@@ -3546,7 +3725,7 @@ namespace relational
if (smart)
os << "&" << var << "," << endl;
- os << "sts.container_statment_cache ()." << sts_name << ");"
+ os << "esc." << sts_name << ");"
<< endl;
break;
}
@@ -3557,10 +3736,17 @@ namespace relational
if (smart)
os << "0," << endl;
- os << "sts.container_statment_cache ()." << sts_name << ");"
+ os << "esc." << sts_name << ");"
<< endl;
break;
}
+ case section_call:
+ {
+ os << "if (" << traits << "::container_traits_type::changed (" <<
+ var << "))" << endl
+ << "s.reset (true, true);"; // loaded, changed
+ break;
+ }
}
if (call_ != erase_id_call && (call_ != erase_obj_call || smart))
@@ -3592,6 +3778,944 @@ namespace relational
member_access* modifier_;
};
+ //
+ //
+ struct section_traits: traversal::class_, virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c)
+ : c_ (c),
+ scope_ ("access::object_traits_impl< " + class_fq_name (c) +
+ ", id_" + db.string () + " >")
+ {
+ }
+
+ // Additional code that need to be executed following the call to
+ // init_value().
+ //
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&, statement_kind)
+ {
+ }
+
+ virtual void
+ section_extra (user_section&)
+ {
+ }
+
+ // Returning "1" means increment by one.
+ //
+ virtual string
+ optimistic_version_increment (semantics::data_member&)
+ {
+ return "1";
+ }
+
+ virtual void
+ update_statement_extra (user_section&)
+ {
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ data_member& m (*s.member);
+
+ class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+ class_* poly_base (poly_derived ? &polymorphic_base (c_) : 0);
+
+ data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // polymorphic hierarchy, then pretend it inherits from the special
+ // version update section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (m) + "_traits");
+ string scope (scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ // bind (id, image_type)
+ //
+ if (load || load_opt || update || update_opt)
+ {
+ os << "std::size_t " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << (reuse_abst ? "," : " id,") << endl
+ << "std::size_t" << (reuse_abst ? "," : " id_size,") << endl
+ << "image_type& i," << endl
+ << db << "::statement_kind sk)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "using namespace " << db << ";"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ // Bind reuse base. It is always first and we never ask it
+ // to bind id(+ver).
+ //
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0 && b.separate_load ());
+ bool load_opt (b.optimistic () && b.separate_load ());
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (load || load_opt || update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::bind (" << endl
+ << "b, 0, 0, i, sk);"
+ << endl;
+ }
+
+ // Bind members.
+ //
+ {
+ instance<bind_member> bm ("", "", &s);
+ traversal::names n (*bm);
+ names (c_, n);
+ }
+
+ // Bind polymorphic image chain for the select statement.
+ //
+ if (s.base != 0 && poly_derived && s.separate_load ())
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::bind (" << endl
+ << "b + n, 0, 0, *i" << acc << ", sk);"
+ << endl;
+ }
+
+ if (!reuse_abst)
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ os << "return n;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow && (load || load_opt))
+ {
+ os << "bool " << scope << "::" << endl
+ << "grow (image_type& i, " << truncated_vector << " t)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);"
+ << endl
+ << "bool grew (false);"
+ << endl;
+
+ size_t index (0);
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+ bool load_opt (b.optimistic ());
+
+ if (load || load_opt)
+ {
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "grew = object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::grow (i, t);"
+ << endl;
+
+ index += b.total + (load_opt ? 1 : 0);
+ }
+ }
+
+ {
+ user_section* ps (&s);
+ instance<grow_member> gm (index, "", ps);
+ traversal::names n (*gm);
+ names (c_, n);
+ }
+
+ // Grow polymorphic image chain.
+ //
+ if (s.base != 0 && poly_derived)
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ size_t cols;
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ cols = b->total + (b->optimistic () ? 1 : 0);
+ if (cols != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::grow (" << endl
+ << "*i" << acc << ", t + " << cols << "UL))" << endl
+ << "i" << acc << "->version++;"
+ << endl;
+ }
+
+ os << "return grew;" << endl
+ << "}";
+ }
+
+ // init (object, image)
+ //
+ if (load)
+ {
+ os << "void " << scope << "::" << endl
+ << "init (object_type& o, const image_type& i, database* db)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ if (s.base != 0)
+ {
+ if (!poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+
+ if (load)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (o, i, db);"
+ << endl;
+ }
+ else
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::init (" << endl
+ << "o, *i" << acc << ", db);"
+ << endl;
+ }
+ }
+
+ {
+ instance<init_value_member> iv ("", "", true, &s);
+ traversal::names n (*iv);
+ names (c_, n);
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ if (update)
+ {
+ os << (generate_grow ? "bool " : "void ") << scope << "::" << endl
+ << "init (image_type& i, const object_type& o)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ // There is no call to init_image_pre() here (which calls the
+ // copy callback for some databases) since we are not going to
+ // touch any of the members that were loaded by query.
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << (generate_grow ? "grew = " : "") <<
+ "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (i, o);"
+ << endl;
+ }
+
+ {
+ instance<init_image_member> ii ("", "", &s);
+ traversal::names n (*ii);
+ names (c_, n);
+ }
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_extra (s);
+ return;
+ }
+
+ // Statements.
+ //
+ qname table (table_name (c_));
+ string qtable (quote_id (table));
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id_member (c_));
+
+ // select_statement
+ //
+ if (load || load_opt)
+ {
+ size_t depth (poly_derived ? polymorphic_depth (c_) : 1);
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, depth, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_select);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "select_statement[] =" << endl
+ << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ os << strlit (" FROM " + qtable) << endl;
+
+ // Join polymorphic bases.
+ //
+ if (depth != 1 && s.base != 0)
+ {
+ size_t d (depth - 1); //@@ (im)perfect forward.
+ user_section& bs (*s.base); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c_, d, bs);
+ j->traverse (*poly_base);
+ }
+
+ // Join tables of inverse members belonging to this section.
+ //
+ {
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* ps (&s); // @@ (im)perfect forwarding
+ instance<object_joins> j (c_, f, depth, ps);
+ j->traverse (c_);
+ }
+
+ instance<query_parameters> qp (table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ os << endl;
+
+ os << strlit ((i == b ? " WHERE " : " AND ") +
+ qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
+ }
+
+ os << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (update || update_opt)
+ {
+ instance<query_parameters> qp (table);
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_update);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + " SET ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
+ update_statement_extra (s);
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ os << endl;
+
+ os << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" +
+ convert_to (qp->next (), i->type, *i->member));
+ }
+
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ os << endl
+ << strlit (" AND " + column_qname (*opt, column_prefix ()) +
+ "=" + convert_to (qp->next (), *opt));
+ }
+
+ os << ";"
+ << endl;
+ }
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "load (extra_statement_cache_type& esc, object_type& obj" <<
+ (poly ? ", bool top" : "") << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);"
+ << endl;
+
+ // Load values, if any.
+ //
+ if (load || load_opt)
+ {
+ // The SELECT statement for the top override loads all the
+ // values.
+ //
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Note that we don't use delayed load machinery here. While
+ // a section can definitely contain self-referencing pointers,
+ // loading such a pointer won't mess up the data members in the
+ // image that we care about. It also holds true for streaming
+ // result, since the bindings are different.
+
+ os << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding ());"
+ << endl;
+
+ // For the polymorphic case, instead of storing an array of
+ // versions as we do for objects, we will add all the versions
+ // up and use that as a cumulative image chain version. If you
+ // meditate a bit on that, you will realize that it will work
+ // (hint: versions can only increase).
+ //
+ string ver;
+ string ver_decl;
+
+ if (s.base != 0 && poly_derived)
+ {
+ ver = "imv";
+ ver_decl = "std::size_t imv (im.version";
+
+ user_section* b (s.base);
+ string acc ("im.base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ ver_decl += " +\n" + acc + "->version";
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ ver_decl += ")";
+
+ os << ver_decl << ";"
+ << endl;
+ }
+ else
+ ver = "im.version";
+
+ os << "if (" << ver << " != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select);"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "}";
+
+ // Id binding is assumed initialized and bound.
+ //
+ os << "select_statement& st (sts.select_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ os << "if (r == select_statement::no_data)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (grow (c_, &s))
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()))" << endl
+ << "im.version++;"
+ << endl;
+
+ // The same logic as above.
+ //
+ if (s.base != 0 && poly_derived)
+ os << ver_decl << ";"
+ << endl;
+
+ os << "if (" << ver << " != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select);"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ if (opt != 0) // Not load_opt, we do it in poly-derived as well.
+ {
+ member_access& ma (opt->get<member_access> ("get"));
+
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (";
+
+ if (poly_derived)
+ {
+ os << "root_traits::version (*im.base";
+ for (class_* b (poly_base);
+ b != poly_root;
+ b = &polymorphic_base (*b))
+ os << "->base";
+ os << ")";
+ }
+ else
+ os << "version (im)";
+
+ os << " != " << ma.translate ("obj") << ")" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ if (load)
+ {
+ os << "init (obj, im, &sts.connection ().database ());";
+ init_value_extra (); // Stream results, etc.
+ os << endl;
+ }
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // Call base to load its containers, if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ // If we don't have any values of our own but out base
+ // does, then allow it to load them.
+ //
+ if (b->containers ||
+ (!load && (b->total != 0 || b->optimistic ())))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // This one is tricky: ideally we would do a direct call to
+ // the base's load() (which may not be our immediate base,
+ // BTW) but there is no easy way to resolve base's extra
+ // statements from ours. So, instead, we are going to go
+ // via the dispatch machinery which requires a connection
+ // rather than statements. Not the most efficient way but
+ // simple.
+
+ // Find the "previous" override by starting the search from
+ // our base.
+ //
+ if (b != 0)
+ {
+ // Note that here we are using the base section index to
+ // handle the special version update base.
+ //
+ os << "info.base->find_section_load (" << b->index << "UL) (" <<
+ "esc." << m.name () << ".connection (), obj, " <<
+ // If we don't have any values of our own, then allow the
+ // base load its.
+ //
+ (load ? "false" : "top") << ");"
+ << endl;
+ }
+ }
+
+ // Load our containers, if any.
+ //
+ if (s.containers)
+ {
+ instance<container_calls> t (container_calls::load_call, &s);
+ t->traverse (c_);
+ }
+
+ os << "}";
+ }
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "update (extra_statement_cache_type& esc, " <<
+ "const object_type& obj" <<
+ (poly_derived && s.base != 0 ? ", bool base" : "") << ")"
+ << "{";
+
+ // Call base if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers ||
+ (poly && b->optimistic ()))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // The same (tricky) logic as in load(). Note that here we are
+ // using the base section index to handle the special version
+ // update base.
+ //
+ if (b != 0)
+ os << "if (base)" << endl
+ << "info.base->find_section_update (" << b->index <<
+ "UL) (esc." << m.name () << ".connection (), obj);"
+ << endl;
+ else
+ os << "ODB_POTENTIALLY_UNUSED (base);"
+ << endl;
+ }
+
+ // Update values, if any.
+ //
+ if (update || update_opt)
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "const binding& id (sts.idv_binding ());" // id+version
+ << "binding& imb (sts.update_image_binding ());"
+ << endl;
+
+ if (update)
+ {
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj)";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+ }
+
+ os << "if (im.version != sts.update_image_version () ||" << endl
+ << "id.version != sts.update_id_binding_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, id.bind, id.count, im, statement_update);"
+ << "sts.update_image_version (im.version);"
+ << "sts.update_id_binding_version (id.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "if (sts.update_statement ().execute () == 0)" << endl;
+
+ if (opt == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+
+ // Update readwrite containers if any.
+ //
+ if (s.readwrite_containers)
+ {
+ instance<container_calls> t (container_calls::update_call, &s);
+ t->traverse (c_);
+ }
+
+ // Update the optimistic concurrency version in the object member.
+ // Very similar code to object.
+ //
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ member_access& ma_get (opt->get<member_access> ("get"));
+ member_access& ma_set (opt->get<member_access> ("set"));
+
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ string obj ("const_cast< object_type& > (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (!ma_set.synthesized)
+ os << "// From " << location_string (ma_set.loc, true) << endl;
+
+ if (ma_set.placeholder ())
+ {
+ if (!ma_get.synthesized)
+ os << "// From " << location_string (ma_get.loc, true) << endl;
+
+ if (inc == "1")
+ os << ma_set.translate (
+ obj, ma_get.translate ("obj") + " + 1") << ";";
+ else
+ os << ma_set.translate (obj, inc) << ";";
+ }
+ else
+ {
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (ma_set.direct () && const_type (opt->type ()));
+ if (cast)
+ os << "const_cast< version_type& > (" << endl;
+
+ os << ma_set.translate (obj);
+
+ if (cast)
+ os << ")";
+
+ if (inc == "1")
+ os << "++;";
+ else
+ os << " = " << inc << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ section_extra (s);
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ protected:
+ semantics::class_& c_;
+ string scope_;
+ };
+
// Output a list of parameters for the persist statement.
//
struct persist_statement_params: object_columns_base, virtual context
@@ -3793,7 +4917,8 @@ namespace relational
object_extra (type&) {}
virtual void
- container_cache_extra_args (bool /*used*/) {}
+ extra_statement_cache_extra_args (bool /*containers*/,
+ bool /*sections*/) {}
virtual void
object_query_statement_ctor_args (type&,
@@ -3815,15 +4940,15 @@ namespace relational
}
virtual string
- optimimistic_version_init (semantics::data_member&)
+ optimistic_version_init (semantics::data_member&)
{
return "1";
}
- // Returning "1" means incremenet by one.
+ // Returning "1" means increment by one.
//
virtual string
- optimimistic_version_increment (semantics::data_member&)
+ optimistic_version_increment (semantics::data_member&)
{
return "1";
}
@@ -4078,6 +5203,9 @@ namespace relational
if (features.view)
os << "#include <odb/" << db << "/view-statements.hxx>" << endl;
+ if (features.section)
+ os << "#include <odb/" << db << "/section-statements.hxx>" << endl;
+
os << "#include <odb/" << db << "/container-statements.hxx>" << endl
<< "#include <odb/" << db << "/exceptions.hxx>" << endl;
diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx
index 35ec91b..664af35 100644
--- a/odb/relational/sqlite/context.cxx
+++ b/odb/relational/sqlite/context.cxx
@@ -123,8 +123,8 @@ namespace relational
{
struct has_grow: traversal::class_
{
- has_grow (bool& r)
- : r_ (r)
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
{
*this >> inherits_ >> *this;
}
@@ -137,7 +137,7 @@ namespace relational
if (!(context::object (c) || context::composite (c)))
return;
- if (c.count ("sqlite-grow"))
+ if (section_ == 0 && c.count ("sqlite-grow"))
r_ = c.get<bool> ("sqlite-grow");
else
{
@@ -148,29 +148,41 @@ namespace relational
if (!r_)
names (c);
- c.set ("sqlite-grow", r_);
+ if (section_ == 0)
+ c.set ("sqlite-grow", r_);
}
}
private:
bool& r_;
+ user_section* section_;
traversal::inherits inherits_;
};
struct has_grow_member: member_base
{
has_grow_member (bool& r,
+ user_section* section = 0,
semantics::type* type = 0,
string const& key_prefix = string ())
- : relational::member_base (type, string (), key_prefix),
+ : relational::member_base (type, string (), key_prefix, section),
r_ (r)
{
}
+ virtual bool
+ pre (member_info& mi)
+ {
+ return (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
virtual void
traverse_composite (member_info& mi)
{
// By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
//
r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
}
@@ -187,14 +199,14 @@ namespace relational
}
bool context::
- grow_impl (semantics::class_& c)
+ grow_impl (semantics::class_& c, user_section* section)
{
- if (c.count ("sqlite-grow"))
+ if (section == 0 && c.count ("sqlite-grow"))
return c.get<bool> ("sqlite-grow");
bool r (false);
- has_grow ct (r);
- has_grow_member mt (r);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
traversal::names names;
ct >> names >> mt;
ct.traverse (c);
@@ -214,7 +226,7 @@ namespace relational
grow_impl (semantics::data_member& m, semantics::type& t, string const& kp)
{
bool r (false);
- has_grow_member mt (r, &t, kp);
+ has_grow_member mt (r, 0, &t, kp);
mt.traverse (m);
return r;
}
diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx
index a3721df..95e4ae6 100644
--- a/odb/relational/sqlite/context.hxx
+++ b/odb/relational/sqlite/context.hxx
@@ -67,7 +67,7 @@ namespace relational
convert_expr (string const&, semantics::data_member&, bool);
virtual bool
- grow_impl (semantics::class_&);
+ grow_impl (semantics::class_&, user_section*);
virtual bool
grow_impl (semantics::data_member&);
diff --git a/odb/validator.cxx b/odb/validator.cxx
index efe9236..080aaa4 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -4,11 +4,13 @@
#include <odb/gcc.hxx>
+#include <set>
#include <iostream>
#include <odb/traversal.hxx>
#include <odb/common.hxx>
#include <odb/context.hxx>
+#include <odb/diagnostics.hxx>
#include <odb/validator.hxx>
#include <odb/relational/validator.hxx>
@@ -110,6 +112,50 @@ 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))
+ {
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: data member belonging to a section can only be a " <<
+ "direct member of a persistent class" << endl;
+ valid_ = false;
+ }
+
+ if (t.fq_name () == "::odb::section")
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: section data member can only be a direct member of a " <<
+ "persistent class" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Make sure the load and update pragmas are only specified on
+ // section members.
+ //
+ if (t.fq_name () != "::odb::section")
+ {
+ if (m.count ("section-load"))
+ {
+ location_t loc (m.get<location_t> ("section-load-location"));
+ error (loc) << "'#pragma db load' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+
+ if (m.count ("section-update"))
+ {
+ location_t loc (m.get<location_t> ("section-update-location"));
+ error (loc) << "'#pragma db update' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+ }
+
// Resolve null overrides.
//
override_null (m);
@@ -438,9 +484,16 @@ namespace
if (id->count ("default"))
{
os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: object id member cannot have default value"
- << endl;
+ << ": error: object id member cannot have default value" << endl;
+ valid_ = false;
+ }
+ // Complain if an id member is in a section.
+ //
+ if (id->count ("section-member"))
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: object id member cannot be in a section" << endl;
valid_ = false;
}
@@ -464,7 +517,7 @@ namespace
// Make sure we have the class declared optimistic.
//
- if (&optimistic->scope () == &c && !c.count ("optimistic"))
+ if (&m.scope () == &c && !c.count ("optimistic"))
{
os << m.file () << ":" << m.line () << ":" << m.column () << ":"
<< " error: version data member in a class not declared "
@@ -490,7 +543,7 @@ namespace
// Make sure id and version members are in the same class. The
// current architecture relies on that.
//
- if (id != 0 && &id->scope () != &optimistic->scope ())
+ if (id != 0 && &id->scope () != &m.scope ())
{
os << c.file () << ":" << c.line () << ":" << c.column () << ":"
<< " error: object id and version members are in different "
@@ -519,6 +572,15 @@ namespace
valid_ = false;
}
+ // Complain if the version member is in a section.
+ //
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: version member cannot be in a section" << endl;
+ valid_ = false;
+ }
+
// This takes care of also marking derived classes as optimistic.
//
c.set ("optimistic-member", optimistic);
@@ -571,6 +633,28 @@ namespace
if (poly_root != 0)
c.set ("polymorphic-root", poly_root);
+ // Sectionable objects.
+ //
+ if (c.count ("sectionable"))
+ {
+ if (optimistic == 0)
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class can be sectionable" << endl;
+ valid_ = false;
+ }
+ else if (&optimistic->scope () != &c && poly_root != &c)
+ {
+ location l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class that declares the version " <<
+ "data member or that is a root of a polymorphic hierarchy can " <<
+ "be sectionable" << endl;
+ info (optimistic->location ()) << "version member is declared " <<
+ "here" << endl;
+ valid_ = false;
+ }
+ }
+
// Update features set based on this object.
//
if (options.at_once () || class_file (c) == unit.file ())
@@ -878,6 +962,56 @@ namespace
virtual void
traverse_object (type& c)
{
+ bool poly (polymorphic (c));
+
+ // Make sure we have no empty or pointless sections unless we
+ // are reuse-abstract or polymorphic.
+ //
+ if (!poly && !abstract (c))
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ // Skip the special version update section (we always treat it
+ // as abstract in reuse inheritance).
+ //
+ if (s.special == user_section::special_version)
+ continue;
+
+ semantics::data_member& m (*s.member);
+ location const& l (m.location ());
+
+ if (s.total == 0 && !s.containers)
+ {
+ error (l) << "empty section" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ continue;
+ }
+
+ // Eager-loaded section with readonly members.
+ //
+ if (s.load == user_section::load_eager && s.update_empty ())
+ {
+ error (l) << "eager-loaded section with readonly members is " <<
+ "pointless" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
if (semantics::data_member* id = id_member (c))
{
semantics::type& t (utype (*id));
@@ -937,6 +1071,20 @@ namespace
}
}
}
+ else
+ {
+ // Make sure an object without id has no sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ if (!uss.empty ())
+ {
+ semantics::data_member& m (*uss.front ().member);
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: object without id cannot have sections" << endl;
+ valid_ = false;
+ }
+ }
}
virtual void