aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--doc/manual.xhtml432
-rw-r--r--odb/common.cxx6
-rw-r--r--odb/context.cxx62
-rw-r--r--odb/context.hxx46
-rw-r--r--odb/header.cxx6
-rw-r--r--odb/instance.hxx17
-rw-r--r--odb/pragma.cxx23
-rw-r--r--odb/processor.cxx129
-rw-r--r--odb/relational/common.cxx9
-rw-r--r--odb/relational/common.hxx42
-rw-r--r--odb/relational/common.txx26
-rw-r--r--odb/relational/header.hxx75
-rw-r--r--odb/relational/mssql/common.cxx9
-rw-r--r--odb/relational/mssql/common.hxx6
-rw-r--r--odb/relational/mssql/header.cxx41
-rw-r--r--odb/relational/mssql/source.cxx4
-rw-r--r--odb/relational/mysql/common.cxx9
-rw-r--r--odb/relational/mysql/common.hxx6
-rw-r--r--odb/relational/mysql/header.cxx42
-rw-r--r--odb/relational/mysql/source.cxx147
-rw-r--r--odb/relational/oracle/common.cxx9
-rw-r--r--odb/relational/oracle/common.hxx6
-rw-r--r--odb/relational/oracle/header.cxx42
-rw-r--r--odb/relational/oracle/source.cxx4
-rw-r--r--odb/relational/pgsql/common.cxx9
-rw-r--r--odb/relational/pgsql/common.hxx6
-rw-r--r--odb/relational/pgsql/header.cxx42
-rw-r--r--odb/relational/pgsql/source.cxx158
-rw-r--r--odb/relational/processor.cxx97
-rw-r--r--odb/relational/source.cxx1105
-rw-r--r--odb/relational/source.hxx1207
-rw-r--r--odb/relational/sqlite/common.cxx9
-rw-r--r--odb/relational/sqlite/common.hxx7
-rw-r--r--odb/relational/sqlite/header.cxx41
-rw-r--r--odb/relational/sqlite/source.cxx147
-rw-r--r--odb/relational/validator.cxx16
37 files changed, 2776 insertions, 1271 deletions
diff --git a/NEWS b/NEWS
index 51fb133..d59dd96 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,10 @@
Version 2.4.0
+ * Support for object loading views. Object loading views allow loading of
+ one or more complete objects instead of, or in addition to, a subset of
+ data member and with a single SELECT statement execution. For details,
+ refer to Section 10.2, "Object Loading Views" in the ODB manual.
+
* Support for bulk operations in Oracle and SQL Server. Bulk operations
persist, update, or erase a range of objects using a single database
statement execution which often translates to significantly better
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
index 6183d2b..f91a4af 100644
--- a/doc/manual.xhtml
+++ b/doc/manual.xhtml
@@ -469,11 +469,12 @@ for consistency.
<th>10</th><td><a href="#10">Views</a>
<table class="toc">
<tr><th>10.1</th><td><a href="#10.1">Object Views</a></td></tr>
- <tr><th>10.2</th><td><a href="#10.2">Table Views</a></td></tr>
- <tr><th>10.3</th><td><a href="#10.3">Mixed Views</a></td></tr>
- <tr><th>10.4</th><td><a href="#10.4">View Query Conditions</a></td></tr>
- <tr><th>10.5</th><td><a href="#10.5">Native Views</a></td></tr>
- <tr><th>10.6</th><td><a href="#10.6">Other View Features and Limitations</a></td></tr>
+ <tr><th>10.2</th><td><a href="#10.2">Object Loading Views</a></td></tr>
+ <tr><th>10.3</th><td><a href="#10.3">Table Views</a></td></tr>
+ <tr><th>10.4</th><td><a href="#10.4">Mixed Views</a></td></tr>
+ <tr><th>10.5</th><td><a href="#10.5">View Query Conditions</a></td></tr>
+ <tr><th>10.6</th><td><a href="#10.6">Native Views</a></td></tr>
+ <tr><th>10.7</th><td><a href="#10.7">Other View Features and Limitations</a></td></tr>
</table>
</td>
</tr>
@@ -9962,7 +9963,7 @@ struct employee_employer
below. The optional <i>join-condition</i> part provides the
criteria which should be used to associate this object with any
of the previously associated objects or, as we will see in
- <a href="#10.3">Section 10.3, "Mixed Views"</a>, tables. Note that
+ <a href="#10.4">Section 10.4, "Mixed Views"</a>, tables. Note that
while the first associated object can have an alias, it cannot
have a join condition.</p>
@@ -10311,7 +10312,398 @@ t.commit ();
query expression, and you need to qualify the column with
the table, then you will need to use the table alias instead.</p>
- <h2><a name="10.2">10.2 Table Views</a></h2>
+ <h2><a name="10.2">10.2 Object Loading Views</a></h2>
+
+ <p>A special variant of object views is object loading views. Object
+ loading views allow us to load one or more complete objects
+ instead of, or in addition to, a subset of data member. While we
+ can often achieve the same end result by calling
+ <code>database::load()</code>, using a view has several advantages.</p>
+
+ <p>If we need to load multiple objects, then using a view allows us
+ to do this with a single <code>SELECT</code> statement execution
+ instead of one for each object that would be necessary in case of
+ <code>load()</code>. A view can also be useful for loading only
+ a single object if the query criterion that we would like to use
+ involves other, potentially unrelated, objects. We will examine
+ concrete examples of these and other scenarios in the rest of this
+ section.</p>
+
+ <p>To load a complete object as part of a view we use a data member of
+ the pointer to object type, just like for object relationships
+ (<a href="#6">Chapter 6, "Relationships"</a>). As an example, here
+ is how we can load both the <code>employee</code> and
+ <code>employer</code> objects from the previous section with a single
+ statement:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ shared_ptr&lt;employee> ee;
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>We use an object loading view just like any other view. In the
+ result of a query, as we would expect, the pointer data members
+ point to the loaded objects. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+
+for (const employee_employer&amp; r:
+ db.query&lt;employee_employer> (query::employee::age &lt; 31))
+{
+ cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>As another example, consider a query that loads the <code>employer</code>
+ objects using some condition based on its employees. For instance, we
+ want to find all the employers that employ people over 65 years old.
+ We can use this object loading view to implement such a query:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) object(employee)
+struct employer_view
+{
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>And this is how we can use this view to find all the employers that
+ employ seniors:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_employer> query;
+
+db.query&lt;employer_view> ((query::employee::age > 65) + distinct))
+ </pre>
+
+ <p>We can even use object loading views to load completely unrelated
+ (from the ODB object relationships point of view) objects. For example,
+ the following view will load all the employers that are named the
+ same as a country (notice the <code>inner</code> join type):</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) \
+ object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+ shared_ptr&lt;employer> e;
+ shared_ptr&lt;country> c;
+};
+ </pre>
+
+ <p>An object loading view can contain ordinary data members
+ in addition to object pointers. For example, if we are only
+ interested in the country code in the above view, then we
+ can reimplement it like this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) \
+ object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+ shared_ptr&lt;employer> e;
+ std::string code;
+};
+ </pre>
+
+ <p>Object loading views also have a few rules and restrictions.
+ Firstly, the pointed-to object in the data member must be associated
+ with the view. Furthermore, if the associated object has an alias,
+ then the data member name must be the same as the alias (more
+ precisely, the public name derived from the data member must
+ match the alias; which means we can use normal data member
+ decorations such as trailing underscores, etc., see the previous
+ section for more information on public names). The following view
+ illustrates the use of aliases as data member names:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) \
+ object(country = nat: employee::nationality_)
+struct employee_country
+{
+ shared_ptr&lt;country> res;
+ shared_ptr&lt;country> nat_;
+};
+ </pre>
+
+ <p>Finally, the object pointers must be direct data members of
+ the view. Using, for example, a composite value that contains
+ pointers as a view data member is not supported. Note also
+ that depending on the join type you are using, some of the
+ resulting pointers might be <code>NULL</code>.</p>
+
+ <p>Up until now we have consistently used <code>shared_ptr</code>
+ as an object pointer in our views. Can we use other pointers,
+ such as <code>unique_ptr</code> or raw pointers? To answer
+ this question we first need to discuss what happens with
+ object pointers that may be inside objects that a view
+ loads. As a concrete example, let us revisit the
+ <code>employee_employer</code> view from the beginning of
+ this section:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ shared_ptr&lt;employee> ee;
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>This view loads two objects: <code>employee</code> and
+ <code>employer</code>. The <code>employee</code> object,
+ however, also contains a pointer to <code>employer</code>
+ (see the <code>employed_by_</code> data member). In fact,
+ this is the same object that the view loads since <code>employer</code>
+ is associated with the view using this same relationship (ODB
+ automatically uses it since it is the only one). The correct
+ result of loading such a view is then clear: both <code>er</code> and
+ <code>er->employed_by_</code> must point to (or share) the
+ same instance.</p>
+
+ <p>Just like object loading via the <code>database</code> class
+ functions, views achieve this correct behavior of only loading
+ a single instance of the same object with the help of session's
+ object cache (<a href="#11">Chapter 11, "Session"</a>). In fact,
+ object loading views enforce this by throwing the
+ <code>session_required</code> exception if there is no current
+ session and the view loads an object that is also indirectly
+ loaded by one of the other objects. The ODB compiler will also
+ issue diagnostics if such an object has session support
+ disabled (<a href="#14.1.10">Section 14.1.10,
+ "<code>session</code>"</a>).</p>
+
+ <p>With this understanding we can now provide the correct implementation
+ of our transaction that uses the <code>employee_employer</code> view:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+odb::session s;
+
+for (const employee_employer&amp; r:
+ db.query&lt;employee_employer> (query::employee::age &lt; 31))
+{
+ assert (r.ee->employed_by_ == r.er);
+ cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>It might seem logical, then, to always load all the objects from
+ all the eager relationships with the view. After all, this will
+ lead to them all being loaded with a single statement. While
+ this is theoretically true, the reality is slightly more nuanced.
+ If there is a high probability of the object already have been
+ loaded and sitting in the cache, then not loading the object
+ as part of the view (and therefore not fetching all its data
+ from the database) might result in better performance.</p>
+
+ <p>Now we can also answer the question about which pointers we can
+ use in object loading views. From the above discussion it should
+ be clear that if an object that we are loading is also part of a
+ relationship inside another object that we are loading, then we
+ should use some form of a shared ownership pointer. If, however,
+ there are no relationships involved, as is the case, for example,
+ in our <code>employer_named_country</code> and
+ <code>employee_country</code> views above, then we can use a
+ unique ownership pointer such as <code>unique_ptr</code>.</p>
+
+ <p>Note also that your choice of a pointer type can be limited by the
+ "official" object pointer type assigned to the object
+ (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>).
+ For example, if the object pointer type is <code>shared_ptr</code>,
+ you will not be able to use <code>unique_ptr</code> to load
+ such an object into a view since initializing <code>unique_ptr</code>
+ from <code>shared_ptr</code> would be a mistake.</p>
+
+ <p>Unless you want to perform your own object cleanup, raw object
+ pointers in views are not particularly useful. They do have one
+ special semantics, however: If a raw pointer is used as a view
+ member, then, before creating a new instance, the implementation
+ will check if the member is <code>NULL</code>. If it is not, then
+ it is assumed to point to an existing instance and the implementation
+ will load the data into it instead of creating a new one. The
+ primary use of this special functionality is to implement by-value
+ loading with the ability to detect <code>NULL</code> values.</p>
+
+ <p>To illustrate this functionality, consider the following view that
+ load the employee's residence country by value:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+ typedef country* country_ptr;
+
+ #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) \
+ set(this.res_null = ((?) == nullptr))
+
+ country res;
+ bool res_null;
+};
+ </pre>
+
+ <p>Here we are using a virtual data member
+ (<a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>) to
+ add an object pointer member to the view. Its accessor expression
+ returns the pointer to the <code>res</code> member so that
+ the implementation can load the data into it. The modifier
+ expression checks the passed pointer to initialize the
+ <code>NULL</code> value indicator. Here, the two possible
+ values that can be passed to the modifier expression are
+ the address of the <code>res</code> member that we returned
+ earlier from the accessor and <code>NULL</code> (strictly
+ speaking, there is a third possibility: the address of an
+ object that was found in the session cache).</p>
+
+ <p>If we are not interested in the <code>NULL</code> indicator,
+ then the above view can simplified to this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+ typedef country* country_ptr;
+
+ #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) set()
+
+ country res;
+};
+ </pre>
+
+ <p>That is, we specify an empty modifier expression which leads to
+ the value being ignored.</p>
+
+ <p>As another example of by-value loading, consider a view that allows
+ us to load objects into existing instances that have been allocated
+ outside the view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) \
+ object(country = nat: employee::nationality_)
+struct employee_country
+{
+ employee_country (country&amp; r, country&amp; n): res (&amp;r), nat (&amp;n) {}
+
+ country* res;
+ country* nat;
+};
+ </pre>
+
+ <p>And here is how we can use this view:</p>
+
+ <pre class="cxx">
+typedef odb::result&lt;employee_country> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_country> (...);
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ country res, nat;
+ employee_country v (res, nat);
+ i.load (v);
+
+ if (v.res != nullptr)
+ ... // Result is in res.
+
+ if (v.nat != nullptr)
+ ... // Result is in nat.
+}
+
+t.commit ();
+ </pre>
+
+ <p>As a final example of the by-value loading, consider the following
+ view which implements a slightly more advanced logic: if the object
+ is already in the session cache, then it sets the pointer data member
+ in the view (<code>er_p</code>) to that. Otherwise, it loads the data
+ into the by-value instance (<code>er</code>). We can also check
+ whether the pointer data member points to the instance to distinguish
+ between the two outcomes. And we can check it for <code>nullptr</code>
+ to detect <code>NULL</code> values.</p>
+
+ <pre class="cxx">
+#pragma db view object(employer)
+struct employer_view
+{
+ // Since we may be getting the pointer as both smart and raw, we
+ // need to create a bit of support code to use in the modifier
+ // expression.
+ //
+ void set_er (employer* p) {er_p = p;} // &amp;er or NULL.
+ void set_er (shared_ptr&lt;employer> p) {er_p = p.get ();} // From cache.
+
+ #pragma db get(&amp;this.er) set(set_er(?))
+ employer* er_p;
+
+ #pragma db transient
+ employer er;
+
+ // Return-by-value support (e.g., query_value()).
+ //
+ employer_view (): er_p (0) {}
+ employer_view (const employer_view&amp; x)
+ : er_p (x.er_p == &amp;x.er ? &amp;er : x.er_p), er (x.er) {}
+};
+ </pre>
+
+ <p>We can use object loading views with polymorphic objects
+ (<a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>). Note,
+ however, that when loading a derived object via the base pointer
+ in a view, a separate statement will be executed to load the
+ dynamic part of the object. There is no support for by-value
+ loading for polymorphic objects.</p>
+
+ <p>We can also use object loading views with objects without id
+ (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).
+ Note, however, that for such objects, <code>NULL</code> values
+ are not automatically detected (since there is no primary key,
+ which is otherwise guaranteed to be not <code>NULL</code>, there
+ might not be a column on which to base this detection). The
+ workaround for this limitation is to load an otherwise not
+ <code>NULL</code> column next to the object which will serve
+ as an indicator. For example:</p>
+
+ <pre class="cxx">
+#pragma db object no_id
+class object
+{
+ ...
+
+ int n; // NOT NULL
+ std::string s;
+};
+
+#include &lt;odb/nullable.hxx>
+
+#pragma db view object(object)
+struct view
+{
+
+ odb::nullable&lt;int> n; // If 'n' is NULL, then, logically, so is 'o'.
+ unique_ptr&lt;object> o;
+};
+ </pre>
+
+ <h2><a name="10.3">10.3 Table Views</a></h2>
<p>A table view is similar to an object view except that it is
based on one or more database tables instead of persistent
@@ -10400,7 +10792,7 @@ struct employee_max_vacation
column names, and query expressions. The optional <i>join-condition</i>
part provides the criteria which should be used to associate this
table with any of the previously associated tables or, as we will see in
- <a href="#10.3">Section 10.3, "Mixed Views"</a>, objects. Note that
+ <a href="#10.4">Section 10.4, "Mixed Views"</a>, objects. Note that
while the first associated table can have an alias, it cannot have
a join condition.</p>
@@ -10468,7 +10860,7 @@ t.commit ();
</pre>
- <h2><a name="10.3">10.3 Mixed Views</a></h2>
+ <h2><a name="10.4">10.4 Mixed Views</a></h2>
<p>A mixed view has both associated objects and tables. As a first
example of a mixed view, let us improve <code>employee_vacation</code>
@@ -10531,7 +10923,7 @@ struct employee_prev_employer
};
</pre>
- <h2><a name="10.4">10.4 View Query Conditions</a></h2>
+ <h2><a name="10.5">10.5 View Query Conditions</a></h2>
<p>Object, table, and mixed views can also specify an optional query
condition that should be used whenever the database is queried for
@@ -10642,7 +11034,7 @@ struct employer_age
};
</pre>
- <h2><a name="10.5">10.5 Native Views</a></h2>
+ <h2><a name="10.6">10.6 Native Views</a></h2>
<p>The last kind of view supported by ODB is a native view. Native
views are a low-level mechanism for capturing results of native
@@ -10651,7 +11043,7 @@ struct employer_age
<code>db&nbsp;query</code> pragma to specify the native SQL query,
which should normally include the select-list and, if applicable,
the from-list. For example, here is how we can re-implement the
- <code>employee_vacation</code> table view from Section 10.2 above
+ <code>employee_vacation</code> table view from Section 10.3 above
as a native view:</p>
<pre class="cxx">
@@ -10777,7 +11169,7 @@ result n (db.query&lt;sequence_value> (
for each database system in <a href="#II">Part II, "Database
Systems"</a>.</p>
- <h2><a name="10.6">10.6 Other View Features and Limitations</a></h2>
+ <h2><a name="10.7">10.7 Other View Features and Limitations</a></h2>
<p>Views cannot be derived from other views. However, you can derive
a view from a transient C++ class. View data members cannot be
@@ -14608,7 +15000,7 @@ class employer
<p>The <code>table</code> specifier specifies a database table
that should be associated with the view. For more information
- on table associations refer to <a href="#10.2">Section 10.2, "Table
+ on table associations refer to <a href="#10.3">Section 10.3, "Table
Views"</a>.</p>
<h3><a name="14.2.3">14.2.3 <code>query</code></a></h3>
@@ -14617,9 +15009,9 @@ class employer
for an object or table view or a native SQL query for a native
view. An empty <code>query</code> specifier indicates that a
native SQL query is provided at runtime. For more information
- on query conditions refer to <a href="#10.4">Section 10.4, "View
+ on query conditions refer to <a href="#10.5">Section 10.5, "View
Query Conditions"</a>. For more information on native SQL queries,
- refer to <a href="#10.5">Section 10.5, "Native Views"</a>.</p>
+ refer to <a href="#10.6">Section 10.6, "Native Views"</a>.</p>
<h3><a name="14.2.4">14.2.4 <code>pointer</code></a></h3>
@@ -16183,7 +16575,7 @@ class person
object data member, the potentially qualified column name, or the column
expression for the data member of a view class. For more information,
refer to <a href="#10.1">Section 10.1, "Object Views"</a> and
- <a href="#10.2">Section 10.2, "Table Views"</a>.</p>
+ <a href="#10.3">Section 10.3, "Table Views"</a>.</p>
<h3><a name="14.4.11">14.4.11 <code>transient</code></a></h3>
@@ -20134,7 +20526,7 @@ class object
<h2><a name="17.7">17.7 MySQL Stored Procedures</a></h2>
- <p>ODB native views (<a href="#10.5">Section 10.5, "Native Views"</a>)
+ <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
can be used to call MySQL stored procedures. For example, assuming
we are using the <code>person</code> class from <a href="#2">Chapter
2, "Hello World Example"</a> (and the corresponding <code>person</code>
@@ -23399,7 +23791,7 @@ for (result::iterator i (r.begin ()); i != r.end (); ++i)
t.commit ();
</pre>
- <p>Finally, if a native view (<a href="#10.5">Section 10.5, "Native
+ <p>Finally, if a native view (<a href="#10.6">Section 10.6, "Native
Views"</a>) 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.</p>
@@ -24147,7 +24539,7 @@ class object
<h2><a name="21.7">21.7 SQL Server Stored Procedures</a></h2>
- <p>ODB native views (<a href="#10.5">Section 10.5, "Native Views"</a>)
+ <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
can be used to call SQL Server stored procedures. For example, assuming
we are using the <code>person</code> class from <a href="#2">Chapter
2, "Hello World Example"</a> (and the corresponding <code>person</code>
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 <odb/common.hxx>
#include <odb/pragma.hxx>
#include <odb/cxx-lexer.hxx>
+#include <odb/diagnostics.hxx>
#include <odb/relational/mssql/context.hxx>
#include <odb/relational/mysql/context.hxx>
@@ -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<semantics::data_member*> data_member_path;
+struct data_member_path: std::vector<semantics::data_member*>
+{
+ 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_object> view_objects;
typedef std::map<std::string, view_object*> view_alias_map;
typedef std::map<semantics::class_*, view_object*> 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<data_member_path, std::pair<view_object*, view_object*> >
+view_relationship_map;
+
//
//
struct view_query
@@ -232,8 +254,8 @@ struct column_expr: std::vector<column_expr_part>
//
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<semantics::class_&> (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 <string>" << endl; // For discriminator.
+ if (ctx.options.std () >= cxx_version::cxx11)
+ os << "#include <utility>" << endl; // move()
+
os << endl;
os << "#include <odb/core.hxx>" << endl
@@ -819,7 +822,8 @@ namespace header
os << "#include <odb/no-id-object-result.hxx>" << endl;
if (ctx.features.view)
- os << "#include <odb/view-result.hxx>" << endl;
+ os << "#include <odb/view-image.hxx>" << endl
+ << "#include <odb/view-result.hxx>" << 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 <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ 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 <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ 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<member_access> ().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<member_access> (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<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("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 <typename T>
void member_base_impl<T>::
- 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 <typename T>
+ struct image_member_impl: image_member, virtual member_base_impl<T>
+ {
+ 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<T>::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<T>::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> 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_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> image_type_;
- struct image_member: relational::image_member, member_base
+ struct image_member: relational::image_member_impl<sql_type>,
+ 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> 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_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<sql_type>,
+ 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> 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<sql_type>,
+ 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<user_section*> (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<user_section*> (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> 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_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> image_type_;
- struct image_member: relational::image_member, member_base
+ struct image_member: relational::image_member_impl<sql_type>,
+ 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> 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_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> section_traits_;
- struct image_member: relational::image_member, member_base
+ struct image_member: relational::image_member_impl<sql_type>,
+ 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> 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<sql_type>,
+ 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<user_section*> (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<user_section*> (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> 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<string> ("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<string> ("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<string> ("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<string> ("id-type");
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
- if (type.empty () && idt.count ("type"))
- type = idt.get<string> ("type");
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
- if (type.empty () && wt != 0 && wt->count ("type"))
- type = wt->get<string> ("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<string> ("id-type");
- if (type.empty ())
- type = database_type (idt, idhint, true);
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
- if (type.empty () && wt != 0)
- type = database_type (*wt, whint, true);
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
- id_type = type;
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("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<view_objects> ("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<semantics::scope&> (*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<polymorphic_object_joins> 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<semantics::scope&> (*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<polymorphic_object_joins> 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<class_&> (
+ 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<class_&> (e.member_path.front ()->scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+
+ t = table_name (*c, e.member_path);
+ }
+ else
+ {
+ c = &static_cast<class_&> (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<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> 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 = &lt;
+ }
+ 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 = &lt;
+ }
+ 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<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> 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 = &lt;
+ }
+ }
+ 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 = &lt;
+ }
+ }
+
+ 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<object_columns_list> l_cols (col_prefix);
+ instance<object_columns_list> 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<polymorphic_object_joins> 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<view_objects> ("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<view_object_check> 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<view_objects> ("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<semantics::scope&> (*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<polymorphic_object_joins> 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<semantics::scope&> (*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<polymorphic_object_joins> 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<class_&> (
- 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<object_columns_list> c_cols; // Container columns.
- instance<object_columns_list> 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 = &lt;
- }
- 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 = &lt;
- }
- 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<object_columns_list> c_cols; // Container columns.
- instance<object_columns_list> 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 = &lt;
- }
- }
- 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 = &lt;
- }
- }
-
- 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<object_columns_list> l_cols (col_prefix);
- instance<object_columns_list> 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<polymorphic_object_joins> 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<view_columns> t (sc);
+ instance<view_columns> 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<object_columns> 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<object_columns> 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*> ("view-object"));
+
+ semantics::data_member& im (*inverse (m));
+
+ typedef view_relationship_map::const_iterator iterator;
+
+ std::pair<iterator, iterator> 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<class_&> (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<class_&> (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<object_columns_list> l_cols; // Our id columns.
+ instance<object_columns_list> 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*> ("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*> ("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<object_columns> 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<object_columns_list> 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<view_object_check> 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<view_object_check> 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<iterator, iterator> 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<semantics::class_&> (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<T>::traverse_pointer (mi);
+ }
+
+ virtual void
traverse_composite (member_info& mi)
{
os << "composite_value_traits< " << mi.fq_type () << ", id_" <<
@@ -1283,6 +1733,195 @@ namespace relational
size_t& index_;
};
+ template <typename T>
+ struct grow_member_impl: grow_member, virtual member_base_impl<T>
+ {
+ 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<T>::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<user_section*> (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<user_section*> (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<T>::traverse_pointer (mi);
+ }
+
+ 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:
+ string e;
+ };
+
struct grow_base: traversal::class_, virtual context
{
typedef grow_base base;
@@ -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<T>::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<member_access> ("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<T>::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> 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<bool> // 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<bool> ("object-pointer-raw"));
+ bool mp_raw (utype (mi.m).is_a<semantics::pointer> ());
+
+ // 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<member_access> ("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<member_access> ("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> init_value_member_;
traversal::names init_value_member_names_;
+ instance<init_view_pointer_member> init_view_pointer_member_pre_;
+ instance<init_view_pointer_member> init_view_pointer_member_post_;
+ traversal::names init_view_pointer_member_pre_names_;
+ traversal::names init_view_pointer_member_post_names_;
+
instance<init_value_member> init_id_value_member_;
instance<init_value_member> init_id_value_member_id_image_;
instance<init_value_member> 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_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<sql_type>,
+ 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> 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<sql_type>,
+ 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<user_section*> (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<user_section*> (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> 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;
}