From 8e69f40ab32dc8604b68f360ae30fa961ba036ee Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 4 Feb 2015 17:23:54 +0200 Subject: Implement object loading views See section 10.2 in the manual for details. --- odb/common.cxx | 6 +- odb/context.cxx | 62 +- odb/context.hxx | 46 +- odb/header.cxx | 6 +- odb/instance.hxx | 17 + odb/pragma.cxx | 23 +- odb/processor.cxx | 129 +++- odb/relational/common.cxx | 9 + odb/relational/common.hxx | 42 +- odb/relational/common.txx | 26 +- odb/relational/header.hxx | 75 +++ odb/relational/mssql/common.cxx | 9 + odb/relational/mssql/common.hxx | 6 +- odb/relational/mssql/header.cxx | 41 +- odb/relational/mssql/source.cxx | 4 +- odb/relational/mysql/common.cxx | 9 + odb/relational/mysql/common.hxx | 6 +- odb/relational/mysql/header.cxx | 42 +- odb/relational/mysql/source.cxx | 147 +---- odb/relational/oracle/common.cxx | 9 + odb/relational/oracle/common.hxx | 6 +- odb/relational/oracle/header.cxx | 42 +- odb/relational/oracle/source.cxx | 4 +- odb/relational/pgsql/common.cxx | 9 + odb/relational/pgsql/common.hxx | 6 +- odb/relational/pgsql/header.cxx | 42 +- odb/relational/pgsql/source.cxx | 158 +---- odb/relational/processor.cxx | 97 +-- odb/relational/source.cxx | 1105 ++++++++++++++++++---------------- odb/relational/source.hxx | 1231 ++++++++++++++++++++++++++++++++++++-- odb/relational/sqlite/common.cxx | 9 + odb/relational/sqlite/common.hxx | 7 +- odb/relational/sqlite/header.cxx | 41 +- odb/relational/sqlite/source.cxx | 147 +---- odb/relational/validator.cxx | 16 +- 35 files changed, 2371 insertions(+), 1263 deletions(-) (limited to 'odb') diff --git a/odb/common.cxx b/odb/common.cxx index b75d323..ed586c9 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -18,7 +18,8 @@ traverse_simple (semantics::data_member&) void object_members_base:: traverse_pointer (semantics::data_member& m, semantics::class_& c) { - traverse_member (m, utype (*id_member (c))); + if (!view_member (m)) // Not really "as if" pointed-to id member. + traverse_member (m, utype (*id_member (c))); } void object_members_base:: @@ -244,7 +245,8 @@ traverse_column (semantics::data_member&, string const&, bool) void object_columns_base:: traverse_pointer (semantics::data_member& m, semantics::class_& c) { - traverse_member (m, utype (*id_member (c))); + if (!view_member (m)) // Not really "as if" pointed-to id column. + traverse_member (m, utype (*id_member (c))); } void object_columns_base:: diff --git a/odb/context.cxx b/odb/context.cxx index e56b412..44d7ef9 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,12 @@ add_space (string& s) string member_access:: translate (string const& obj, string const& val) const { + if (empty ()) + { + error (loc) << "non-empty " << kind << " expression required" << endl; + throw operation_failed (); + } + // This code is similar to translate_expression() from relations/source.cxx. // string r; @@ -2400,18 +2407,59 @@ namespace virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { - size_t t (c_.total); + // Object pointers in views require special treatment. + // + if (view_member (m)) + { + using semantics::class_; + + column_count_type cc; + + if (class_* root = polymorphic (c)) + { + // For a polymorphic class we are going to load all the members + // from all the bases (i.e., equivalent to the first statement + // in the list of SELECT statements generated for the object). + // So our count should be the same as the first value in the + // generated column_counts array. + // + for (class_* b (&c);; b = &polymorphic_base (*b)) + { + column_count_type const& ccb (column_count (*b, section_)); + + cc.total += ccb.total - (b != root ? ccb.id : 0); + cc.separate_load += ccb.separate_load; + cc.soft += ccb.soft; - object_members_base::traverse_pointer (m, c); + if (b == root) + break; + } + } + else + cc = column_count (c, section_); - if (context::inverse (m)) + c_.total += cc.total - cc.separate_load; + + if (added (member_path_) != 0 || deleted (member_path_) != 0) + c_.soft += cc.total; + else + c_.soft += cc.soft; + } + else { - size_t n (c_.total - t); + size_t t (c_.total); + + object_members_base::traverse_pointer (m, c); - c_.inverse += n; + if (context::inverse (m)) + { + size_t n (c_.total - t); - if (separate_update (member_path_)) - c_.separate_update -= n; + c_.inverse += n; + + if (separate_update (member_path_)) + c_.separate_update -= n; + } } } diff --git a/odb/context.hxx b/odb/context.hxx index 9538389..34f9692 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -84,7 +84,13 @@ enum class_kind // that lead all the way from the object member to the innermost // composite value member. // -typedef std::vector data_member_path; +struct data_member_path: std::vector +{ + data_member_path () {} + + explicit + data_member_path (semantics::data_member& m) {push_back (&m);} +}; // Class inheritance chain, from the most derived to base. // @@ -162,6 +168,7 @@ struct view_object tree scope; location_t loc; semantics::class_* obj; + semantics::data_member* ptr; // Corresponding object pointer, if any. cxx_tokens cond; // Join condition tokens. }; @@ -173,6 +180,21 @@ typedef std::vector view_objects; typedef std::map view_alias_map; typedef std::map view_object_map; +// Collection of relationships via which the objects are joined. +// We need this information to figure out which alias/table +// names to use for columns corresponding to inverse object +// pointers inside objects that this view may be loading. +// +// The first object is the pointer (i.e., the one containing +// this data member) while the other is the pointee. In other +// words, first -> second. We always store the direct (i.e., +// non-inverse) side of the relationship. Note also that there +// could be multiple objects joined using the same relationship. +// +typedef +std::multimap > +view_relationship_map; + // // struct view_query @@ -232,8 +254,8 @@ struct column_expr: std::vector // struct member_access { - member_access (const location& l, bool s) - : loc (l), synthesized (s), by_value (false) {} + member_access (const location& l, const char* k, bool s) + : loc (l), kind (k), synthesized (s), by_value (false) {} // Return true of we have the (?) placeholder. // @@ -249,11 +271,21 @@ struct member_access return synthesized && expr.size () == 3; // this.member } + bool + empty () const + { + return expr.empty (); + } + + // Issues diagnostics and throws operation_failed if expression is + // empty. + // std::string translate (std::string const& obj, std::string const& val = std::string ()) const; location loc; + const char* kind; // accessor/modifier; used for diagnostics. bool synthesized; // If true, then this is a synthesized expression. cxx_tokens expr; bool by_value; // True if accessor returns by value. False doesn't @@ -623,6 +655,14 @@ public: return t.count ("view"); } + // Direct member of a view. + // + static bool + view_member (semantics::data_member& m) + { + return view (dynamic_cast (m.scope ())); + } + // Check whether the type is a wrapper. Return the wrapped type if // it is a wrapper and NULL otherwise. Note that the returned type // may be cvr-qualified. diff --git a/odb/header.cxx b/odb/header.cxx index fad1668..989adc3 100644 --- a/odb/header.cxx +++ b/odb/header.cxx @@ -758,6 +758,9 @@ namespace header if (ctx.features.polymorphic_object) os << "#include " << endl; // For discriminator. + if (ctx.options.std () >= cxx_version::cxx11) + os << "#include " << endl; // move() + os << endl; os << "#include " << endl @@ -819,7 +822,8 @@ namespace header os << "#include " << endl; if (ctx.features.view) - os << "#include " << endl; + os << "#include " << endl + << "#include " << endl; } os << endl diff --git a/odb/instance.hxx b/odb/instance.hxx index 461efd1..7047c08 100644 --- a/odb/instance.hxx +++ b/odb/instance.hxx @@ -212,6 +212,23 @@ struct instance x_ = factory_type::create (prototype); } + template + instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5, A6 a6) + { + base_type prototype (a1, a2, a3, a4, a5, a6); + x_ = factory_type::create (prototype); + } + + template + instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4, + A5 const& a5, A6 const& a6) + { + base_type prototype (a1, a2, a3, a4, a5, a6); + x_ = factory_type::create (prototype); + } + instance (instance const& i) { // This is tricky: use the other instance as a prototype. diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 4d0acae..7a3d6ec 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -1089,18 +1089,23 @@ handle_pragma (cxx_lexer& l, return; } - tt = l.next (tl, &tn); - - val = member_access (loc, false); - if (!parse_expression (l, tt, tl, tn, val.value ().expr, p)) - return; // Diagnostics has already been issued. + member_access ma (loc, p == "set" ? "modifier" : "accessor", false); - if (tt != CPP_CLOSE_PAREN) + tt = l.next (tl, &tn); + if (tt != CPP_CLOSE_PAREN) // Empty expression are ok. { - error (l) << "')' expected at the end of db pragma " << p << endl; - return; + if (!parse_expression (l, tt, tl, tn, ma.expr, p)) + return; // Diagnostics has already been issued. + + if (tt != CPP_CLOSE_PAREN) + { + error (l) << "')' expected at the end of db pragma " << p << endl; + return; + } } + val = ma; + // Convert access to the get/set pair. // if (p == "access") @@ -1109,6 +1114,8 @@ handle_pragma (cxx_lexer& l, add_pragma ( pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns); + ma.kind = "modifier"; + val = ma; name = "set"; } diff --git a/odb/processor.cxx b/odb/processor.cxx index a6f18e0..7a0efa3 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -350,7 +350,9 @@ namespace { found_type found (found_none); semantics::access const& a (m.named ().access ()); - member_access& ma (m.set (k, member_access (m.location (), true))); + member_access& ma ( + m.set ( + k, member_access (m.location (), kind, true))); // If this member is not virtual and is either public or if we // are a friend of this class, then go for the member directly. @@ -490,6 +492,10 @@ namespace } member_access& ma (m.get (k)); + + if (ma.empty ()) + return; + cxx_tokens& e (ma.expr); // If it is just a name, resolve it and convert to an appropriate @@ -1350,9 +1356,10 @@ namespace throw operation_failed (); } - // Make sure the pointed-to class has object id. + // Make sure the pointed-to class has object id unless it is in a + // view where we can load no-id objects. // - if (id_member (*c) == 0) + if (id_member (*c) == 0 && !view_member (m)) { os << m.file () << ":" << m.line () << ":" << m.column () << ": " << "error: pointed-to class '" << class_fq_name (*c) << "' " @@ -2014,6 +2021,96 @@ namespace tree container_traits_; }; + struct view_data_member: traversal::data_member, context + { + view_data_member (semantics::class_& c) + : view_ (c), + amap_ (c.get ("alias-map")), + omap_ (c.get ("object-map")) {} + + virtual void + traverse (semantics::data_member& m) + { + using semantics::data_member; + + if (transient (m)) + return; + + semantics::type& t (utype (m)); + + if (semantics::class_* c = object_pointer (t)) + { + location const& l (m.location ()); + + if (lazy_pointer (t)) + { + error (l) << "lazy object pointer in view" << endl; + throw operation_failed (); + } + + // Find the corresponding associated object. First see if this + // data member name matches any aliases. + // + view_alias_map::iterator i (amap_.find (m.name ())); + + if (i == amap_.end ()) + i = amap_.find (public_name (m, false)); + + view_object* vo (0); + + if (i != amap_.end ()) + { + vo = i->second; + + if (vo->obj != c) // @@ Poly base/derived difference? + { + error (l) << "different pointed-to and associated objects" << endl; + info (vo->loc) << "associated object is defined here" << endl; + throw operation_failed (); + } + } + else + { + // If there is no alias match, try the object type. + // + view_object_map::iterator i (omap_.find (c)); + + if (i == omap_.end ()) + { + error (l) << "unable to find associated object for object " + << "pointer" << endl; + info (l) << "use associated object alias as this data member " + << "name" << endl; + throw operation_failed (); + } + + vo = i->second; + } + + if (vo->ptr != 0) + { + location const& l2 (vo->ptr->location ()); + + error (l) << "associated object is already loaded via another " + << "object pointer" << endl; + info (l2) << "the other data member is defined here" << endl; + info (l2) << "use associated object alias as this data member " + << "name to load a different object" << endl; + + throw operation_failed (); + } + + vo->ptr = &m; + m.set ("view-object", vo); + } + } + + private: + semantics::class_& view_; + view_alias_map& amap_; + view_object_map& omap_; + }; + // Figure out the "summary" added/deleted version for a composite // value type. // @@ -2485,6 +2582,7 @@ namespace } i->obj = &o; + i->ptr = 0; // Nothing yet. if (i->alias.empty ()) { @@ -2546,6 +2644,14 @@ namespace virtual void traverse_view_post (type& c) { + // Handle data members. + // + { + view_data_member t (c); + traversal::names n (t); + names (c, n); + } + // Figure out if we are versioned. Forced versioning is handled // in relational/processing. // @@ -2594,6 +2700,7 @@ namespace try { + bool raw; string ptr; string const& type (class_fq_name (c)); @@ -2623,15 +2730,20 @@ namespace if (p == "*") { + raw = true; ptr = type + "*"; cp_template = true; } else if (p[p.size () - 1] == '*') + { + raw = true; ptr = p; + } else if (p.find ('<') != string::npos) { // Template-id. // + raw = false; // Fair to assume not raw, though technically can be. ptr = p; decl_name.assign (p, 0, p.find ('<')); } @@ -2645,6 +2757,7 @@ namespace if (tc == TYPE_DECL) { + raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE); ptr = p; // This can be a typedef'ed alias for a TR1 template-id. @@ -2663,6 +2776,7 @@ namespace } else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) { + raw = false; ptr = p + "< " + type + " >"; decl_name = p; cp_template = true; @@ -2713,7 +2827,10 @@ namespace // Namespace-specified pointer can only be '*' or are template. // if (p == "*") + { + raw = true; ptr = type + "*"; + } else if (p[p.size () - 1] == '*') { error (cp->loc) @@ -2735,6 +2852,7 @@ namespace if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) { + raw = false; ptr = p + "< " + type + " >"; decl_name = p; } @@ -2765,9 +2883,13 @@ namespace string const& p (options.default_pointer ()); if (p == "*") + { + raw = true; ptr = type + "*"; + } else { + raw = false; ptr = p + "< " + type + " >"; decl_name = p; } @@ -2917,6 +3039,7 @@ namespace } c.set ("object-pointer", ptr); + c.set ("object-pointer-raw", raw); } catch (invalid_name const& ex) { diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx index 5e20f5f..22dfe48 100644 --- a/odb/relational/common.cxx +++ b/odb/relational/common.cxx @@ -8,6 +8,15 @@ using namespace std; namespace relational { + // member_image_type + // + string member_image_type:: + image_type (semantics::data_member&) + { + assert (false); + return string (); + } + // member_database_type_id // string member_database_type_id:: diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index abe5e31..a70bef0 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -174,34 +174,44 @@ namespace relational // should be called for this member. // virtual bool - pre (member_info&) - { - return true; - } + pre (member_info&) {return true;} virtual void - post (member_info&) - { - } + post (member_info&) {} virtual void - traverse_composite (member_info&) - { - } + traverse_composite (member_info&) {} virtual void - traverse_container (member_info&) - { - } + traverse_container (member_info&) {} - // Note that by default traverse_object_pointer() will traverse the + // Note that by default traverse_pointer() will traverse the // pointed-to object id type. // virtual void - traverse_object_pointer (member_info&); + traverse_pointer (member_info&); virtual void - traverse_simple (member_info&) = 0; + traverse_simple (member_info&) {} + }; + + // + // + struct member_image_type: virtual member_base + { + typedef member_image_type base; + + member_image_type (semantics::type* type = 0, + string const& fq_type = string (), + string const& key_prefix = string ()) + : member_base (type, fq_type, key_prefix) + { + } + + // Has to be overriden. + // + virtual string + image_type (semantics::data_member&); }; // diff --git a/odb/relational/common.txx b/odb/relational/common.txx index 9f22b88..4b873dd 100644 --- a/odb/relational/common.txx +++ b/odb/relational/common.txx @@ -31,8 +31,11 @@ namespace relational semantics::type* cont; if (semantics::class_* c = object_pointer (t)) { - semantics::type& t (utype (*id_member (*c))); - semantics::class_* comp (composite_wrapper (t)); + // A pointer in view might point to an object without id. + // + semantics::data_member* idm (id_member (*c)); + semantics::type& t (utype (idm != 0 ? *idm : m)); + semantics::class_* comp (idm != 0 ? composite_wrapper (t) : 0); member_info mi (m, (comp != 0 ? *comp : t), @@ -43,12 +46,14 @@ namespace relational mi.ptr = c; - if (comp == 0) + // Pointer in views aren't really a "column". + // + if (!view_member (m) && comp == 0) mi.st = &member_sql_type (m); if (pre (mi)) { - traverse_object_pointer (mi); + traverse_pointer (mi); post (mi); } } @@ -102,11 +107,14 @@ namespace relational template void member_base_impl:: - traverse_object_pointer (member_info& mi) + traverse_pointer (member_info& mi) { - if (composite (mi.t)) // Already unwrapped. - traverse_composite (mi); - else - traverse_simple (mi); + if (!view_member (mi.m)) // Not really "as if" pointed-to id member. + { + if (composite (mi.t)) // Already unwrapped. + traverse_composite (mi); + else + traverse_simple (mi); + } } } diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 7a36d0e..dc20854 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -34,6 +34,81 @@ namespace relational } }; + template + struct image_member_impl: image_member, virtual member_base_impl + { + typedef image_member_impl base_impl; + + image_member_impl (base const& x) + : member_base::base (x), // virtual base + base (x), + member_image_type_ (base::type_override_, + base::fq_type_override_, + base::key_prefix_) + { + } + + typedef typename member_base_impl::member_info member_info; + + virtual bool + pre (member_info& mi) + { + if (container (mi)) + return false; + + image_type = member_image_type_->image_type (mi.m); + + if (var_override_.empty ()) + os << "// " << mi.m.name () << endl + << "//" << endl; + + return true; + } + + virtual void + traverse_pointer (member_info& mi) + { + // Object pointers in views require special treatment. + // + if (view_member (mi.m)) + { + using semantics::class_; + + class_& c (*mi.ptr); + class_* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + if (poly_derived) + // Use a helper to create a complete chain of images all + // the way to the root (see libodb/odb/view-image.hxx). + // + os << "view_object_image<" << endl + << " " << class_fq_name (c) << "," << endl + << " " << class_fq_name (*poly_root) << "," << endl + << " id_" << db << " >"; + else + os << "object_traits_impl< " << class_fq_name (c) << ", " << + "id_" << db << " >::image_type"; + + os << " " << mi.var << "value;" + << endl; + } + else + member_base_impl::traverse_pointer (mi); + } + + virtual void + traverse_composite (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << endl; + } + + protected: + string image_type; + instance member_image_type_; + }; + struct image_base: traversal::class_, virtual context { typedef image_base base; diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx index a97b78d..be35ca9 100644 --- a/odb/relational/mssql/common.cxx +++ b/odb/relational/mssql/common.cxx @@ -184,6 +184,13 @@ namespace relational }; member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -314,6 +321,8 @@ namespace relational type_ = "unsigned char*"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/mssql/common.hxx b/odb/relational/mssql/common.hxx index e597d4d..ec99776 100644 --- a/odb/relational/mssql/common.hxx +++ b/odb/relational/mssql/common.hxx @@ -119,12 +119,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/mssql/header.cxx b/odb/relational/mssql/header.cxx index ff16950..aa0e751 100644 --- a/odb/relational/mssql/header.cxx +++ b/odb/relational/mssql/header.cxx @@ -149,39 +149,14 @@ namespace relational }; entry image_type_; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -331,10 +306,6 @@ namespace relational << "SQLLEN " << mi.var << "size_ind;" << endl; } - - private: - string image_type; - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx index 2ce8810..ca36dfb 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/relational/mssql/source.cxx @@ -607,9 +607,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "size_ind == SQL_NULL_DATA"; + os << "i." << var << "size_ind == SQL_NULL_DATA"; } virtual void diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx index 011bad7..6833ace 100644 --- a/odb/relational/mysql/common.cxx +++ b/odb/relational/mysql/common.cxx @@ -150,6 +150,13 @@ namespace relational }; member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -233,6 +240,8 @@ namespace relational type_ = "details::buffer"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/mysql/common.hxx b/odb/relational/mysql/common.hxx index f6913ad..571ed51 100644 --- a/odb/relational/mysql/common.hxx +++ b/odb/relational/mysql/common.hxx @@ -81,12 +81,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/mysql/header.cxx b/odb/relational/mysql/header.cxx index bffb46d..189f005 100644 --- a/odb/relational/mysql/header.cxx +++ b/odb/relational/mysql/header.cxx @@ -15,39 +15,14 @@ namespace relational { namespace relational = relational::header; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -155,11 +130,6 @@ namespace relational << "my_bool " << mi.var << "null;" << endl; } - - private: - string image_type; - - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 0df66db..bcc7f23 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -301,142 +301,14 @@ namespace relational // grow // - struct grow_member: relational::grow_member, member_base + struct grow_member: relational::grow_member_impl, + member_base { grow_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - // Ignore polymorphic id references; they are not returned by - // the select statement. - // - if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) - return false; - - ostringstream ostr; - ostr << "t[" << index_ << "UL]"; - e = ostr.str (); - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - semantics::class_* comp (composite (mi.t)); - - // If the member is soft- added or deleted, check the version. - // - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - // If this is a composite member, see if it is summarily - // added/deleted. - // - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - // If the addition/deletion version is the same as the section's, - // then we don't need the test. - // - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - { - os << "if ("; - - if (av != 0) - os << "svm >= schema_version_migration (" << av << "ULL, true)"; - - if (av != 0 && dv != 0) - os << " &&" << endl; - - if (dv != 0) - os << "svm <= schema_version_migration (" << dv << "ULL, true)"; - - os << ")" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - semantics::class_* comp (composite (mi.t)); - - if (var_override_.empty ()) - { - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - os << "}"; - } - - if (comp != 0) - index_ += column_count (*comp).total; - else - index_++; - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (composite_value_traits< " << mi.fq_type () << - ", id_mysql >::grow (" << endl - << "i." << mi.var << "value, t + " << index_ << "UL" << - (versioned (*composite (mi.t)) ? ", svm" : "") << "))" << endl - << "grew = true;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info&) @@ -528,9 +400,6 @@ namespace relational << "grew = true;" << "}"; } - - private: - string e; }; entry grow_member_; @@ -694,9 +563,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "null"; + os << "i." << var << "null"; } virtual void diff --git a/odb/relational/oracle/common.cxx b/odb/relational/oracle/common.cxx index 9bfafde..a4f89a4 100644 --- a/odb/relational/oracle/common.cxx +++ b/odb/relational/oracle/common.cxx @@ -170,6 +170,13 @@ namespace relational // member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -270,6 +277,8 @@ namespace relational type_ = "oracle::lob_callback"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/oracle/common.hxx b/odb/relational/oracle/common.hxx index 7872e5a..fc21468 100644 --- a/odb/relational/oracle/common.hxx +++ b/odb/relational/oracle/common.hxx @@ -89,12 +89,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/oracle/header.cxx b/odb/relational/oracle/header.cxx index 055ef7b..8ce4a20 100644 --- a/odb/relational/oracle/header.cxx +++ b/odb/relational/oracle/header.cxx @@ -52,39 +52,14 @@ namespace relational }; entry image_type_; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_int32 (member_info& mi) @@ -219,11 +194,6 @@ namespace relational << "oracle::lob " << mi.var << "lob;" << endl; } - - private: - string image_type; - - member_image_type member_image_type_; }; entry image_member_; diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index 2a795e2..ea4a14b 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -375,9 +375,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "indicator == -1"; + os << "i." << var << "indicator == -1"; } virtual void diff --git a/odb/relational/pgsql/common.cxx b/odb/relational/pgsql/common.cxx index 41cfc6b..6f04824 100644 --- a/odb/relational/pgsql/common.cxx +++ b/odb/relational/pgsql/common.cxx @@ -126,6 +126,13 @@ namespace relational } member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -196,6 +203,8 @@ namespace relational type_ = "unsigned char*"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/pgsql/common.hxx b/odb/relational/pgsql/common.hxx index bb9d961..7fa8b60 100644 --- a/odb/relational/pgsql/common.hxx +++ b/odb/relational/pgsql/common.hxx @@ -69,12 +69,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx index a23ec9e..fd9093b 100644 --- a/odb/relational/pgsql/header.cxx +++ b/odb/relational/pgsql/header.cxx @@ -177,39 +177,14 @@ namespace relational }; entry section_traits_; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -290,11 +265,6 @@ namespace relational << "bool " << mi.var << "null;" << endl; } - - private: - string image_type; - - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index b3d934d..9606e71 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -267,153 +267,14 @@ namespace relational // grow // - struct grow_member: relational::grow_member, member_base + struct grow_member: relational::grow_member_impl, + member_base { grow_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x) - { - } - - virtual bool - pre (member_info& mi) - { - 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. - // - if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) - return false; - - ostringstream ostr; - ostr << "t[" << index_ << "UL]"; - e = ostr.str (); - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - semantics::class_* comp (composite (mi.t)); - - // If the member is soft- added or deleted, check the version. - // - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - // If this is a composite member, see if it is summarily - // added/deleted. - // - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - // If the addition/deletion version is the same as the section's, - // then we don't need the test. - // - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - { - os << "if ("; - - if (av != 0) - os << "svm >= schema_version_migration (" << av << "ULL, true)"; - - if (av != 0 && dv != 0) - os << " &&" << endl; - - if (dv != 0) - os << "svm <= schema_version_migration (" << dv << "ULL, true)"; - - os << ")" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - semantics::class_* comp (composite (mi.t)); - - if (var_override_.empty ()) - { - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - os << "}"; - } - - if (comp != 0) - index_ += column_count (*comp).total; - else - index_++; - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (composite_value_traits< " << mi.fq_type () << - ", id_pgsql >::grow (" << endl - << "i." << mi.var << "value, t + " << index_ << "UL" << - (versioned (*composite (mi.t)) ? ", svm" : "") << "))" << endl - << "grew = true;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info&) @@ -479,9 +340,6 @@ namespace relational os << e << " = 0;" << endl; } - - private: - string e; }; entry grow_member_; @@ -617,9 +475,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "null"; + os << "i." << var << "null"; } virtual void diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index b70451a..7658725 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -75,53 +75,61 @@ namespace relational if (semantics::class_* c = object_pointer (t)) { - // This is an object pointer. The column type is the pointed-to - // object id type. + // An object pointer in view doesn't really have a "column" + // so pretend that it has already been handled. // - semantics::data_member& id (*id_member (*c)); - - semantics::names* idhint; - semantics::type& idt (utype (id, idhint)); - - // The id type can be a composite value type. - // - if (composite_wrapper (idt)) - kind = composite; + if (view_member (m)) + kind = simple; else { - semantics::type* wt; - semantics::names* whint (0); - if ((wt = wrapper (idt, whint))) - wt = &utype (*wt, whint); - - if (type.empty () && id.count ("id-type")) - type = id.get ("id-type"); + // This is an object pointer. The column type is the pointed-to + // object id type. + // + semantics::data_member& id (*id_member (*c)); - if (type.empty () && id.count ("type")) - type = id.get ("type"); + semantics::names* idhint; + semantics::type& idt (utype (id, idhint)); - // The rest should be identical to the code for the id_type in - // the else block. + // The id type can be a composite value type. // - if (type.empty () && idt.count ("id-type")) - type = idt.get ("id-type"); + if (composite_wrapper (idt)) + kind = composite; + else + { + semantics::type* wt; + semantics::names* whint (0); + if ((wt = wrapper (idt, whint))) + wt = &utype (*wt, whint); - if (type.empty () && wt != 0 && wt->count ("id-type")) - type = wt->get ("id-type"); + if (type.empty () && id.count ("id-type")) + type = id.get ("id-type"); - if (type.empty () && idt.count ("type")) - type = idt.get ("type"); + if (type.empty () && id.count ("type")) + type = id.get ("type"); - if (type.empty () && wt != 0 && wt->count ("type")) - type = wt->get ("type"); + // The rest should be identical to the code for the id_type in + // the else block. + // + if (type.empty () && idt.count ("id-type")) + type = idt.get ("id-type"); - if (type.empty ()) - type = database_type (idt, idhint, true); + if (type.empty () && wt != 0 && wt->count ("id-type")) + type = wt->get ("id-type"); - if (type.empty () && wt != 0) - type = database_type (*wt, whint, true); + if (type.empty () && idt.count ("type")) + type = idt.get ("type"); - id_type = type; + if (type.empty () && wt != 0 && wt->count ("type")) + type = wt->get ("type"); + + if (type.empty ()) + type = database_type (idt, idhint, true); + + if (type.empty () && wt != 0) + type = database_type (*wt, whint, true); + + id_type = type; + } } } else @@ -454,6 +462,13 @@ namespace relational if (transient (m)) return; + semantics::type& t (utype (m)); + + // Object pointers are associated with objects. + // + if (object_pointer (t)) + return; + data_member* src_m (0); // Source member. // Resolve member references in column expressions. @@ -1493,10 +1508,16 @@ namespace relational // Ignore inverse sides of the same relationship to avoid // phony conflicts caused by the direct side that will end - // up in the relationship list as well. + // up in the relationship list as well. Unless the inverse + // member is in the polymorphic base in which case we will + // miss it since we don't examine inside poly bases on the + // backwards scan (see above). // - if (inverse (m)) - return; + if (semantics::data_member* im = inverse (m)) + { + if (&im->scope () == &c) // Direct member. + return; + } // Ignore self-pointers if requested. // diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index fad4ac5..39a210e 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -4741,6 +4741,594 @@ traverse_view (type& c) if (!schema_name.empty ()) schema_name = strlit (schema_name); + // Generate the from-list. Also collect relationships via which + // the objects are joined. + // + strings from; + view_relationship_map rel_map; + + if (vq.kind == view_query::condition) + { + view_objects& objs (c.get ("objects")); + + for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i) + { + bool first (i == objs.begin ()); + string l; + + // + // Tables. + // + + if (i->kind == view_object::table) + { + if (first) + { + l = "FROM "; + l += quote_id (i->tbl_name); + + if (!i->alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); + + from.push_back (l); + continue; + } + + l = "LEFT JOIN "; + l += quote_id (i->tbl_name); + + if (!i->alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); + + semantics::scope& scope ( + dynamic_cast (*unit.find (i->scope))); + + expression e ( + translate_expression ( + c, i->cond, scope, i->loc, "table")); + + if (e.kind != expression::literal) + { + error (i->loc) << "invalid join condition in db pragma " << + "table" << endl; + throw operation_failed (); + } + + l += " ON"; + + // Add the pragma location for easier error tracking. + // + from.push_back (l); + from.push_back ("// From " + location_string (i->loc, true)); + from.push_back (e.value); + continue; + } + + // + // Objects. + // + semantics::class_& o (*i->obj); + + bool poly (polymorphic (o)); + size_t poly_depth (poly ? polymorphic_depth (o) : 1); + + string alias (i->alias); + + // For polymorphic objects, alias is just a prefix. + // + if (poly && !alias.empty ()) + alias += "_" + table_name (o).uname (); + + // First object. + // + if (first) + { + l = "FROM "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + from.push_back (l); + + if (poly_depth != 1) + { + bool t (true); //@@ (im)perfect forwarding + size_t d (poly_depth - 1); //@@ (im)perfect forward. + instance j (o, t, d, i->alias); + j->traverse (polymorphic_base (o)); + + from.insert (from.end (), j->begin (), j->end ()); + query_optimize = query_optimize || !j->joins.empty (); + } + continue; + } + + semantics::scope& scope ( + dynamic_cast (*unit.find (i->scope))); + + expression e ( + translate_expression ( + c, i->cond, scope, i->loc, "object")); + + // Literal expression. + // + if (e.kind == expression::literal) + { + l = "LEFT JOIN "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + l += " ON"; + + // Add the pragma location for easier error tracking. + // + from.push_back (l); + from.push_back ("// From " + location_string (i->loc, true)); + from.push_back (e.value); + + if (poly_depth != 1) + { + bool t (true); //@@ (im)perfect forwarding + size_t d (poly_depth - 1); //@@ (im)perfect forward. + instance j (o, t, d, i->alias); + j->traverse (polymorphic_base (o)); + + from.insert (from.end (), j->begin (), j->end ()); + query_optimize = query_optimize || !j->joins.empty (); + } + continue; + } + + // We have an object relationship (pointer) for which we need + // to come up with the corresponding JOIN condition. If this + // is a to-many relationship, then we first need to JOIN the + // container table. This code is similar to object_joins. + // + using semantics::data_member; + + data_member& m (*e.member_path.back ()); + data_member* im (inverse (m)); + + // Resolve the pointed-to object to view_object and do + // some sanity checks while at it. + // + semantics::class_* c (0); + + if (semantics::type* cont = container (m)) + c = object_pointer (container_vt (*cont)); + else + c = object_pointer (utype (m)); + + view_object* vo (0); + + // Check if the pointed-to object has been previously associated + // with this view and is unambiguous. A pointer to ourselves is + // always assumed to point to this association. + // + if (&o == c) + vo = &*i; + else + { + bool ambig (false); + + for (view_objects::iterator j (objs.begin ()); j != i; ++j) + { + if (j->obj != c) + continue; + + if (vo == 0) + { + vo = &*j; + continue; + } + + // If it is the first ambiguous object, issue the + // error. + // + if (!ambig) + { + error (i->loc) << "pointed-to object '" << class_name (*c) << + "' is ambiguous" << endl; + info (i->loc) << "candidates are:" << endl; + info (vo->loc) << " '" << vo->name () << "'" << endl; + ambig = true; + } + + info (j->loc) << " '" << j->name () << "'" << endl; + } + + if (ambig) + { + info (i->loc) << "use the other side of the relationship " << + "or full join condition clause in db pragma object to " << + "resolve this ambiguity" << endl; + throw operation_failed (); + } + + if (vo == 0) + { + error (i->loc) << "pointed-to object '" << class_name (*c) << + "' specified in the join condition has not been " << + "previously associated with this view" << endl; + throw operation_failed (); + } + } + + // JOIN relationship points to us: + // vo - us + // e.vo - other side + // e.member_path - in other side + // + // JOIN relationship points to other side: + // vo - other side + // e.vo - us + // e.member_path - in us + // + if (im == 0) + rel_map.insert (make_pair (e.member_path, make_pair (e.vo, vo))); + else + rel_map.insert ( + make_pair (data_member_path (*im), make_pair (vo, e.vo))); + + // Left and right-hand side table names. + // + qname lt; + { + using semantics::class_; + + class_& o (*e.vo->obj); + string const& a (e.vo->alias); + + if (class_* root = polymorphic (o)) + { + // If the object is polymorphic, then figure out which of the + // bases this member comes from and use the corresponding + // table. + // + class_* c ( + &static_cast ( + e.member_path.front ()->scope ())); + + // If this member's class is not polymorphic (root uses reuse + // inheritance), then use the root table. + // + if (!polymorphic (*c)) + c = root; + + qname const& t (table_name (*c)); + + if (a.empty ()) + lt = t; + else + lt = qname (a + "_" + t.uname ()); + } + else + lt = a.empty () ? table_name (o) : qname (a); + } + + qname rt; + { + qname t (table_name (*vo->obj)); + string const& a (vo->alias); + rt = a.empty () + ? t + : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a); + } + + // First join the container table if necessary. + // + semantics::type* cont (container (im != 0 ? *im : m)); + + string ct; // Container table. + if (cont != 0) + { + l = "LEFT JOIN "; + + // The same relationship can be used by multiple associated + // objects. So if the object that contains this container has + // an alias, then also construct one for the table that we + // are joining. + // + { + using semantics::class_; + + qname t; + + // In a polymorphic hierarchy the member can be in a base (see + // above). + // + if (class_* root = polymorphic (im != 0 ? *vo->obj : *e.vo->obj)) + { + class_* c; + if (im == 0) + { + c = &static_cast (e.member_path.front ()->scope ()); + + if (!polymorphic (*c)) + c = root; + + t = table_name (*c, e.member_path); + } + else + { + c = &static_cast (im->scope ()); + + if (!polymorphic (*c)) + c = root; + + t = table_name (*im, table_prefix (*c)); + } + } + else + t = im != 0 + ? table_name (*im, table_prefix (*vo->obj)) + : table_name (*e.vo->obj, e.member_path); + + // The tricky part is to figure out which view_object, vo + // or e.vo we should use. We want to use the alias from the + // object that actually contains this container. The following + // might not make intuitive sense, but it has been verified + // with the truth table. + // + string const& a (im != 0 ? vo->alias : e.vo->alias); + + if (a.empty ()) + { + ct = quote_id (t); + l += ct; + } + else + { + ct = quote_id (a + '_' + t.uname ()); + l += quote_id (t); + l += (need_alias_as ? " AS " : " ") + ct; + } + } + + l += " ON"; + from.push_back (l); + + // If we are the pointed-to object, then we have to turn + // things around. This is necessary to have the proper + // JOIN order. There seems to be a pattern there but it + // is not yet intuitively clear what it means. + // + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + + if (im != 0) + { + if (&o == c) + { + // container.value = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < + } + else + { + // container.id = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse ( + *im, utype (id), "id", "object_id", vo->obj); + o_cols->traverse (id); + ot = &rt; + } + } + else + { + if (&o == c) + { + // container.id = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse ( + m, utype (id), "id", "object_id", e.vo->obj); + o_cols->traverse (id); + ot = < + } + else + { + // container.value = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; + } + } + + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + from.push_back (strlit (l)); + } + } + + l = "LEFT JOIN "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + l += " ON"; + from.push_back (l); + + if (cont != 0) + { + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + + if (im != 0) + { + if (&o == c) + { + // container.id = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (*im, utype (id), "id", "object_id", vo->obj); + o_cols->traverse (id); + ot = &rt; + } + else + { + // container.value = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < + } + } + else + { + if (&o == c) + { + // container.value = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; + } + else + { + // container.id = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj); + o_cols->traverse (id); + ot = < + } + } + + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + from.push_back (strlit (l)); + } + } + else + { + column_prefix col_prefix; + + if (im == 0) + col_prefix = column_prefix (e.member_path); + + instance l_cols (col_prefix); + instance r_cols; + + if (im != 0) + { + // our.id = pointed-to.pointer + // + l_cols->traverse (*id_member (*e.vo->obj)); + r_cols->traverse (*im); + } + else + { + // our.pointer = pointed-to.id + // + l_cols->traverse (*e.member_path.back ()); + r_cols->traverse (*id_member (*vo->obj)); + } + + for (object_columns_list::iterator b (l_cols->begin ()), i (b), + j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += quote_id (lt); + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (rt); + l += '.'; + l += quote_id (j->name); + + from.push_back (strlit (l)); + } + } + + if (poly_depth != 1) + { + bool t (true); //@@ (im)perfect forwarding + size_t d (poly_depth - 1); //@@ (im)perfect forward. + instance j (o, t, d, i->alias); + j->traverse (polymorphic_base (o)); + + from.insert (from.end (), j->begin (), j->end ()); + query_optimize = query_optimize || !j->joins.empty (); + } + } // End JOIN-generating for-loop. + } + + // Check that pointed-to objects inside objects that we are loading + // have session support enabled (required to have a shared copy). + // Also see if we need to throw if there is no session. + // + bool need_session (false); + if (vq.kind == view_query::condition) + { + view_objects& objs (c.get ("objects")); + for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i) + { + if (i->kind != view_object::object || i->ptr == 0) + continue; // Not an object or not loaded. + + instance t (*i, rel_map); + t->traverse (*i->obj); + need_session = need_session || t->session_; + } + } + os << "// " << class_name (c) << endl << "//" << endl << endl; @@ -4843,7 +5431,19 @@ traverse_view (type& c) os << endl; + if (need_session) + os << "if (!session::has_current ())" << endl + << "throw session_required ();" + << endl; + + if (has_a (c, test_pointer)) + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl; + + names (c, init_view_pointer_member_pre_names_); names (c, init_value_member_names_); + names (c, init_view_pointer_member_post_names_); os << "}"; } @@ -4926,512 +5526,11 @@ traverse_view (type& c) } else // vq.kind == view_query::condition { - // Generate the from-list. + // Use the from-list generated above. // - strings from; - view_objects const& objs (c.get ("objects")); - - for (view_objects::const_iterator i (objs.begin ()); - i != objs.end (); - ++i) - { - bool first (i == objs.begin ()); - string l; - - // - // Tables. - // - - if (i->kind == view_object::table) - { - if (first) - { - l = "FROM "; - l += quote_id (i->tbl_name); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - from.push_back (l); - continue; - } - - l = "LEFT JOIN "; - l += quote_id (i->tbl_name); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - semantics::scope& scope ( - dynamic_cast (*unit.find (i->scope))); - - expression e ( - translate_expression ( - c, i->cond, scope, i->loc, "table")); - - if (e.kind != expression::literal) - { - error (i->loc) << "invalid join condition in db pragma " << - "table" << endl; - throw operation_failed (); - } - - l += " ON"; - - // Add the pragma location for easier error tracking. - // - from.push_back (l); - from.push_back ("// From " + location_string (i->loc, true)); - from.push_back (e.value); - continue; - } - - // - // Objects. - // - semantics::class_& o (*i->obj); - - bool poly (polymorphic (o)); - size_t poly_depth (poly ? polymorphic_depth (o) : 1); - - string alias (i->alias); - - // For polymorphic objects, alias is just a prefix. - // - if (poly && !alias.empty ()) - alias += "_" + table_name (o).uname (); - - // First object. - // - if (first) - { - l = "FROM "; - l += table_qname (o); - - if (!alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (alias); - - from.push_back (l); - - if (poly_depth != 1) - { - bool t (true); //@@ (im)perfect forwarding - size_t d (poly_depth - 1); //@@ (im)perfect forward. - instance j (o, t, d, i->alias); - j->traverse (polymorphic_base (o)); - - from.insert (from.end (), j->begin (), j->end ()); - query_optimize = query_optimize || !j->joins.empty (); - } - continue; - } - - semantics::scope& scope ( - dynamic_cast (*unit.find (i->scope))); - - expression e ( - translate_expression ( - c, i->cond, scope, i->loc, "object")); - - // Literal expression. - // - if (e.kind == expression::literal) - { - l = "LEFT JOIN "; - l += table_qname (o); - - if (!alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (alias); - - l += " ON"; - - // Add the pragma location for easier error tracking. - // - from.push_back (l); - from.push_back ("// From " + location_string (i->loc, true)); - from.push_back (e.value); - - if (poly_depth != 1) - { - bool t (true); //@@ (im)perfect forwarding - size_t d (poly_depth - 1); //@@ (im)perfect forward. - instance j (o, t, d, i->alias); - j->traverse (polymorphic_base (o)); - - from.insert (from.end (), j->begin (), j->end ()); - query_optimize = query_optimize || !j->joins.empty (); - } - continue; - } - - // We have an object relationship (pointer) for which we need - // to come up with the corresponding JOIN condition. If this - // is a to-many relationship, then we first need to JOIN the - // container table. This code is similar to object_joins. - // - using semantics::data_member; - - data_member& m (*e.member_path.back ()); - - // Resolve the pointed-to object to view_object and do - // some sanity checks while at it. - // - semantics::class_* c (0); - - if (semantics::type* cont = container (m)) - c = object_pointer (container_vt (*cont)); - else - c = object_pointer (utype (m)); - - view_object const* vo (0); - - // Check if the pointed-to object has been previously - // associated with this view and is unambiguous. A - // pointer to ourselves is always assumed to point - // to this association. - // - if (&o == c) - vo = &*i; - else - { - bool ambig (false); - - for (view_objects::const_iterator j (objs.begin ()); - j != i; - ++j) - { - if (j->obj != c) - continue; - - if (vo == 0) - { - vo = &*j; - continue; - } - - // If it is the first ambiguous object, issue the - // error. - // - if (!ambig) - { - error (i->loc) << "pointed-to object '" << class_name (*c) << - "' is ambiguous" << endl; - info (i->loc) << "candidates are:" << endl; - info (vo->loc) << " '" << vo->name () << "'" << endl; - ambig = true; - } - - info (j->loc) << " '" << j->name () << "'" << endl; - } - - if (ambig) - { - info (i->loc) << "use the full join condition clause in db " << - "pragma object to resolve this ambiguity" << endl; - throw operation_failed (); - } - - if (vo == 0) - { - error (i->loc) << "pointed-to object '" << class_name (*c) << - "' specified in the join condition has not been " << - "previously associated with this view" << endl; - throw operation_failed (); - } - } - - // Left and right-hand side table names. - // - qname lt; - { - using semantics::class_; - - class_& o (*e.vo->obj); - string const& a (e.vo->alias); - - if (class_* root = polymorphic (o)) - { - // If the object is polymorphic, then figure out which of the - // bases this member comes from and use the corresponding - // table. - // - class_* c ( - &static_cast ( - e.member_path.front ()->scope ())); - - // If this member's class is not polymorphic (root uses reuse - // inheritance), then use the root table. - // - if (!polymorphic (*c)) - c = root; - - qname const& t (table_name (*c)); - - if (a.empty ()) - lt = t; - else - lt = qname (a + "_" + t.uname ()); - } - else - lt = a.empty () ? table_name (o) : qname (a); - } - - qname rt; - { - qname t (table_name (*vo->obj)); - string const& a (vo->alias); - rt = a.empty () - ? t - : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a); - } - - // First join the container table if necessary. - // - data_member* im (inverse (m)); - - semantics::type* cont (container (im != 0 ? *im : m)); - - string ct; // Container table. - if (cont != 0) - { - if (im != 0) - { - // For now a direct member can only be directly in - // the object scope. If this changes, the inverse() - // function would have to return a member path instead - // of just a single member. - // - ct = table_qname (*im, table_prefix (*vo->obj)); - } - else - ct = table_qname (*e.vo->obj, e.member_path); - - l = "LEFT JOIN "; - l += ct; - l += " ON"; - from.push_back (l); - - // If we are the pointed-to object, then we have to turn - // things around. This is necessary to have the proper - // JOIN order. There seems to be a pattern there but it - // is not yet intuitively clear what it means. - // - instance c_cols; // Container columns. - instance o_cols; // Object columns. - - qname* ot; // Object table (either lt or rt). - - if (im != 0) - { - if (&o == c) - { - // container.value = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (*im, utype (id), "value", "value"); - o_cols->traverse (id); - ot = < - } - else - { - // container.id = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse ( - *im, utype (id), "id", "object_id", vo->obj); - o_cols->traverse (id); - ot = &rt; - } - } - else - { - if (&o == c) - { - // container.id = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse ( - m, utype (id), "id", "object_id", e.vo->obj); - o_cols->traverse (id); - ot = < - } - else - { - // container.value = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (m, utype (id), "value", "value"); - o_cols->traverse (id); - ot = &rt; - } - } - - for (object_columns_list::iterator b (c_cols->begin ()), i (b), - j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += ct; - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (*ot); - l += '.'; - l += quote_id (j->name); - - from.push_back (strlit (l)); - } - } - - l = "LEFT JOIN "; - l += table_qname (o); - - if (!alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (alias); - - l += " ON"; - from.push_back (l); - - if (cont != 0) - { - instance c_cols; // Container columns. - instance o_cols; // Object columns. - - qname* ot; // Object table (either lt or rt). - - if (im != 0) - { - if (&o == c) - { - // container.id = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (*im, utype (id), "id", "object_id", vo->obj); - o_cols->traverse (id); - ot = &rt; - } - else - { - // container.value = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (*im, utype (id), "value", "value"); - o_cols->traverse (id); - ot = < - } - } - else - { - if (&o == c) - { - // container.value = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (m, utype (id), "value", "value"); - o_cols->traverse (id); - ot = &rt; - } - else - { - // container.id = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj); - o_cols->traverse (id); - ot = < - } - } - - for (object_columns_list::iterator b (c_cols->begin ()), i (b), - j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += ct; - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (*ot); - l += '.'; - l += quote_id (j->name); - - from.push_back (strlit (l)); - } - } - else - { - column_prefix col_prefix; - - if (im == 0) - col_prefix = column_prefix (e.member_path); - - instance l_cols (col_prefix); - instance r_cols; - - if (im != 0) - { - // our.id = pointed-to.pointer - // - l_cols->traverse (*id_member (*e.vo->obj)); - r_cols->traverse (*im); - } - else - { - // our.pointer = pointed-to.id - // - l_cols->traverse (*e.member_path.back ()); - r_cols->traverse (*id_member (*vo->obj)); - } - - for (object_columns_list::iterator b (l_cols->begin ()), i (b), - j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += quote_id (lt); - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (rt); - l += '.'; - l += quote_id (j->name); - - from.push_back (strlit (l)); - } - } - - if (poly_depth != 1) - { - bool t (true); //@@ (im)perfect forwarding - size_t d (poly_depth - 1); //@@ (im)perfect forward. - instance j (o, t, d, i->alias); - j->traverse (polymorphic_base (o)); - - from.insert (from.end (), j->begin (), j->end ()); - query_optimize = query_optimize || !j->joins.empty (); - } - } // End JOIN-generating for-loop. - statement_columns sc; { - instance t (sc); + instance t (sc, from, rel_map); t->traverse (c); process_statement_columns ( sc, statement_select, versioned || query_optimize); diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index cbdec73..a725b6a 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -77,12 +77,29 @@ namespace relational { typedef object_columns base; + // If provided, used to resolve table/alias names for inverse + // pointed-to and base objects. Returns qualified name. + // + struct table_name_resolver + { + virtual string + resolve_pointer (semantics::data_member&) const = 0; + + virtual string + resolve_base (semantics::class_&) const = 0; + }; + object_columns (statement_kind sk, statement_columns& sc, query_parameters* param = 0, object_section* section = 0) : object_columns_base (true, true, section), - sk_ (sk), ro_ (true), sc_ (sc), param_ (param), depth_ (1) + sk_ (sk), + ro_ (true), + sc_ (sc), + param_ (param), + table_name_resolver_ (0), + depth_ (1) { } @@ -91,7 +108,12 @@ namespace relational statement_columns& sc, query_parameters* param) : object_columns_base (true, true, 0), - sk_ (sk), ro_ (ignore_ro), sc_ (sc), param_ (param), depth_ (1) + sk_ (sk), + ro_ (ignore_ro), + sc_ (sc), + param_ (param), + table_name_resolver_ (0), + depth_ (1) { } @@ -99,13 +121,15 @@ namespace relational statement_kind sk, statement_columns& sc, size_t depth = 1, - object_section* section = 0) + object_section* section = 0, + table_name_resolver* tnr = 0) : object_columns_base (true, true, section), sk_ (sk), ro_ (true), sc_ (sc), param_ (0), table_name_ (table_qname), + table_name_resolver_ (tnr), depth_ (depth) { } @@ -143,7 +167,12 @@ namespace relational if (sk_ == statement_select && --depth_ != 0) { - table_name_ = table_qname (polymorphic_base (c)); + semantics::class_& b (polymorphic_base (c)); + + table_name_ = table_name_resolver_ != 0 + ? table_name_resolver_->resolve_base (b) + : table_qname (b); + inherits (c); } } @@ -196,7 +225,12 @@ namespace relational string table; if (!table_name_.empty ()) - table = table_qname (*im, table_prefix (imc)); + { + if (table_name_resolver_ != 0) + table = table_name_resolver_->resolve_pointer (m); + else + table = table_qname (*im, table_prefix (imc)); + } instance oc (table, sk_, sc_); oc->traverse (*im, idt, "id", "object_id", &imc); @@ -219,32 +253,37 @@ namespace relational if (!table_name_.empty ()) { - string n; - - if (composite_wrapper (idt)) - { - n = column_prefix (m, key_prefix_, default_name_).prefix; - - if (n.empty ()) - n = public_name_db (m); - else if (n[n.size () - 1] == '_') - n.resize (n.size () - 1); // Remove trailing underscore. - } + if (table_name_resolver_ != 0) + alias = table_name_resolver_->resolve_pointer (m); else { - bool dummy; - n = column_name (m, key_prefix_, default_name_, dummy); - } + string n; - alias = compose_name (column_prefix_.prefix, n); + if (composite_wrapper (idt)) + { + n = column_prefix (m, key_prefix_, default_name_).prefix; - if (poly) - { - qname const& table (table_name (imc)); - alias = quote_id (alias + "_" + table.uname ()); + if (n.empty ()) + n = public_name_db (m); + else if (n[n.size () - 1] == '_') + n.resize (n.size () - 1); // Remove trailing underscore. + } + else + { + bool dummy; + n = column_name (m, key_prefix_, default_name_, dummy); + } + + alias = compose_name (column_prefix_.prefix, n); + + if (poly) + { + qname const& table (table_name (imc)); + alias = quote_id (alias + "_" + table.uname ()); + } + else + alias = quote_id (alias); } - else - alias = quote_id (alias); } instance oc (alias, sk_, sc_); @@ -312,14 +351,233 @@ namespace relational statement_columns& sc_; query_parameters* param_; string table_name_; + table_name_resolver* table_name_resolver_; size_t depth_; }; - struct view_columns: object_columns_base, virtual context + struct view_columns: object_columns_base, + object_columns::table_name_resolver, + virtual context { typedef view_columns base; - view_columns (statement_columns& sc): sc_ (sc), in_composite_ (false) {} + view_columns (statement_columns& sc, + strings& from, + const view_relationship_map& rm) + : sc_ (sc), from_ (from), rel_map_ (rm), in_composite_ (false) {} + + // Implementation of table_name_resolver for object_columns. + // + virtual string + resolve_pointer (semantics::data_member& m) const + { + view_object& us (*ptr_->get ("view-object")); + + semantics::data_member& im (*inverse (m)); + + typedef view_relationship_map::const_iterator iterator; + + std::pair r ( + rel_map_.equal_range (data_member_path (im))); + + using semantics::class_; + + for (; r.first != r.second; ++r.first) + { + if (r.first->second.second != &us) // Not our associated. + continue; + + view_object& vo (*r.first->second.first); // First because inverse. + + // Derive the table name the same way as the JOIN code. + // + class_* c (vo.obj); + if (class_* root = polymorphic (*c)) + { + // Can be in base. + // + c = &static_cast (im.scope ()); + + if (!polymorphic (*c)) + c = root; + } + + string const& a (vo.alias); + + if (container (im)) + { + // If this is a container, then object_columns will use the + // column from the container table, not from the object table + // (which, strictly speaking, might not have been JOIN'ed). + // + qname t (table_name (im, table_prefix (*c))); + return a.empty () + ? quote_id (t) + : quote_id (a + '_' + t.uname ()); + } + else + { + qname t; + if (a.empty ()) + t = table_name (*c); + else + { + if (polymorphic (*c)) + t = qname (a + "_" + table_name (*c).uname ()); + else + t = qname (a); + } + return quote_id (t); + } + } + + // So there is no associated object for this column. The initial + // plan was to complain and ask the user to explicitly associate + // the object. This is not a bad plan except for one thing: if + // the direct side of the relationship is a container, then + // associating that object explicitly will result in both the + // container table and the object table being JOIN'ed. But we + // only need the container table (for the object id) So we will + // be joining a table for nothing, which is not very clean. So + // the alternative, and more difficult, plan is to go ahead and + // add the necessary JOIN's automatically. + // + // This code follows the normal JOIN generation code. + // + class_* o (object_pointer (utype (m))); + if (class_* root = polymorphic (*o)) + { + o = &static_cast (im.scope ()); + + if (!polymorphic (*o)) + o = root; + } + + string const& a (us.alias); + string lt (a.empty () ? table_qname (*us.obj) : quote_id (a)); + string rt; + + qname ct ( + container (im) + ? table_name (im, table_prefix (*o)) + : table_name (*o)); + + string l ("LEFT JOIN "); + + if (a.empty ()) + { + rt = quote_id (ct); + l += rt; + } + else + { + // The same relationship can be used by multiple associated + // objects. So if we have an alias, then also construct one + // for the table that we are joining. + // + rt = quote_id (a + '_' + ct.uname ()); + l += quote_id (ct); + l += (need_alias_as ? " AS " : " ") + rt; + } + + l += " ON"; + from_.push_back (l); + + instance l_cols; // Our id columns. + instance r_cols; // Other side id columns. + + semantics::data_member& id (*id_member (*us.obj)); + + l_cols->traverse (id); + + if (container (im)) + r_cols->traverse (im, utype (id), "value", "value"); + else + r_cols->traverse (im); + + for (object_columns_list::iterator b (l_cols->begin ()), i (b), + j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += lt; + l += '.'; + l += quote_id (i->name); + l += '='; + l += rt; + l += '.'; + l += quote_id (j->name); + + from_.push_back (strlit (l)); + } + + return rt; + + /* + // The alternative implementation: + // + location const& l1 (m.location ()); + location const& l2 (ptr_->location ()); + + string n1 (class_name (*object_pointer (utype (m)))); + string n2 (class_name (*object_pointer (utype (*ptr_)))); + + error (l1) << "object '" << n1 << "' pointed-to by the inverse " + << "data member in object '" << n2 << "' must be " + << "explicitly associated with the view" << endl; + + info (l2) << "view data member that loads '" << n2 << "' is " + << "defined here" << endl; + + throw operation_failed (); + */ + } + + virtual string + resolve_base (semantics::class_& b) const + { + view_object& vo (*ptr_->get ("view-object")); + + qname t (vo.alias.empty () + ? table_name (b) + : qname (vo.alias + "_" + table_name (b).uname ())); + + return quote_id (t); + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1); + + view_object& vo (*m.get ("view-object")); + string const& a (vo.alias); + + qname t; + if (a.empty ()) + t = table_name (c); + else + { + if (poly) + t = qname (a + "_" + table_name (c).uname ()); + else + t = qname (a); + } + string qt (quote_id (t)); + + ptr_ = &m; + + statement_kind sk (statement_select); // Imperfect forwarding. + object_section* s (&main_section); // Imperfect forwarding. + instance oc (qt, sk, sc_, poly_depth, s, this); + oc->traverse (c); + } virtual void traverse_composite (semantics::data_member* pm, semantics::class_& c) @@ -492,8 +750,15 @@ namespace relational protected: statement_columns& sc_; + strings& from_; + const view_relationship_map& rel_map_; + bool in_composite_; qname table_prefix_; // Table corresponding to column_prefix_; + + // Set to the current pointer data member that we are traversing. + // + semantics::data_member* ptr_; }; struct polymorphic_object_joins: object_columns_base, virtual context @@ -909,6 +1174,148 @@ namespace relational instance id_cols_; }; + // Check that eager object pointers in the objects that a view loads + // can be loaded from the cache (i.e., they have session support + // enabled). + // + struct view_object_check: object_members_base + { + typedef view_object_check base; + + view_object_check (view_object& vo, view_relationship_map& rm) + : object_members_base (false, &main_section), + session_ (false), vo_ (vo), rel_map_ (rm) {} + + virtual bool + section_test (data_member_path const& mp) + { + // Include eager loaded members into the main section. + // + object_section& s (section (mp)); + return *section_ == s || !s.separate_load (); + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // Ignore polymorphic id references. + // + if (m.count ("polymorphic-ref")) + return; + + check (m, inverse (m), utype (m), c); + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type& t) + { + semantics::type& vt (container_vt (t)); + semantics::data_member* im (inverse (m, "value")); + + if (semantics::class_* cvt = composite_wrapper (vt)) + { + // Check this composite value for any pointers. + // + instance t (vo_, rel_map_); + t->traverse (*cvt); + + session_ = session_ || t->session_; + } + else if (semantics::class_* c = object_pointer (vt)) + check (m, im, vt, *c); + } + + void + check (semantics::data_member& m, + semantics::data_member* im, + semantics::type& pt, // Pointer type. + semantics::class_& c) + { + // We don't care about lazy pointers. + // + if (lazy_pointer (pt)) + return; + + // First check the pointed-to object recursively. + // + if (!c.count ("view-object-check-seen")) + { + c.set ("view-object-check-seen", true); + instance t (vo_, rel_map_); + t->traverse (c); + + // We may come again for another view. + // + c.remove ("view-object-check-seen"); + + session_ = session_ || t->session_; + } + + // See if the pointed-to object in this relationship is loaded + // by this view. + // + typedef view_relationship_map::const_iterator iterator; + + std::pair r ( + rel_map_.equal_range ( + im != 0 ? data_member_path (*im) : member_path_)); + + if (r.first == r.second) + return; // This relationship does not figure in the view. + + view_object& vo (*(im != 0 + ? r.first->second.first + : r.first->second.second)); + + assert (vo.obj == &c); // Got the above right? + + if (vo.ptr == 0) + return; // This object is not loaded by the view. + + // The pointed-to object in this relationship is loaded + // by the view. The hard question, of course, is whether + // it has anything to do with us. We assume it does. + // + if (!session (c)) + { + // Always direct data member. + // + semantics::class_& v ( + dynamic_cast (vo.ptr->scope ())); + + location const& l1 (c.location ()); + location const& l2 (m.location ()); + location const& l3 (vo_.ptr->location ()); + location const& l4 (vo.ptr->location ()); + + string on (class_name (c)); + string vn (class_name (v)); + + error (l1) << "object '" << on << "' has session support disabled " + << "but may be loaded by view '" << vn << "' via " + << "several data members" << endl; + + info (l2) << "indirectly via this data member..." << endl; + info (l3) << "...as a result of this object load" << endl; + info (l4) << "and directly as a result of this load" << endl; + info (l1) << "session support is required to only load one copy " + << "of the object" << endl; + info (l1) << "and don't forget to create a session instance when " + << "using this view" << endl; + + throw operation_failed (); + } + + session_ = true; + } + + bool session_; + + private: + view_object& vo_; + view_relationship_map& rel_map_; + }; + // // bind // @@ -1103,7 +1510,30 @@ namespace relational if (av != 0 || dv != 0) os << "}"; - if (comp != 0) + if (mi.ptr != 0 && view_member (mi.m)) + { + // See column_count_impl for details on what's going on here. + // + column_count_type cc; + if (semantics::class_* root = polymorphic (*mi.ptr)) + { + for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b)) + { + column_count_type const& ccb (column_count (*b)); + + cc.total += ccb.total - (b != root ? ccb.id : 0); + cc.separate_load += ccb.separate_load; + + if (b == root) + break; + } + } + else + cc = column_count (*mi.ptr); + + os << "n += " << cc.total - cc.separate_load << "UL;"; + } + else if (comp != 0) { bool ro (readonly (*comp)); column_count_type const& cc (column_count (*comp)); @@ -1169,6 +1599,26 @@ namespace relational } virtual void + traverse_pointer (member_info& mi) + { + // Object pointers in views require special treatment. + // + if (view_member (mi.m)) + { + semantics::class_& c (*mi.ptr); + semantics::class_* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + os << "object_traits_impl< " << class_fq_name (c) << ", id_" << + db << " >::bind (" << endl + << "b + n, " << (poly_derived ? "0, 0, " : "") << arg << "." << + mi.var << "value, sk" << (versioned (c) ? ", svm" : "") << ");"; + } + else + member_base_impl::traverse_pointer (mi); + } + + virtual void traverse_composite (member_info& mi) { os << "composite_value_traits< " << mi.fq_type () << ", id_" << @@ -1262,25 +1712,214 @@ namespace relational { typedef grow_member base; - grow_member (size_t& index, - string const& var = string (), - user_section* section = 0) - : member_base (var, 0, string (), string (), section), - index_ (index) + grow_member (size_t& index, + string const& var = string (), + user_section* section = 0) + : member_base (var, 0, string (), string (), section), + index_ (index) + { + } + + grow_member (size_t& index, + string const& var, + semantics::type& t, + string const& fq_type, + string const& key_prefix) + : member_base (var, &t, fq_type, key_prefix), index_ (index) + { + } + + protected: + size_t& index_; + }; + + template + struct grow_member_impl: grow_member, virtual member_base_impl + { + typedef grow_member_impl base_impl; + + grow_member_impl (base const& x) + : member_base::base (x), // virtual base + base (x) {} + + typedef typename member_base_impl::member_info member_info; + + virtual bool + pre (member_info& mi) + { + if (container (mi)) + return false; + + // Ignore polymorphic id references; they are not returned by + // the select statement. + // + if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) + return false; + + std::ostringstream ostr; + ostr << "t[" << index_ << "UL]"; + e = ostr.str (); + + if (var_override_.empty ()) + { + os << "// " << mi.m.name () << endl + << "//" << endl; + + semantics::class_* comp (composite (mi.t)); + + // If the member is soft- added or deleted, check the version. + // + unsigned long long av (added (mi.m)); + unsigned long long dv (deleted (mi.m)); + + // If this is a composite member, see if it is summarily + // added/deleted. + // + if (comp != 0) + { + unsigned long long cav (added (*comp)); + unsigned long long cdv (deleted (*comp)); + + if (cav != 0 && (av == 0 || av < cav)) + av = cav; + + if (cdv != 0 && (dv == 0 || dv > cdv)) + dv = cdv; + } + + // If the addition/deletion version is the same as the section's, + // then we don't need the test. + // + if (user_section* s = dynamic_cast (section_)) + { + if (av == added (*s->member)) + av = 0; + + if (dv == deleted (*s->member)) + dv = 0; + } + + if (av != 0 || dv != 0) + { + os << "if ("; + + if (av != 0) + os << "svm >= schema_version_migration (" << av << "ULL, true)"; + + if (av != 0 && dv != 0) + os << " &&" << endl; + + if (dv != 0) + os << "svm <= schema_version_migration (" << dv << "ULL, true)"; + + os << ")" + << "{"; + } + } + + return true; + } + + virtual void + post (member_info& mi) + { + semantics::class_* comp (composite (mi.t)); + + if (var_override_.empty ()) + { + unsigned long long av (added (mi.m)); + unsigned long long dv (deleted (mi.m)); + + if (comp != 0) + { + unsigned long long cav (added (*comp)); + unsigned long long cdv (deleted (*comp)); + + if (cav != 0 && (av == 0 || av < cav)) + av = cav; + + if (cdv != 0 && (dv == 0 || dv > cdv)) + dv = cdv; + } + + if (user_section* s = dynamic_cast (section_)) + { + if (av == added (*s->member)) + av = 0; + + if (dv == deleted (*s->member)) + dv = 0; + } + + if (av != 0 || dv != 0) + os << "}"; + } + + if (mi.ptr != 0 && view_member (mi.m)) + { + // See column_count_impl for details on what's going on here. + // + column_count_type cc; + if (semantics::class_* root = polymorphic (*mi.ptr)) + { + for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b)) + { + column_count_type const& ccb (column_count (*b)); + + cc.total += ccb.total - (b != root ? ccb.id : 0); + cc.separate_load += ccb.separate_load; + + if (b == root) + break; + } + } + else + cc = column_count (*mi.ptr); + + index_ += cc.total - cc.separate_load; + } + else if (comp != 0) + index_ += column_count (*comp).total; + else + index_++; + } + + virtual void + traverse_pointer (member_info& mi) { + // Object pointers in views require special treatment. They + // can only be immediate members of the view class. + // + if (view_member (mi.m)) + { + semantics::class_& c (*mi.ptr); + + os << "if (object_traits_impl< " << class_fq_name (c) << + ", id_" << db << " >::grow (" << endl + << "i." << mi.var << "value, t + " << index_ << "UL" << + (versioned (c) ? ", svm" : "") << "))" << endl + << "grew = true;" + << endl; + } + else + member_base_impl::traverse_pointer (mi); } - grow_member (size_t& index, - string const& var, - semantics::type& t, - string const& fq_type, - string const& key_prefix) - : member_base (var, &t, fq_type, key_prefix), index_ (index) + virtual void + traverse_composite (member_info& mi) { + semantics::class_& c (*composite (mi.t)); + + os << "if (composite_value_traits< " << mi.fq_type () << + ", id_" << db << " >::grow (" << endl + << "i." << mi.var << "value, t + " << index_ << "UL" << + (versioned (c) ? ", svm" : "") << "))" << endl + << "grew = true;" + << endl; } protected: - size_t& index_; + string e; }; struct grow_base: traversal::class_, virtual context @@ -1795,6 +2434,9 @@ namespace relational { } + virtual void + get_null (string const& /*var*/) const {}; + protected: string member_override_; bool ignore_implicit_discriminator_; @@ -1817,7 +2459,7 @@ namespace relational typedef typename member_base_impl::member_info member_info; virtual void - get_null (member_info&) = 0; + get_null (string const& var) const = 0; virtual void check_modifier (member_info&, member_access&) {} @@ -1909,6 +2551,9 @@ namespace relational os << "{"; + if (mi.ptr != 0 && view_member (mi.m)) + return true; // That's enough for the object pointer in view. + // Get the member using the accessor expression. // member_access& ma (mi.m.template get ("set")); @@ -2007,7 +2652,7 @@ namespace relational << "i." << mi.var << "value" << (versioned (*comp) ? ", svm" : "") << ")"; else - get_null (mi); + get_null (mi.var); os << ")" << endl; @@ -2046,6 +2691,13 @@ namespace relational { if (mi.ptr != 0) { + if (view_member (mi.m)) + { + // The object pointer in view doesn't need any of this. + os << "}"; + return; + } + // Restore the member variable name. // member = member_override_.empty () ? "v" : member_override_; @@ -2110,6 +2762,116 @@ namespace relational } virtual void + traverse_pointer (member_info& mi) + { + // Object pointers in views require special treatment. + // + if (view_member (mi.m)) + { + // This is the middle part. The pre and post parts are generated + // by init_view_pointer_member below. + // + using semantics::class_; + + class_& c (*mi.ptr); + class_* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + + string o_tp (mi.var + "object_type"); + string o_tr (mi.var + "object_traits"); + string r_tr (poly_derived ? mi.var + "root_traits" : o_tr); + string i_tp (mi.var + "info_type"); + + string id (mi.var + "id"); + string o (mi.var + "o"); + string pi (mi.var + "pi"); // Polymorphic type info. + + // If load_() will be loading containers or the rest of the + // polymorphic object, then we need to perform several extra + // things. We need to initialize the id image in the object + // statements. We also have to lock the statements so that + // nobody messes up this id image. + // + bool init_id ( + poly || + has_a (c, test_container | include_eager_load, &main_section)); + + bool versioned (context::versioned (c)); + + os << "if (" << o << " != 0)" + << "{"; + + if (poly) + os << "callback_event ce (callback_event::pre_load);" + << pi << "->dispatch (" << i_tp << "::call_callback, " << + "*db, " << o << ", &ce);"; + else + os << o_tr << "::callback (*db, *" << o << + ", callback_event::pre_load);"; + + os << o_tr << "::init (*" << o << ", i." << mi.var << "value, db" << + (versioned ? ", svm" : "") << ");"; + + // Call load_() to load the rest of the object (containers, etc). + // + if (id_member (poly ? *poly_root : c) != 0) + { + const char* s (poly_derived ? "osts" : "sts"); + + os << o_tr << "::statements_type& " << s << " (" << endl + << "conn.statement_cache ().find_object<" << o_tp << "> ());"; + + if (poly_derived) + os << r_tr << "::statements_type& sts (osts.root_statements ());"; + + if (init_id) + { + // This can only be top-level call so lock must succeed. + // + os << r_tr << "::statements_type::auto_lock l (sts);" + << endl + << r_tr << "::id_image_type& i (sts.id_image ());" + << r_tr << "::init (i, " << id << ");" + << db << "::binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || " << + "idb.version == 0)" + << "{" + << r_tr << "::bind (idb.bind, i);" + << "sts.id_image_version (i.version);" + << "idb.version++;"; + if (optimistic (poly ? *poly_root : c) != 0) + os << "sts.optimistic_id_image_binding ().version++;"; + os << "}"; + } + + os << o_tr << "::load_ (" << s << ", *" << o << ", false" << + (versioned ? ", svm" : "") << ");"; + + // Load the dynamic part of the object unless static and dynamic + // types are the same. + // + if (poly) + os << endl + << "if (" << pi << " != &" << o_tr << "::info)" + << "{" + << "std::size_t d (" << o_tr << "::depth);" + << pi << "->dispatch (" << i_tp << "::call_load, *db, " << + o << ", &d);" + << "}"; + + if (init_id) + os << "sts.load_delayed (" << (versioned ? "&svm" : "0") << ");" + << "l.unlock ();"; + } + + os << "}"; + } + else + member_base_impl::traverse_pointer (mi); + } + + virtual void traverse_composite (member_info& mi) { os << traits << "::init (" << endl @@ -2134,6 +2896,377 @@ namespace relational instance member_database_type_id_; }; + // This class generates the pre and post parts. The middle part is + // generated by init_value_member above. + // + struct init_view_pointer_member: virtual member_base, + member_base_impl // Dummy SQL type. + { + typedef init_view_pointer_member base; + + init_view_pointer_member (bool pre, init_value_member const& ivm) + : member_base ("", 0, "", "", 0), + pre_ (pre), init_value_member_ (ivm) {} + + virtual bool + pre (member_info& mi) + { + // Only interested in object pointers inside views. + // + return mi.ptr != 0 && view_member (mi.m); + } + + virtual void + traverse_pointer (member_info& mi) + { + using semantics::class_; + + class_& c (*mi.ptr); + bool abst (abstract (c)); + class_* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1); + + semantics::data_member* idm (id_member (poly ? *poly_root : c)); + + os << "// " << mi.m.name () << (pre_ ? " pre" : " post") << endl + << "//" << endl; + + string o_tp (mi.var + "object_type"); + string o_tr (mi.var + "object_traits"); + string r_tr (poly_derived ? mi.var + "root_traits" : o_tr); + string i_tp (mi.var + "info_type"); + string p_tp (mi.var + "pointer_type"); + string p_tr (mi.var + "pointer_traits"); + string c_tr (mi.var + "cache_traits"); + + string id (mi.var + "id"); // Object id. + string p (mi.var + "p"); // Object pointer. + string pg (mi.var + "pg"); // Pointer guard. + string ig (mi.var + "ig"); // Cache insert guard. + string o (mi.var + "o"); // Object. + string pi (mi.var + "pi"); // Polymorphic type info. + + bool op_raw (c.get ("object-pointer-raw")); + bool mp_raw (utype (mi.m).is_a ()); + + // Output aliases and variables before any schema version if- + // blocks since we need to be able to access them across all + // three parts. + // + if (pre_) + { + os << "typedef " << class_fq_name (c) << " " << o_tp << ";" + << "typedef object_traits_impl<" << o_tp << ", id_" << db << + "> " << o_tr << ";"; + + if (poly_derived) + os << "typedef " << o_tr << "::root_traits " << r_tr << ";"; + + if (poly) + os << "typedef " << r_tr << "::info_type " << i_tp << ";"; + + os << "typedef " << r_tr << "::pointer_type " << p_tp << ";" + << "typedef " << r_tr << "::pointer_traits " << p_tr << ";"; + if (idm != 0) + os << "typedef " << r_tr << "::pointer_cache_traits " << + c_tr << ";"; + os << endl; + + if (idm != 0) + os << r_tr << "::id_type " << id << ";"; + os << p_tp << " " << p << (op_raw ? " = 0" : "") << ";" // VC++ + << p_tr << "::guard " << pg << ";"; + if (idm != 0) + os << c_tr << "::insert_guard " << ig << ";"; + os << o_tp << "* " << o << " (0);"; + + if (poly) + os << "const " << i_tp << "* " << pi << " = 0;"; // VC++ + + os << endl; + } + + // If the member is soft- added or deleted, check the version. + // + unsigned long long av (added (mi.m)); + unsigned long long dv (deleted (mi.m)); + + if (av != 0 || dv != 0) + { + os << "if ("; + + if (av != 0) + os << "svm >= schema_version_migration (" << av << "ULL, true)"; + + if (av != 0 && dv != 0) + os << " &&" << endl; + + if (dv != 0) + os << "svm <= schema_version_migration (" << dv << "ULL, true)"; + + os << ")"; + } + + os << "{"; + + if (pre_) + { + string id_im; + if (idm != 0) + { + // Check for NULL. + // + string id_var; + { + id_im = mi.var + "value"; + + // In a polymorphic class, the id is in the root image. + // + for (size_t i (0); i < poly_depth - 1; ++i) + id_im += (i == 0 ? ".base" : "->base"); + + string const& n (idm->name ()); + id_var = id_im + (poly_derived ? "->" : ".") + n + + (n[n.size () - 1] == '_' ? "" : "_"); + + id_im = (poly_derived ? "*i." : "i.") + id_im; + } + + os << "if ("; + + if (semantics::class_* comp = composite (mi.t)) + os << "!composite_value_traits< " << o_tr << "::id_type, id_" << + db << " >::get_null (" << endl + << "i." << id_var << "value" << + (versioned (*comp) ? ", svm" : "") << ")"; + else + { + os << "!("; + init_value_member_.get_null (id_var); + os << ")"; + } + + os << ")" + << "{"; + + // Check cache. + // + os << id << " = " << r_tr << "::id (" << id_im << ");" + << p << " = " << c_tr << "::find (*db, " << id << ");" + << endl; + + os << "if (" << p_tr << "::null_ptr (" << p << "))" + << "{"; + } + + // To support by-value object loading, we are going to load + // into an existing instance if the pointer is already not + // NULL. To limit the potential misuse (especially when it + // comes to sessions), we are going to limit this support + // only to raw pointers. Furthermore, we will only insert + // such an object into the cache if its object pointer is + // also raw. + // + if (mp_raw && !poly) + { + // Get the member using the accessor expression. + // + member_access& ma (mi.m.get ("get")); + + // If this is not a synthesized expression, then output + // its location for easier error tracking. + // + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + // Use the original type to form the const reference. VC++ + // cannot grok the constructor syntax. + // + os << member_ref_type (mi.m, true, "m") << " =" << endl + << " " << ma.translate ("o") << ";" + << endl; + + os << "if (m != 0)" + << "{"; + + if (op_raw) + os << ig << ".reset (" << c_tr << "::insert (*db, " << id << + ", m));"; + + os << o << " = m;" + << "}" + << "else" + << "{"; + } + + if (poly) + { + os << r_tr << "::discriminator_type d (" << endl + << r_tr << "::discriminator (" << id_im << "));"; + + if (abst) + os << pi << " = &" << r_tr << "::map->find (d);"; + else + os << pi << " = (d == " << o_tr << "::info.discriminator" << endl + << "? &" << o_tr << "::info" << endl + << ": &" << r_tr << "::map->find (d));"; + + os << p << " = " << pi << "->create ();"; + } + else + os << p << " = object_factory<" << o_tp << ", " << p_tp << + ">::create ();"; + + os << pg << ".reset (" << p << ");"; + if (idm != 0) + os << ig << ".reset (" << c_tr << "::insert (*db, " << id << + ", " << p << "));"; + + if (poly_derived) + os << o << " = static_cast<" << o_tp << "*> (" << p_tr << + "::get_ptr (" << p << "));"; + else + os << o << " = " << p_tr << "::get_ptr (" << p << ");"; + + if (mp_raw && !poly) + os << "}"; + + if (idm != 0) + os << "}" // Cache. + << "}"; // NULL. + } + else + { + os << "if (" << o << " != 0)" + << "{"; + + if (poly) + os << "callback_event ce (callback_event::post_load);" + << pi << "->dispatch (" << i_tp << "::call_callback, " << + "*db, " << o << ", &ce);"; + else + os << o_tr << "::callback (*db, *" << o << + ", callback_event::post_load);"; + + if (idm != 0) + { + if (mp_raw && !op_raw && !poly) + os << "if (!" << p_tr << "::null_ptr (" << p << "))" + << "{"; + + os << c_tr << "::load (" << ig << ".position ());" + << ig << ".release ();"; + + if (mp_raw && !op_raw && !poly) + os << "}"; + } + + os << pg << ".release ();"; + + os << "}"; + + // If member pointer is not raw, then result is in p. + // If both member and object are raw, then result is in o. + // If member is raw but object is not, then result is in + // p if it is not NULL, and in o (either NULL or the same + // as the member value) otherwise. + // + member_access& ma (mi.m.get ("set")); + + if (ma.empty () && !poly) + { + // It is ok to have empty modifier expression as long as + // the member pointer is raw. This is the by-value load + // and the user is not interested in learning whether the + // object is NULL. + // + if (!mp_raw) + { + error (ma.loc) << "non-empty modifier expression required " << + "for loading an object via a smart pointer" << endl; + throw operation_failed (); + } + + os << "// Empty modifier expression was specified for this\n" + << "// object so make sure we have actually loaded the\n" + << "// data into the existing instance rather than, say,\n" + << "// finding the object in the cache or creating a new one.\n" + << "//\n" + << "assert (" << p_tr << "::null_ptr (" << p << "));"; + } + else + { + if (!(mp_raw && op_raw) || poly) + { + string r (options.std () >= cxx_version::cxx11 + ? "std::move (" + p + ")" + : p); + + if (poly_derived) + // This pointer could have from from cache, so use dynamic + // cast. + // + r = p_tr + "::dynamic_pointer_cast<" + o_tp + "> (\n" + + r + ")"; + + // Unless the pointer is raw, explicitly construct the + // smart pointer from the object pointer so that we get + // the behavior similar to calling database::load() (in + // both cases we are the "ownership end-points"; unless + // the object was already in the session before loading + // this view (in which case using raw pointers as object + // pointers is a really stupid idea), this logic will do + // the right thing and what the user most likely expects. + // + if (!mp_raw) + r = member_val_type (mi.m, false) + " (\n" + r + ")"; + + if (mp_raw && !op_raw) + os << "if (!" << p_tr << "::null_ptr (" << p << "))" << endl; + + os << "// If a compiler error points to the line below, then\n" + << "// it most likely means that a pointer used in view\n" + << "// member cannot be initialized from an object pointer.\n" + << "//" << endl; + + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + if (ma.placeholder ()) + os << ma.translate ("o", r) << ";"; + else + os << ma.translate ("o") << " = " << r << ";"; + } + + if (mp_raw && !poly) + { + if (!op_raw) + os << "else" << endl; // NULL p + + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + if (ma.placeholder ()) + os << ma.translate ("o", o) << ";"; + else + os << ma.translate ("o") << " = " << o << ";"; + } + } + } + + os << "}"; + } + + virtual bool const& + member_sql_type (semantics::data_member&) {return pre_;}; + + protected: + bool pre_; + init_value_member const& init_value_member_; + }; + struct init_value_base: traversal::class_, virtual context { typedef init_value_base base; @@ -5318,6 +6451,8 @@ namespace relational bind_discriminator_member_ ("discriminator_"), init_id_image_member_ ("id_", "id"), init_version_image_member_ ("version_", "(*v)"), + init_view_pointer_member_pre_ (true, *init_value_member_), + init_view_pointer_member_post_ (false, *init_value_member_), init_id_value_member_ ("id"), init_id_value_member_id_image_ ("id", "id_"), init_version_value_member_ ("v"), @@ -5344,6 +6479,8 @@ namespace relational bind_discriminator_member_ ("discriminator_"), init_id_image_member_ ("id_", "id"), init_version_image_member_ ("version_", "(*v)"), + init_view_pointer_member_pre_ (true, *init_value_member_), + init_view_pointer_member_post_ (false, *init_value_member_), init_id_value_member_ ("id"), init_id_value_member_id_image_ ("id", "id_"), init_version_value_member_ ("v"), @@ -5375,6 +6512,9 @@ namespace relational init_value_base_inherits_ >> init_value_base_; init_value_member_names_ >> init_value_member_; + + init_view_pointer_member_pre_names_ >> init_view_pointer_member_pre_; + init_view_pointer_member_post_names_ >> init_view_pointer_member_post_; } virtual void @@ -5760,6 +6900,11 @@ namespace relational instance init_value_member_; traversal::names init_value_member_names_; + instance init_view_pointer_member_pre_; + instance init_view_pointer_member_post_; + traversal::names init_view_pointer_member_pre_names_; + traversal::names init_view_pointer_member_post_names_; + instance init_id_value_member_; instance init_id_value_member_id_image_; instance init_version_value_member_; diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx index 2a932a0..09c5a1f 100644 --- a/odb/relational/sqlite/common.cxx +++ b/odb/relational/sqlite/common.cxx @@ -60,6 +60,13 @@ namespace relational // member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -100,6 +107,8 @@ namespace relational type_ = "details::buffer"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/sqlite/common.hxx b/odb/relational/sqlite/common.hxx index c97a665..742284e 100644 --- a/odb/relational/sqlite/common.hxx +++ b/odb/relational/sqlite/common.hxx @@ -58,12 +58,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void @@ -86,7 +88,6 @@ namespace relational member_base { member_database_type_id (base const&); - member_database_type_id (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); diff --git a/odb/relational/sqlite/header.cxx b/odb/relational/sqlite/header.cxx index 883bfbf..31073a7 100644 --- a/odb/relational/sqlite/header.cxx +++ b/odb/relational/sqlite/header.cxx @@ -15,39 +15,14 @@ namespace relational { namespace relational = relational::header; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -73,10 +48,6 @@ namespace relational << "bool " << mi.var << "null;" << endl; } - - private: - string image_type; - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index 78bee1d..3dfaae2 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -78,142 +78,14 @@ namespace relational // grow // - struct grow_member: relational::grow_member, member_base + struct grow_member: relational::grow_member_impl, + member_base { grow_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - // Ignore polymorphic id references; they are not returned by - // the select statement. - // - if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) - return false; - - ostringstream ostr; - ostr << "t[" << index_ << "UL]"; - e = ostr.str (); - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - semantics::class_* comp (composite (mi.t)); - - // If the member is soft- added or deleted, check the version. - // - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - // If this is a composite member, see if it is summarily - // added/deleted. - // - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - // If the addition/deletion version is the same as the section's, - // then we don't need the test. - // - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - { - os << "if ("; - - if (av != 0) - os << "svm >= schema_version_migration (" << av << "ULL, true)"; - - if (av != 0 && dv != 0) - os << " &&" << endl; - - if (dv != 0) - os << "svm <= schema_version_migration (" << dv << "ULL, true)"; - - os << ")" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - semantics::class_* comp (composite (mi.t)); - - if (var_override_.empty ()) - { - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - os << "}"; - } - - if (comp != 0) - index_ += column_count (*comp).total; - else - index_++; - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (composite_value_traits< " << mi.fq_type () << - ", id_sqlite >::grow (" << endl - << "i." << mi.var << "value, t + " << index_ << "UL" << - (versioned (*composite (mi.t)) ? ", svm" : "") << "))" << endl - << "grew = true;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info&) @@ -238,9 +110,6 @@ namespace relational << "grew = true;" << "}"; } - - private: - string e; }; entry grow_member_; @@ -316,9 +185,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "null"; + os << "i." << var << "null"; } virtual void diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx index 33d796d..8629588 100644 --- a/odb/relational/validator.cxx +++ b/odb/relational/validator.cxx @@ -221,18 +221,16 @@ namespace relational } virtual void - traverse_simple (semantics::data_member& m) + traverse_pointer (semantics::data_member& m, semantics::class_&) { - if (object_pointer (utype (m))) + if (dm_ != 0 && object_pointer (utype (m))) { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + location const& l (dm_->location ()); - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is an object pointer" << endl; - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain object pointers" << endl; + error (l) << "nested view data member '" << member_prefix_ + << m.name () << "' is an object pointer" << endl; + info (l) << "views can only contain direct object pointer members" + << endl; valid_ = false; } -- cgit v1.1