Section 10.5, "Native
+ Finally, if a native view (Section 10.6, "Native
Views") contains one or more long data members, then such
members should come last both in the select-list of the native
SQL query and the list of data members in the C++ class.
@@ -24147,7 +24539,7 @@ class object
- ODB native views (Section 10.5, "Native Views")
+
ODB native views (Section 10.6, "Native Views")
can be used to call SQL Server stored procedures. For example, assuming
we are using the person
class from Chapter
2, "Hello World Example" (and the corresponding person
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