aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-02-17 10:08:18 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-02-22 12:29:43 +0200
commit3a1eed21d4d5d0e7f6a9f400420fdc28d7be9b61 (patch)
tree97ba7338fb804c264c9eaaaa41085b08f6483c68
parent3f73cc933b64d7d9a88325d33a3c33a0e28720c6 (diff)
Add support for composite object ids
New pragma id_type (member). New test: common/composite-id. The composite example has also been updated.
-rw-r--r--NEWS7
-rw-r--r--doc/manual.xhtml330
-rw-r--r--odb/common.cxx254
-rw-r--r--odb/common.hxx176
-rw-r--r--odb/context.cxx70
-rw-r--r--odb/context.hxx17
-rw-r--r--odb/gcc.hxx1
-rw-r--r--odb/odb.cxx20
-rw-r--r--odb/pragma.cxx13
-rw-r--r--odb/relational/common.cxx259
-rw-r--r--odb/relational/common.hxx189
-rw-r--r--odb/relational/common.txx112
-rw-r--r--odb/relational/header.hxx41
-rw-r--r--odb/relational/model.hxx328
-rw-r--r--odb/relational/mssql/common.cxx103
-rw-r--r--odb/relational/mssql/common.hxx132
-rw-r--r--odb/relational/mssql/context.cxx17
-rw-r--r--odb/relational/mssql/context.hxx8
-rw-r--r--odb/relational/mssql/model.cxx2
-rw-r--r--odb/relational/mssql/source.cxx343
-rw-r--r--odb/relational/mysql/common.cxx103
-rw-r--r--odb/relational/mysql/common.hxx118
-rw-r--r--odb/relational/mysql/context.cxx17
-rw-r--r--odb/relational/mysql/context.hxx7
-rw-r--r--odb/relational/mysql/model.cxx2
-rw-r--r--odb/relational/mysql/source.cxx361
-rw-r--r--odb/relational/oracle/common.cxx101
-rw-r--r--odb/relational/oracle/common.hxx118
-rw-r--r--odb/relational/oracle/context.cxx17
-rw-r--r--odb/relational/oracle/context.hxx8
-rw-r--r--odb/relational/oracle/model.cxx4
-rw-r--r--odb/relational/oracle/source.cxx356
-rw-r--r--odb/relational/pgsql/common.cxx101
-rw-r--r--odb/relational/pgsql/common.hxx118
-rw-r--r--odb/relational/pgsql/context.cxx17
-rw-r--r--odb/relational/pgsql/context.hxx8
-rw-r--r--odb/relational/pgsql/model.cxx2
-rw-r--r--odb/relational/pgsql/source.cxx436
-rw-r--r--odb/relational/processor.cxx147
-rw-r--r--odb/relational/source.hxx1096
-rw-r--r--odb/relational/sqlite/common.cxx103
-rw-r--r--odb/relational/sqlite/common.hxx118
-rw-r--r--odb/relational/sqlite/context.cxx17
-rw-r--r--odb/relational/sqlite/context.hxx8
-rw-r--r--odb/relational/sqlite/model.cxx2
-rw-r--r--odb/relational/sqlite/source.cxx340
-rw-r--r--odb/validator.cxx583
47 files changed, 3076 insertions, 3654 deletions
diff --git a/NEWS b/NEWS
index f36d7b7..b0453dd 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+Version 1.9.0
+
+ * Support for composite object ids. Now a composite value type can be used
+ to declare an object id member. For more information, refer to Section
+ 7.2.1, "Composite Object Ids" in the ODB manual as well as the 'composite'
+ example in the odb-examples package.
+
Version 1.8.0
* Support for the Microsoft SQL Server database. The provided connection
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
index 64d96f8..30f4901 100644
--- a/doc/manual.xhtml
+++ b/doc/manual.xhtml
@@ -390,7 +390,8 @@ for consistency.
<tr>
<th>7.2</th><td><a href="#7.2">Composite Value Types</a>
<table class="toc">
- <tr><th>7.2.1</th><td><a href="#7.2.1">Composite Value Column and Table Names</a></td></tr>
+ <tr><th>7.2.1</th><td><a href="#7.2.1">Composite Object Ids</a></td></tr>
+ <tr><th>7.2.2</th><td><a href="#7.2.2">Composite Value Column and Table Names</a></td></tr>
</table>
</td>
</tr>
@@ -492,29 +493,30 @@ for consistency.
<tr><th>12.4.1</th><td><a href="#12.4.1"><code>id</code></a></td></tr>
<tr><th>12.4.2</th><td><a href="#12.4.2"><code>auto</code></a></td></tr>
<tr><th>12.4.3</th><td><a href="#12.4.3"><code>type</code></a></td></tr>
- <tr><th>12.4.4</th><td><a href="#12.4.4"><code>null</code>/<code>not_null</code></a></td></tr>
- <tr><th>12.4.5</th><td><a href="#12.4.5"><code>default</code></a></td></tr>
- <tr><th>12.4.6</th><td><a href="#12.4.6"><code>options</code></a></td></tr>
- <tr><th>12.4.7</th><td><a href="#12.4.7"><code>column</code> (object, composite value)</a></td></tr>
- <tr><th>12.4.8</th><td><a href="#12.4.8"><code>column</code> (view)</a></td></tr>
- <tr><th>12.4.9</th><td><a href="#12.4.9"><code>transient</code></a></td></tr>
- <tr><th>12.4.10</th><td><a href="#12.4.10"><code>readonly</code></a></td></tr>
- <tr><th>12.4.11</th><td><a href="#12.4.11"><code>inverse</code></a></td></tr>
- <tr><th>12.4.12</th><td><a href="#12.4.12"><code>version</code></a></td></tr>
- <tr><th>12.4.13</th><td><a href="#12.4.13"><code>unordered</code></a></td></tr>
- <tr><th>12.4.14</th><td><a href="#12.4.14"><code>table</code></a></td></tr>
- <tr><th>12.4.15</th><td><a href="#12.4.15"><code>index_type</code></a></td></tr>
- <tr><th>12.4.16</th><td><a href="#12.4.16"><code>key_type</code></a></td></tr>
- <tr><th>12.4.17</th><td><a href="#12.4.17"><code>value_type</code></a></td></tr>
- <tr><th>12.4.18</th><td><a href="#12.4.18"><code>value_null</code>/<code>value_not_null</code></a></td></tr>
- <tr><th>12.4.19</th><td><a href="#12.4.19"><code>id_options</code></a></td></tr>
- <tr><th>12.4.20</th><td><a href="#12.4.20"><code>index_options</code></a></td></tr>
- <tr><th>12.4.21</th><td><a href="#12.4.21"><code>key_options</code></a></td></tr>
- <tr><th>12.4.22</th><td><a href="#12.4.22"><code>value_options</code></a></td></tr>
- <tr><th>12.4.23</th><td><a href="#12.4.23"><code>id_column</code></a></td></tr>
- <tr><th>12.4.24</th><td><a href="#12.4.24"><code>index_column</code></a></td></tr>
- <tr><th>12.4.25</th><td><a href="#12.4.25"><code>key_column</code></a></td></tr>
- <tr><th>12.4.26</th><td><a href="#12.4.26"><code>value_column</code></a></td></tr>
+ <tr><th>12.4.4</th><td><a href="#12.4.4"><code>id_type</code></a></td></tr>
+ <tr><th>12.4.5</th><td><a href="#12.4.5"><code>null</code>/<code>not_null</code></a></td></tr>
+ <tr><th>12.4.6</th><td><a href="#12.4.6"><code>default</code></a></td></tr>
+ <tr><th>12.4.7</th><td><a href="#12.4.7"><code>options</code></a></td></tr>
+ <tr><th>12.4.8</th><td><a href="#12.4.8"><code>column</code> (object, composite value)</a></td></tr>
+ <tr><th>12.4.9</th><td><a href="#12.4.9"><code>column</code> (view)</a></td></tr>
+ <tr><th>12.4.10</th><td><a href="#12.4.10"><code>transient</code></a></td></tr>
+ <tr><th>12.4.11</th><td><a href="#12.4.11"><code>readonly</code></a></td></tr>
+ <tr><th>12.4.12</th><td><a href="#12.4.12"><code>inverse</code></a></td></tr>
+ <tr><th>12.4.13</th><td><a href="#12.4.13"><code>version</code></a></td></tr>
+ <tr><th>12.4.14</th><td><a href="#12.4.14"><code>unordered</code></a></td></tr>
+ <tr><th>12.4.15</th><td><a href="#12.4.15"><code>table</code></a></td></tr>
+ <tr><th>12.4.16</th><td><a href="#12.4.16"><code>index_type</code></a></td></tr>
+ <tr><th>12.4.17</th><td><a href="#12.4.17"><code>key_type</code></a></td></tr>
+ <tr><th>12.4.18</th><td><a href="#12.4.18"><code>value_type</code></a></td></tr>
+ <tr><th>12.4.19</th><td><a href="#12.4.19"><code>value_null</code>/<code>value_not_null</code></a></td></tr>
+ <tr><th>12.4.20</th><td><a href="#12.4.20"><code>id_options</code></a></td></tr>
+ <tr><th>12.4.21</th><td><a href="#12.4.21"><code>index_options</code></a></td></tr>
+ <tr><th>12.4.22</th><td><a href="#12.4.22"><code>key_options</code></a></td></tr>
+ <tr><th>12.4.23</th><td><a href="#12.4.23"><code>value_options</code></a></td></tr>
+ <tr><th>12.4.24</th><td><a href="#12.4.24"><code>id_column</code></a></td></tr>
+ <tr><th>12.4.25</th><td><a href="#12.4.25"><code>index_column</code></a></td></tr>
+ <tr><th>12.4.26</th><td><a href="#12.4.26"><code>key_column</code></a></td></tr>
+ <tr><th>12.4.27</th><td><a href="#12.4.27"><code>value_column</code></a></td></tr>
</table>
</td>
</tr>
@@ -870,7 +872,7 @@ for consistency.
object state in binary format instead of text which reduces
the load on the application and the database server. Extensive
caching of connections, prepared statements, and buffers saves
- time and resources on connection establishment, statement parsing
+ time and resources on connection establishment, statement parsing,
and memory allocations. For each supported database system the
native C API is used instead of ODBC or higher-level wrapper
APIs to reduce overhead and provide the most efficient implementation
@@ -2157,8 +2159,11 @@ class person
without the default constructor. However, in this case, the database
operations can only load the persistent state into an existing instance
(<a href="#3.8">Section 3.8, "Loading Persistent Objects"</a>,
- <a href="#4.4">Section 4.4, "Query Result"</a>). <p>The object id type
- should be default-constructible.</p></p>
+ <a href="#4.4">Section 4.4, "Query Result"</a>).</p>
+
+ <p>The object id can be of a simple or composite (<a href="#7.2.1">Section
+ 7.2.1, "Composite Object Ids"</a>) value type which should be
+ default-constructible.</p>
<p>If an object class has private or protected non-transient data
members or if its default constructor is not public, then the
@@ -3140,7 +3145,7 @@ t.commit ();
data members can be declared read-only (see <a href="#12.1.4">Section
12.1.4, "<code>readonly</code> (object)"</a>, <a href="#12.3.6">Section
12.3.6, "<code>readonly</code> (composite value)"</a>, and
- <a href="#12.4.10">Section 12.4.10, "<code>readonly</code>
+ <a href="#12.4.11">Section 12.4.11, "<code>readonly</code>
(data member)"</a>).</p>
<p>If an individual data member is declared read-only, then
@@ -4446,7 +4451,7 @@ private:
of person's nicknames is probably not important. To instruct the ODB
compiler to ignore the order in ordered containers we can use the
<code>db&nbsp;unordered</code> pragma (<a href="#12.3.7">Section 12.3.7,
- "<code>unordered</code>"</a>, <a href="#12.4.13">Section 12.4.13,
+ "<code>unordered</code>"</a>, <a href="#12.4.14">Section 12.4.14,
"<code>unordered</code>"</a>). For example:</p>
<pre class="c++">
@@ -4696,11 +4701,11 @@ class employee
<p>By default, an object pointer can be <code>NULL</code>. To
specify that a pointer always points to a valid object we can
- use the <code>not_null</code> pragma (<a href="#12.4.4">Section
- 12.4.4, "<code>null</code>/<code>not_null</code>"</a>) for
+ use the <code>not_null</code> pragma (<a href="#12.4.5">Section
+ 12.4.5, "<code>null</code>/<code>not_null</code>"</a>) for
single object pointers and the <code>value_not_null</code> pragma
- (<a href="#12.4.18">Section
- 12.4.18, "<code>value_null</code>/<code>value_not_null</code>"</a>)
+ (<a href="#12.4.19">Section
+ 12.4.19, "<code>value_null</code>/<code>value_not_null</code>"</a>)
for containers of object pointers. For example:</p>
<pre class="c++">
@@ -5069,7 +5074,7 @@ CREATE TABLE employee (
of these references.</p>
<p>To eliminate redundant database schema references we can use the
- <code>inverse</code> pragma (<a href="#12.4.11">Section 12.4.11,
+ <code>inverse</code> pragma (<a href="#12.4.12">Section 12.4.12,
"<code>inverse</code>"</a>) which tells the ODB compiler that
a pointer is the inverse side of a bidirectional relationship.
Either side of a relationship can be made inverse. For example:</p>
@@ -5113,7 +5118,7 @@ CREATE TABLE employee (
pointer. Also note that an ordered container (<a href="#5.1">Section
5.1, "Ordered Containers"</a>) of pointers that is an inverse side
of a bidirectional relationship is always treated as unordered
- (<a href="#12.4.13">Section 12.4.13, "<code>unordered</code>"</a>)
+ (<a href="#12.4.14">Section 12.4.14, "<code>unordered</code>"</a>)
because the contents of such a container are implicitly built from
the direct side of the relationship which does not contain the
element order (index).</p>
@@ -5859,12 +5864,42 @@ result r (db.query&lt;person> (
t.commit ();
</pre>
- <h3><a name="7.2.1">7.2.1 Composite Value Column and Table Names</a></h3>
+ <h3><a name="7.2.1">7.2.1 Composite Object Ids</a></h3>
+
+ <p>An object id can be of a composite value type, for example:</p>
+
+ <pre class="c++">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ name name_;
+};
+ </pre>
+
+ <p>However, a value type that can be used as an object id has a number
+ of restrictions. Such a value type cannot have container, object
+ pointer, or read-only data members. It also must be
+ default-constructible and implement the less-than comparison operator
+ (<code>operator&lt;</code>).</p>
+
+ <h3><a name="7.2.2">7.2.2 Composite Value Column and Table Names</a></h3>
<p>Customizing a column name for a data member of a simple value
type is straightforward: we simply specify the desired name with
- the <code>db&nbsp;column</code> pragma (<a href="#12.4.7">Section
- 12.4.7, "<code>column</code>"</a>). For composite value
+ the <code>db&nbsp;column</code> pragma (<a href="#12.4.8">Section
+ 12.4.8, "<code>column</code>"</a>). For composite value
types things are slightly more complex since they are mapped to
multiple columns. Consider the following example:</p>
@@ -5965,9 +6000,9 @@ CREATE TABLE person (
<p>The same principle applies when a composite value type is used
as an element of a container, except that instead of
<code>db&nbsp;column</code>, either the <code>db&nbsp;value_column</code>
- (<a href="#12.4.26">Section 12.4.26, "<code>value_column</code>"</a>) or
+ (<a href="#12.4.27">Section 12.4.27, "<code>value_column</code>"</a>) or
<code>db&nbsp;key_column</code>
- (<a href="#12.4.25">Section 12.4.25, "<code>key_column</code>"</a>)
+ (<a href="#12.4.26">Section 12.4.26, "<code>key_column</code>"</a>)
pragmas are used to specify the column prefix.</p>
<p>When a composite value type contains a container, an extra table
@@ -6011,8 +6046,8 @@ CREATE TABLE person (
</pre>
<p>To customize the container table name we can use the
- <code>db&nbsp;table</code> pragma (<a href="#12.4.14">Section
- 12.4.14, "<code>table</code>"</a>), for example:</p>
+ <code>db&nbsp;table</code> pragma (<a href="#12.4.15">Section
+ 12.4.15, "<code>table</code>"</a>), for example:</p>
<pre class="c++">
#pragma db value
@@ -6053,7 +6088,7 @@ CREATE TABLE person_nickname (
of a valid value in a column. While by default ODB maps
values to columns that do not allow <code>NULL</code> values,
it is possible to change that with the <code>db&nbsp;null</code>
- pragma (<a href="#12.4.4">Section 12.4.4,
+ pragma (<a href="#12.4.5">Section 12.4.5,
"<code>null</code>/<code>not_null</code>"</a>).</p>
<p>To properly support the <code>NULL</code> semantics, the
@@ -6804,7 +6839,7 @@ struct employee_birth_code
or the match is ambiguous, the ODB compiler will issue an error.
To associate two differently-named members or to resolve an ambiguity,
we can explicitly specify the member association using the
- <code>db&nbsp;column</code> pragma (<a href="#12.4.7">Section 12.4.7,
+ <code>db&nbsp;column</code> pragma (<a href="#12.4.8">Section 12.4.8,
"<code>column</code>"</a>). For example:</p>
<pre>
@@ -7822,7 +7857,7 @@ p.age (age);
<p>To declare a persistent class with the optimistic concurrency model we
use the <code>optimistic</code> pragma (<a href="#12.1.5">Section 12.1.5,
"<code>optimistic</code>"</a>). We also use the <code>version</code>
- pragma (<a href="#12.4.12">Section 12.4.12, "<code>version</code>"</a>)
+ pragma (<a href="#12.4.13">Section 12.4.13, "<code>version</code>"</a>)
to specify which data member will store the object version. For
example:</p>
@@ -8402,7 +8437,7 @@ class person
read-only while the rest is treated as read-write.</p>
<p>Note that it is also possible to declare individual data members
- (<a href="#12.4.10">Section 12.4.10, "<code>readonly</code>"</a>)
+ (<a href="#12.4.11">Section 12.4.11, "<code>readonly</code>"</a>)
as well as composite value types (<a href="#12.3.6">Section 12.3.6,
"<code>readonly</code>"</a>) as read-only.</p>
@@ -8412,7 +8447,7 @@ class person
has the optimistic concurrency model. A class with the optimistic
concurrency model must also specify the data member that is used to
store the object version using the <code>version</code> pragma
- (<a href="#12.4.12">Section 12.4.12, "<code>version</code>"</a>).
+ (<a href="#12.4.13">Section 12.4.13, "<code>version</code>"</a>).
For example:</p>
<pre class="c++">
@@ -8794,8 +8829,8 @@ class employee
<p>The standard syntax for qualified names used in the
<code>schema</code> and <code>table</code> specifiers as well
- as the view <code>column</code> specifier (<a href="#12.4.8">Section
- 12.4.8, "<code>column</code> (view)"</a>) has the
+ as the view <code>column</code> specifier (<a href="#12.4.9">Section
+ 12.4.9, "<code>column</code> (view)"</a>) has the
<code>"</code><i>name</i><code>.</code><i>name</i>...<code>"</code>
form where, as discussed above, the leading name component
can be empty to denote a fully qualified name. This form, however,
@@ -9181,7 +9216,7 @@ typedef shared_ptr&lt;person> person_ptr;
</pre>
<p>The <code>NULL</code> semantics can also be specified on the
- per-member basis (<a href="#12.4.4">Section 12.4.4,
+ per-member basis (<a href="#12.4.5">Section 12.4.5,
"<code>null</code>/<code>not_null</code>"</a>). If both a type and
a member have <code>null</code>/<code>not_null</code> specifiers,
then the member specifier takes precedence. If a member specifier
@@ -9234,7 +9269,7 @@ class person
<p>The semantics of the <code>default</code> specifier for a value type
are similar to those of the <code>default</code> specifier for a
- data member (<a href="#12.4.5">Section 12.4.5,
+ data member (<a href="#12.4.6">Section 12.4.6,
"<code>default</code>"</a>).</p>
<h3><a name="12.3.5">12.3.5 <code>options</code></a></h3>
@@ -9257,7 +9292,7 @@ class person
<p>The semantics of the <code>options</code> specifier for a value type
are similar to those of the <code>options</code> specifier for a
- data member (<a href="#12.4.6">Section 12.4.6,
+ data member (<a href="#12.4.7">Section 12.4.7,
"<code>options</code>"</a>).</p>
<h3><a name="12.3.6">12.3.6 <code>readonly</code></a></h3>
@@ -9286,7 +9321,7 @@ class person_name
read-only while the rest is treated as read-write.</p>
<p>Note that it is also possible to declare individual data members
- (<a href="#12.4.10">Section 12.4.10, "<code>readonly</code>"</a>)
+ (<a href="#12.4.11">Section 12.4.11, "<code>readonly</code>"</a>)
as well as whole objects (<a href="#12.1.4">Section 12.1.4,
"<code>readonly</code>"</a>) as read-only.</p>
@@ -9395,7 +9430,7 @@ typedef std::vector&lt;std::string> nicknames;
<p>The semantics of the <code>id_options</code> specifier for a container
type are similar to those of the <code>id_options</code> specifier for
- a container data member (<a href="#12.4.19">Section 12.4.19,
+ a container data member (<a href="#12.4.20">Section 12.4.20,
"<code>id_options</code>"</a>).</p>
@@ -9412,7 +9447,7 @@ typedef std::vector&lt;std::string> nicknames;
<p>The semantics of the <code>index_options</code> specifier for a container
type are similar to those of the <code>index_options</code> specifier for
- a container data member (<a href="#12.4.20">Section 12.4.20,
+ a container data member (<a href="#12.4.21">Section 12.4.21,
"<code>index_options</code>"</a>).</p>
@@ -9429,7 +9464,7 @@ typedef std::map&lt;std::string, std::string> properties;
<p>The semantics of the <code>key_options</code> specifier for a container
type are similar to those of the <code>key_options</code> specifier for
- a container data member (<a href="#12.4.21">Section 12.4.21,
+ a container data member (<a href="#12.4.22">Section 12.4.22,
"<code>key_options</code>"</a>).</p>
@@ -9446,7 +9481,7 @@ typedef std::set&lt;std::string> nicknames;
<p>The semantics of the <code>value_options</code> specifier for a container
type are similar to those of the <code>value_options</code> specifier for
- a container data member (<a href="#12.4.22">Section 12.4.22,
+ a container data member (<a href="#12.4.23">Section 12.4.23,
"<code>value_options</code>"</a>).</p>
@@ -9543,141 +9578,147 @@ typedef std::map&lt;unsigned short, float> age_weight_map;
</tr>
<tr>
+ <td><code>id_type</code></td>
+ <td>database type for a member when used as an object id</td>
+ <td><a href="#12.4.4">12.4.4</a></td>
+ </tr>
+
+ <tr>
<td><code>null</code>/<code>not_null</code></td>
<td>member can/cannot be <code>NULL</code></td>
- <td><a href="#12.4.4">12.4.4</a></td>
+ <td><a href="#12.4.5">12.4.5</a></td>
</tr>
<tr>
<td><code>default</code></td>
<td>default value for a member</td>
- <td><a href="#12.4.5">12.4.5</a></td>
+ <td><a href="#12.4.6">12.4.6</a></td>
</tr>
<tr>
<td><code>options</code></td>
<td>database options for a member</td>
- <td><a href="#12.4.6">12.4.6</a></td>
+ <td><a href="#12.4.7">12.4.7</a></td>
</tr>
<tr>
<td><code>column</code></td>
<td>column name for a member of an object or composite value</td>
- <td><a href="#12.4.7">12.4.7</a></td>
+ <td><a href="#12.4.8">12.4.8</a></td>
</tr>
<tr>
<td><code>column</code></td>
<td>column name for a member of a view</td>
- <td><a href="#12.4.8">12.4.8</a></td>
+ <td><a href="#12.4.9">12.4.9</a></td>
</tr>
<tr>
<td><code>transient</code></td>
<td>member is not stored in the database</td>
- <td><a href="#12.4.9">12.4.9</a></td>
+ <td><a href="#12.4.10">12.4.10</a></td>
</tr>
<tr>
<td><code>readonly</code></td>
<td>member is read-only</td>
- <td><a href="#12.4.10">12.4.10</a></td>
+ <td><a href="#12.4.11">12.4.11</a></td>
</tr>
<tr>
<td><code>inverse</code></td>
<td>member is an inverse side of a bidirectional relationship</td>
- <td><a href="#12.4.11">12.4.11</a></td>
+ <td><a href="#12.4.12">12.4.12</a></td>
</tr>
<tr>
<td><code>version</code></td>
<td>member stores object version</td>
- <td><a href="#12.4.12">12.4.12</a></td>
+ <td><a href="#12.4.13">12.4.13</a></td>
</tr>
<tr>
<td><code>unordered</code></td>
<td>ordered container should be stored unordered</td>
- <td><a href="#12.4.13">12.4.13</a></td>
+ <td><a href="#12.4.14">12.4.14</a></td>
</tr>
<tr>
<td><code>table</code></td>
<td>table name for a container</td>
- <td><a href="#12.4.14">12.4.14</a></td>
+ <td><a href="#12.4.15">12.4.15</a></td>
</tr>
<tr>
<td><code>index_type</code></td>
<td>database type for a container's index type</td>
- <td><a href="#12.4.15">12.4.15</a></td>
+ <td><a href="#12.4.16">12.4.16</a></td>
</tr>
<tr>
<td><code>key_type</code></td>
<td>database type for a container's key type</td>
- <td><a href="#12.4.16">12.4.16</a></td>
+ <td><a href="#12.4.17">12.4.17</a></td>
</tr>
<tr>
<td><code>value_type</code></td>
<td>database type for a container's value type</td>
- <td><a href="#12.4.17">12.4.17</a></td>
+ <td><a href="#12.4.18">12.4.18</a></td>
</tr>
<tr>
<td><code>value_null</code>/<code>value_not_null</code></td>
<td>container's value can/cannot be <code>NULL</code></td>
- <td><a href="#12.4.18">12.4.18</a></td>
+ <td><a href="#12.4.19">12.4.19</a></td>
</tr>
<tr>
<td><code>id_options</code></td>
<td>database options for a container's id column</td>
- <td><a href="#12.4.19">12.4.19</a></td>
+ <td><a href="#12.4.20">12.4.20</a></td>
</tr>
<tr>
<td><code>index_options</code></td>
<td>database options for a container's index column</td>
- <td><a href="#12.4.20">12.4.20</a></td>
+ <td><a href="#12.4.21">12.4.21</a></td>
</tr>
<tr>
<td><code>key_options</code></td>
<td>database options for a container's key column</td>
- <td><a href="#12.4.21">12.4.21</a></td>
+ <td><a href="#12.4.22">12.4.22</a></td>
</tr>
<tr>
<td><code>value_options</code></td>
<td>database options for a container's value column</td>
- <td><a href="#12.4.22">12.4.22</a></td>
+ <td><a href="#12.4.23">12.4.23</a></td>
</tr>
<tr>
<td><code>id_column</code></td>
<td>column name for a container's object id</td>
- <td><a href="#12.4.23">12.4.23</a></td>
+ <td><a href="#12.4.24">12.4.24</a></td>
</tr>
<tr>
<td><code>index_column</code></td>
<td>column name for a container's index</td>
- <td><a href="#12.4.24">12.4.24</a></td>
+ <td><a href="#12.4.25">12.4.25</a></td>
</tr>
<tr>
<td><code>key_column</code></td>
<td>column name for a container's key</td>
- <td><a href="#12.4.25">12.4.25</a></td>
+ <td><a href="#12.4.26">12.4.26</a></td>
</tr>
<tr>
<td><code>value_column</code></td>
<td>column name for a container's value</td>
- <td><a href="#12.4.26">12.4.26</a></td>
+ <td><a href="#12.4.27">12.4.27</a></td>
</tr>
</table>
@@ -9764,11 +9805,44 @@ class person
};
</pre>
- <p>The <code>null</code> and <code>not_null</code> (<a href="#12.4.4">Section
- 12.4.4, "<code>null</code>/<code>not_null</code>"</a>) specifiers
+ <p>The <code>null</code> and <code>not_null</code> (<a href="#12.4.5">Section
+ 12.4.5, "<code>null</code>/<code>not_null</code>"</a>) specifiers
can be used to control the NULL semantics of a data member.</p>
- <h3><a name="12.4.4">12.4.4 <code>null</code>/<code>not_null</code></a></h3>
+ <h3><a name="12.4.4">12.4.4 <code>id_type</code></a></h3>
+
+ <p>The <code>type</code> specifier specifies the native database type
+ that should be used for a data member when it is part of an
+ object identifier. This specifier only makes sense when applied to
+ a member of a composite value type that is used for both id and
+ non-id members. For example:</p>
+
+ <pre class="c++">
+#pragma db value
+class name
+{
+ ...
+
+ #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+ std::string first_;
+
+ #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ name name_; // name_.first_, name_.last_ mapped to VARCHAR(64)
+
+ name alias_; // alias_.first_, alias_.last_ mapped to VARCHAR(256)
+};
+ </pre>
+
+ <h3><a name="12.4.5">12.4.5 <code>null</code>/<code>not_null</code></a></h3>
<p>The <code>null</code> and <code>not_null</code> specifiers specify that
a data member can or cannot be <code>NULL</code>, respectively.
@@ -9823,7 +9897,7 @@ class account
discussion of the <code>NULL</code> semantics for object pointers,
refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
- <h3><a name="12.4.5">12.4.5 <code>default</code></a></h3>
+ <h3><a name="12.4.6">12.4.6 <code>default</code></a></h3>
<p>The <code>default</code> specifier specifies the database default value
that should be used for a data member. For example:</p>
@@ -9844,8 +9918,8 @@ class person
an integer literal, a floating point literal, a string literal, or
an enumerator name. If you need to specify a default value that is
an expression, for example an SQL function call, then you can use
- the <code>options</code> specifier (<a href="#12.4.6">Section
- 12.4.6, "<code>options</code>"</a>) instead. For example:</p>
+ the <code>options</code> specifier (<a href="#12.4.7">Section
+ 12.4.7, "<code>options</code>"</a>) instead. For example:</p>
<pre class="c++">
enum gender {male, female, undisclosed};
@@ -9931,7 +10005,7 @@ class person
<p>Additionally, the <code>default</code> specifier cannot be specified
for view data members.</p>
- <h3><a name="12.4.6">12.4.6 <code>options</code></a></h3>
+ <h3><a name="12.4.7">12.4.7 <code>options</code></a></h3>
<p>The <code>options</code> specifier specifies additional column
definition options that should be used for a data member. For
@@ -9979,9 +10053,9 @@ class person
<p>ODB provides dedicated specifiers for specifying column types
(<a href="#12.4.3">Section 12.4.3, "<code>type</code>"</a>),
- <code>NULL</code> constraints (<a href="#12.4.4">Section 12.4.4,
+ <code>NULL</code> constraints (<a href="#12.4.5">Section 12.4.5,
"<code>null</code>/<code>not_null</code>"</a>), and default
- values (<a href="#12.4.5">Section 12.4.5, "<code>default</code>"</a>).
+ values (<a href="#12.4.6">Section 12.4.6, "<code>default</code>"</a>).
For ODB to function correctly these specifiers should always be
used instead of the opaque <code>options</code> specifier for
these components of a column definition.</p>
@@ -9989,7 +10063,7 @@ class person
<p>Note also that the <code>options</code> specifier cannot be specified
for view data members.</p>
- <h3><a name="12.4.7">12.4.7 <code>column</code> (object, composite value)</a></h3>
+ <h3><a name="12.4.8">12.4.8 <code>column</code> (object, composite value)</a></h3>
<p>The <code>column</code> specifier specifies the column name
that should be used to store a data member of a persistent class
@@ -10007,7 +10081,7 @@ class person
</pre>
<p>For a member of a composite value type, the <code>column</code> specifier
- specifies the column name prefix. Refer to <a href="#7.2.1">Section 7.2.1,
+ specifies the column name prefix. Refer to <a href="#7.2.2">Section 7.2.2,
"Composite Value Column and Table Names"</a> for details.</p>
<p>If the column name is not specified, it is derived from the member's
@@ -10015,7 +10089,7 @@ class person
the common data member name decorations, such as leading and trailing
underscores, the <code>m_</code> prefix, etc.</p>
- <h3><a name="12.4.8">12.4.8 <code>column</code> (view)</a></h3>
+ <h3><a name="12.4.9">12.4.9 <code>column</code> (view)</a></h3>
<p>The <code>column</code> specifier can be used to specify the associated
object data member, the potentially qualified column name, or the column
@@ -10023,7 +10097,7 @@ class person
refer to <a href="#9.1">Section 9.1, "Object Views"</a> and
<a href="#9.2">Section 9.2, "Table Views"</a>.</p>
- <h3><a name="12.4.9">12.4.9 <code>transient</code></a></h3>
+ <h3><a name="12.4.10">12.4.10 <code>transient</code></a></h3>
<p>The <code>transient</code> specifier instructs the ODB compiler
not to store a data member in the database. For example:</p>
@@ -10045,7 +10119,7 @@ class person
references that are only meaningful in the application's
memory, as well as utility members such as mutexes, etc.</p>
- <h3><a name="12.4.10">12.4.10 <code>readonly</code></a></h3>
+ <h3><a name="12.4.11">12.4.11 <code>readonly</code></a></h3>
<p>The <code>readonly</code> specifier specifies that a data member of
an object or composite value type is read-only. Changes to a read-only
@@ -10054,7 +10128,7 @@ class person
containing such a member. Since views are read-only, it is not
necessary to use this specifier for view data members. Object id
(<a href="#12.4.1">Section 12.4.1, "<code>id</code>"</a>)
- and inverse (<a href="#12.4.11">Section 12.4.11,
+ and inverse (<a href="#12.4.12">Section 12.4.12,
"<code>inverse</code>"</a>) data members are automatically treated
as read-only and must not be explicitly declared as such. For
example:</p>
@@ -10138,7 +10212,7 @@ class person
as well as whole objects (<a href="#12.1.4">Section 12.1.4,
"<code>readonly</code>"</a>) as read-only.</p>
- <h3><a name="12.4.11">12.4.11 <code>inverse</code></a></h3>
+ <h3><a name="12.4.12">12.4.12 <code>inverse</code></a></h3>
<p>The <code>inverse</code> specifier specifies that a data member of
an object pointer or a container of object pointers type is an
@@ -10176,12 +10250,12 @@ class person
relationship information. Only ordered and set containers can be used
for inverse members. If an inverse member is of an ordered container
type, it is automatically marked as unordered
- (<a href="#12.4.13">Section 12.4.13, "<code>unordered</code>"</a>).</p>
+ (<a href="#12.4.14">Section 12.4.14, "<code>unordered</code>"</a>).</p>
<p>For a more detailed discussion of inverse members, refer to
<a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>.</p>
- <h3><a name="12.4.12">12.4.12 <code>version</code></a></h3>
+ <h3><a name="12.4.13">12.4.13 <code>version</code></a></h3>
<p>The <code>version</code> specifier specifies that the data member stores
the object version used to support optimistic concurrency. If a class
@@ -10211,7 +10285,7 @@ class person
<p>For a more detailed discussion of optimistic concurrency, refer to
<a href="#11">Chapter 11, "Optimistic Concurrency"</a>.</p>
- <h3><a name="12.4.13">12.4.13 <code>unordered</code></a></h3>
+ <h3><a name="12.4.14">12.4.14 <code>unordered</code></a></h3>
<p>The <code>unordered</code> specifier specifies that a member of
an ordered container type should be stored unordered in the database.
@@ -10234,7 +10308,7 @@ class person
storage in the database, refer to <a href="#5.1">Section 5.1,
"Ordered Containers"</a>.</p>
- <h3><a name="12.4.14">12.4.14 <code>table</code></a></h3>
+ <h3><a name="12.4.15">12.4.15 <code>table</code></a></h3>
<p>The <code>table</code> specifier specifies the table name that should
be used to store the contents of a container member. For example:</p>
@@ -10261,7 +10335,7 @@ class person
<p>The <code>table</code> specifier can also be used for members of
composite value types. In this case it specifies the table name
prefix for container members inside the composite value type. Refer
- to <a href="#7.2.1">Section 7.2.1, "Composite Value Column and Table
+ to <a href="#7.2.2">Section 7.2.2, "Composite Value Column and Table
Names"</a> for details.</p>
<p>The container table name can be qualified with a database
@@ -10282,7 +10356,7 @@ class person
qualified names, refer to <a href="#12.1.8">Section 12.1.8,
"<code>schema</code>"</a>.</p>
- <h3><a name="12.4.15">12.4.15 <code>index_type</code></a></h3>
+ <h3><a name="12.4.16">12.4.16 <code>index_type</code></a></h3>
<p>The <code>index_type</code> specifier specifies the native
database type that should be used for an ordered container's
@@ -10302,7 +10376,7 @@ class person
};
</pre>
- <h3><a name="12.4.16">12.4.16 <code>key_type</code></a></h3>
+ <h3><a name="12.4.17">12.4.17 <code>key_type</code></a></h3>
<p>The <code>key_type</code> specifier specifies the native
database type that should be used for a map container's
@@ -10322,7 +10396,7 @@ class person
};
</pre>
- <h3><a name="12.4.17">12.4.17 <code>value_type</code></a></h3>
+ <h3><a name="12.4.18">12.4.18 <code>value_type</code></a></h3>
<p>The <code>value_type</code> specifier specifies the native
database type that should be used for a container's
@@ -10343,18 +10417,18 @@ class person
</pre>
<p>The <code>value_null</code> and <code>value_not_null</code>
- (<a href="#12.4.18">Section 12.4.18,
+ (<a href="#12.4.19">Section 12.4.19,
"<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
can be used to control the NULL semantics of a value column.</p>
- <h3><a name="12.4.18">12.4.18 <code>value_null</code>/<code>value_not_null</code></a></h3>
+ <h3><a name="12.4.19">12.4.19 <code>value_null</code>/<code>value_not_null</code></a></h3>
<p>The <code>value_null</code> and <code>value_not_null</code> specifiers
specify that a container's element value for a data member can or
cannot be <code>NULL</code>, respectively. The semantics of
<code>value_null</code> and <code>value_not_null</code> are similar
to those of the <code>null</code> and <code>not_null</code> specifiers
- (<a href="#12.4.4">Section 12.4.4, "<code>null</code>/<code>not_null</code>"</a>).
+ (<a href="#12.4.5">Section 12.4.5, "<code>null</code>/<code>not_null</code>"</a>).
For example:</p>
<pre class="c++">
@@ -10380,7 +10454,7 @@ class account
Multiset Containers"</a>) the element value is automatically treated
as not allowing a <code>NULL</code> value.</p>
- <h3><a name="12.4.19">12.4.19 <code>id_options</code></a></h3>
+ <h3><a name="12.4.20">12.4.20 <code>id_options</code></a></h3>
<p>The <code>id_options</code> specifier specifies additional
column definition options that should be used for a container's
@@ -10401,10 +10475,10 @@ class person
</pre>
<p>The semantics of <code>id_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#12.4.6">Section
- 12.4.6, "<code>options</code>"</a>).</p>
+ of the <code>options</code> specifier (<a href="#12.4.7">Section
+ 12.4.7, "<code>options</code>"</a>).</p>
- <h3><a name="12.4.20">12.4.20 <code>index_options</code></a></h3>
+ <h3><a name="12.4.21">12.4.21 <code>index_options</code></a></h3>
<p>The <code>index_options</code> specifier specifies additional
column definition options that should be used for a container's
@@ -10422,10 +10496,10 @@ class person
</pre>
<p>The semantics of <code>index_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#12.4.6">Section
- 12.4.6, "<code>options</code>"</a>).</p>
+ of the <code>options</code> specifier (<a href="#12.4.7">Section
+ 12.4.7, "<code>options</code>"</a>).</p>
- <h3><a name="12.4.21">12.4.21 <code>key_options</code></a></h3>
+ <h3><a name="12.4.22">12.4.22 <code>key_options</code></a></h3>
<p>The <code>key_options</code> specifier specifies additional
column definition options that should be used for a container's
@@ -10443,10 +10517,10 @@ class person
</pre>
<p>The semantics of <code>key_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#12.4.6">Section
- 12.4.6, "<code>options</code>"</a>).</p>
+ of the <code>options</code> specifier (<a href="#12.4.7">Section
+ 12.4.7, "<code>options</code>"</a>).</p>
- <h3><a name="12.4.22">12.4.22 <code>value_options</code></a></h3>
+ <h3><a name="12.4.23">12.4.23 <code>value_options</code></a></h3>
<p>The <code>value_options</code> specifier specifies additional
column definition options that should be used for a container's
@@ -10464,17 +10538,17 @@ class person
</pre>
<p>The semantics of <code>value_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#12.4.6">Section
- 12.4.6, "<code>options</code>"</a>).</p>
+ of the <code>options</code> specifier (<a href="#12.4.7">Section
+ 12.4.7, "<code>options</code>"</a>).</p>
- <h3><a name="12.4.23">12.4.23 <code>id_column</code></a></h3>
+ <h3><a name="12.4.24">12.4.24 <code>id_column</code></a></h3>
<p>The <code>id_column</code> specifier specifies the column
name that should be used to store the object id in a
container's table for a data member. The semantics of
<code>id_column</code> are similar to those of the
<code>column</code> specifier
- (<a href="#12.4.7">Section 12.4.7, "<code>column</code>"</a>).
+ (<a href="#12.4.8">Section 12.4.8, "<code>column</code>"</a>).
For example:</p>
<pre class="c++">
@@ -10491,14 +10565,14 @@ class person
<p>If the column name is not specified, then <code>object_id</code>
is used by default.</p>
- <h3><a name="12.4.24">12.4.24 <code>index_column</code></a></h3>
+ <h3><a name="12.4.25">12.4.25 <code>index_column</code></a></h3>
<p>The <code>index_column</code> specifier specifies the column
name that should be used to store the element index in an
ordered container's table for a data member. The semantics of
<code>index_column</code> are similar to those of the
<code>column</code> specifier
- (<a href="#12.4.7">Section 12.4.7, "<code>column</code>"</a>).
+ (<a href="#12.4.8">Section 12.4.8, "<code>column</code>"</a>).
For example:</p>
<pre class="c++">
@@ -10515,14 +10589,14 @@ class person
<p>If the column name is not specified, then <code>index</code>
is used by default.</p>
- <h3><a name="12.4.25">12.4.25 <code>key_column</code></a></h3>
+ <h3><a name="12.4.26">12.4.26 <code>key_column</code></a></h3>
<p>The <code>key_column</code> specifier specifies the column
name that should be used to store the key in a map
container's table for a data member. The semantics of
<code>key_column</code> are similar to those of the
<code>column</code> specifier
- (<a href="#12.4.7">Section 12.4.7, "<code>column</code>"</a>).
+ (<a href="#12.4.8">Section 12.4.8, "<code>column</code>"</a>).
For example:</p>
<pre class="c++">
@@ -10539,14 +10613,14 @@ class person
<p>If the column name is not specified, then <code>key</code>
is used by default.</p>
- <h3><a name="12.4.26">12.4.26 <code>value_column</code></a></h3>
+ <h3><a name="12.4.27">12.4.27 <code>value_column</code></a></h3>
<p>The <code>value_column</code> specifier specifies the column
name that should be used to store the element value in a
container's table for a data member. The semantics of
<code>value_column</code> are similar to those of the
<code>column</code> specifier
- (<a href="#12.4.7">Section 12.4.7, "<code>column</code>"</a>).
+ (<a href="#12.4.8">Section 12.4.8, "<code>column</code>"</a>).
For example:</p>
<pre class="c++">
@@ -12885,7 +12959,7 @@ SHOW integer_datetimes
in the generated schema, columns of these types are always
declared as <code>NULL</code>, even if explicitly declared as
<code>NOT NULL</code> with the <code>db&nbsp;not_null</code> pragma
- (<a href="#12.4.4">Section 12.4.4, "<code>null/not_null</code>"</a>).</p>
+ (<a href="#12.4.5">Section 12.4.5, "<code>null/not_null</code>"</a>).</p>
<p>The Oracle ODB runtime library also provides support for mapping the
<code>std::string</code> type to the Oracle <code>CHAR</code>,
diff --git a/odb/common.cxx b/odb/common.cxx
index 230e674..6261b85 100644
--- a/odb/common.cxx
+++ b/odb/common.cxx
@@ -16,6 +16,12 @@ 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)));
+}
+
+void object_members_base::
traverse_composite (semantics::data_member*, semantics::class_& c)
{
inherits (c);
@@ -51,26 +57,6 @@ traverse_view (semantics::class_& c)
}
void object_members_base::
-traverse (semantics::data_member& m, semantics::class_& c)
-{
- // We are starting from the member. Add an empty chain which
- // corresponds to the scope that contains this member.
- //
- //member_scope_.push_back (class_inheritance_chain ());
- //member_path_.push_back (&m);
-
- member_scope_.push_back (class_inheritance_chain ());
- member_scope_.back ().push_back (&c);
-
- traverse_composite_wrapper (&m, c, 0);
-
- member_scope_.pop_back ();
-
- //member_path_.pop_back ();
- //member_scope_.pop_back ();
-}
-
-void object_members_base::
traverse (semantics::class_& c)
{
class_kind_type k (class_kind (c));
@@ -156,68 +142,73 @@ traverse (semantics::class_& c)
context::cur_object = prev;
}
-void object_members_base::member::
-traverse (semantics::data_member& m)
+void object_members_base::
+traverse_member (semantics::data_member& m, semantics::type& t)
{
- if (transient (m))
- return;
-
- om_.member_path_.push_back (&m);
-
- semantics::type& t (utype (m));
-
if (semantics::class_* comp = context::composite_wrapper (t))
{
- om_.member_scope_.push_back (class_inheritance_chain ());
- om_.member_scope_.back ().push_back (comp);
+ member_scope_.push_back (class_inheritance_chain ());
+ member_scope_.back ().push_back (comp);
qname old_table_prefix;
string old_flat_prefix, old_member_prefix;
- if (om_.build_flat_prefix_)
+ if (build_flat_prefix_)
{
- old_flat_prefix = om_.flat_prefix_;
- om_.flat_prefix_ += om_.public_name (m);
- om_.flat_prefix_ += '_';
+ old_flat_prefix = flat_prefix_;
+ flat_prefix_ += public_name (m);
+ flat_prefix_ += '_';
}
- if (om_.build_member_prefix_)
+ if (build_member_prefix_)
{
- old_member_prefix = om_.member_prefix_;
- om_.member_prefix_ += m.name ();
- om_.member_prefix_ += '.';
+ old_member_prefix = member_prefix_;
+ member_prefix_ += m.name ();
+ member_prefix_ += '.';
}
- if (om_.build_table_prefix_)
+ if (build_table_prefix_)
{
- old_table_prefix = om_.table_prefix_.prefix;
- append (m, om_.table_prefix_);
+ old_table_prefix = table_prefix_.prefix;
+ append (m, table_prefix_);
}
- om_.traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0));
+ traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0));
- if (om_.build_table_prefix_)
+ if (build_table_prefix_)
{
- om_.table_prefix_.level--;
- om_.table_prefix_.prefix = old_table_prefix;
+ table_prefix_.level--;
+ table_prefix_.prefix = old_table_prefix;
}
- if (om_.build_flat_prefix_)
- om_.flat_prefix_ = old_flat_prefix;
+ if (build_flat_prefix_)
+ flat_prefix_ = old_flat_prefix;
- if (om_.build_member_prefix_)
- om_.member_prefix_ = old_member_prefix;
+ if (build_member_prefix_)
+ member_prefix_ = old_member_prefix;
- om_.member_scope_.pop_back ();
+ member_scope_.pop_back ();
}
- else if (semantics::type* c = context::container (m))
- {
+ else
+ traverse_simple (m);
+}
+
+void object_members_base::member::
+traverse (semantics::data_member& m)
+{
+ if (transient (m))
+ return;
+
+ om_.member_path_.push_back (&m);
+
+ semantics::type& t (utype (m));
+
+ if (semantics::type* c = context::container (m))
om_.traverse_container (m, *c);
- }
+ else if (semantics::class_* c = object_pointer (t))
+ om_.traverse_pointer (m, *c);
else
- {
- om_.traverse_simple (m);
- }
+ om_.traverse_member (m, t);
om_.member_path_.pop_back ();
}
@@ -283,6 +274,18 @@ flush ()
{
}
+bool object_columns_base::
+traverse_column (semantics::data_member&, string const&, bool)
+{
+ return false;
+}
+
+void object_columns_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ traverse_member (m, utype (*id_member (c)));
+}
+
void object_columns_base::
traverse_composite (semantics::data_member*, semantics::class_& c)
{
@@ -307,30 +310,39 @@ traverse_view (semantics::class_& c)
void object_columns_base::
traverse (semantics::data_member& m,
- semantics::class_& c,
- string const& key_prefix,
- string const& default_name)
+ semantics::type& t,
+ std::string const& kp,
+ std::string const& dn,
+ semantics::class_* to)
{
- // We are starting from the member. Add an empty chain which
- // corresponds to the scope that contains this member.
- //
- //member_scope_.push_back (class_inheritance_chain ());
- //member_path_.push_back (&m);
+ semantics::class_* oto (context::top_object);
- member_scope_.push_back (class_inheritance_chain ());
- member_scope_.back ().push_back (&c);
+ if (to != 0)
+ context::top_object = to;
- column_prefix_ = column_prefix (m, key_prefix, default_name);
+ semantics::class_* c (object_pointer (t));
+ semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));
- traverse_composite (&m, c);
+ root_ = &m;
+ root_id_ = (kp.empty () ? context::id (m) : kp == "id");
+ root_op_ = (c != 0);
- if (!member_.first_)
- flush ();
+ key_prefix_ = kp;
+ default_name_ = dn;
- member_scope_.pop_back ();
+ if (root_op_)
+ traverse_pointer (m, *c);
+ else
+ traverse_member (m, *rt);
+
+ key_prefix_.clear ();
+ default_name_.clear ();
+
+ if (!first_ && composite_wrapper (*rt))
+ flush ();
- //member_path_.pop_back ();
- //member_scope_.pop_back ();
+ root_ = 0;
+ context::top_object = oto;
}
void object_columns_base::
@@ -386,7 +398,7 @@ traverse (semantics::class_& c)
context::cur_object = prev;
}
- if (f && !member_.first_)
+ if (f && !first_)
flush ();
}
@@ -421,48 +433,102 @@ column_prefix (semantics::data_member& m, string const& kp, string const& dn)
return r;
}
-void object_columns_base::member::
-traverse (semantics::data_member& m)
+string object_columns_base::
+column_prefix (data_member_path const& mp)
{
- if (transient (m))
- return;
+ if (mp.size () < 2)
+ return "";
- oc_.member_path_.push_back (&m);
+ string r;
- semantics::type& t (utype (m));
+ for (data_member_path::const_iterator i (mp.begin ()), e (mp.end () - 1);
+ i != e; ++i)
+ r += column_prefix (**i);
+
+ return r;
+}
+void object_columns_base::
+traverse_member (semantics::data_member& m, semantics::type& t)
+{
if (semantics::class_* comp = composite_wrapper (t))
{
- oc_.member_scope_.push_back (class_inheritance_chain ());
- oc_.member_scope_.back ().push_back (comp);
+ member_scope_.push_back (class_inheritance_chain ());
+ member_scope_.back ().push_back (comp);
- string old_prefix (oc_.column_prefix_);
- oc_.column_prefix_ += column_prefix (m);
+ string old_prefix (column_prefix_);
+ column_prefix_ += column_prefix (m, key_prefix_, default_name_);
- oc_.traverse_composite (&m, *comp);
+ // Save and clear the key prefix and default name.
+ //
+ string old_kp, old_dn;
+ old_kp.swap (key_prefix_);
+ old_dn.swap (default_name_);
- oc_.column_prefix_ = old_prefix;
+ traverse_composite (&m, *comp);
- oc_.member_scope_.pop_back ();
- }
- else if (container (m))
- {
- // Container gets its own table, so nothing to do here.
- //
+ old_kp.swap (key_prefix_);
+ old_dn.swap (default_name_);
+
+ column_prefix_ = old_prefix;
+ member_scope_.pop_back ();
}
else
{
- if (oc_.traverse_column (m, oc_.column_prefix_ + column_name (m), first_))
+ string name (column_prefix_ + column_name (m, key_prefix_, default_name_));
+
+ if (traverse_column (m, name, first_))
{
if (first_)
first_ = false;
}
}
+}
+
+void object_columns_base::member::
+traverse (semantics::data_member& m)
+{
+ if (transient (m))
+ return;
+
+ // Container gets its own table, so nothing to do here.
+ //
+ if (container (m))
+ return;
+
+ oc_.member_path_.push_back (&m);
+
+ semantics::type& t (utype (m));
+
+ if (semantics::class_* c = object_pointer (t))
+ oc_.traverse_pointer (m, *c);
+ else
+ oc_.traverse_member (m, t);
oc_.member_path_.pop_back ();
}
//
+// object_columns_list
+//
+
+void object_columns_list::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore inverse object pointers.
+ //
+ if (!ignore_inverse_ || !inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+}
+
+bool object_columns_list::
+traverse_column (semantics::data_member& m, std::string const& name, bool)
+{
+ columns_.push_back (column (name, column_type (), m));
+ return true;
+}
+
+//
// typedefs
//
diff --git a/odb/common.hxx b/odb/common.hxx
index 28ed2c1..ffbd2d6 100644
--- a/odb/common.hxx
+++ b/odb/common.hxx
@@ -6,7 +6,9 @@
#define ODB_COMMON_HXX
#include <string>
+#include <vector>
#include <cstddef> // std::size_t
+#include <cassert>
#include <odb/context.hxx>
@@ -18,6 +20,14 @@ struct object_members_base: traversal::class_, virtual context
virtual void
traverse_simple (semantics::data_member&);
+ // Traverse object pointer. The second argument is the pointed-to
+ // class. When overriding this function, you will most likely want
+ // to call the base in order to traverse the pointer as a simple
+ // member (simple object id) or as composite (composite object id).
+ //
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
// If you override this function, you can call the base to traverse
// bases and members. The first argument is the data member and can
// be NULL if we are traversing the root type or a base. The second
@@ -81,11 +91,6 @@ public:
virtual void
traverse (semantics::class_&);
- // Composite value with data member.
- //
- virtual void
- traverse (semantics::data_member&, semantics::class_& comp);
-
public:
// Append composite member prefix.
//
@@ -93,13 +98,21 @@ public:
append (semantics::data_member&, table_prefix&);
protected:
- std::string flat_prefix_;
+ string flat_prefix_;
table_prefix table_prefix_;
- std::string member_prefix_;
+ string member_prefix_;
data_member_path member_path_;
data_member_scope member_scope_;
+protected:
+ semantics::data_member*
+ id () const
+ {
+ assert (!member_path_.empty ());
+ return context::id (member_path_);
+ }
+
private:
void
init (bool build_flat_prefix,
@@ -115,6 +128,9 @@ private:
}
private:
+ virtual void
+ traverse_member (semantics::data_member&, semantics::type&);
+
struct member: traversal::data_member
{
member (object_members_base& om)
@@ -148,8 +164,16 @@ struct object_columns_base: traversal::class_, virtual context
//
virtual bool
traverse_column (semantics::data_member&,
- std::string const& name,
- bool first) = 0;
+ string const& name,
+ bool first);
+
+ // Traverse object pointer. The second argument is the pointed-to
+ // class. When overriding this function, you will most likely want
+ // to call the base in order to traverse the member as a column
+ // (simple object id) or columns (composite object id).
+ //
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
// If you override this function, you can call the base to traverse
// bases and members. The first argument is the data member and can
@@ -179,14 +203,22 @@ struct object_columns_base: traversal::class_, virtual context
flush ();
public:
- object_columns_base ()
- : top_level_ (true), member_ (*this)
+ object_columns_base (bool first = true,
+ string const& column_prefix = string ())
+ : column_prefix_ (column_prefix),
+ root_ (0),
+ first_ (first),
+ top_level_ (true),
+ member_ (*this)
{
init ();
}
- object_columns_base (object_columns_base const&)
+ object_columns_base (object_columns_base const& x)
: context (), //@@ -Wextra
+ column_prefix_ (x.column_prefix_),
+ root_ (0),
+ first_ (x.first_),
top_level_ (true),
member_ (*this)
{
@@ -196,28 +228,76 @@ public:
virtual void
traverse (semantics::class_&);
- // Composite value with data member.
+ // Traverse a data member with type, which can be a simple or composite
+ // value type, or an object pointer (with a simple or composite id).
//
virtual void
+ traverse (semantics::data_member& m)
+ {
+ traverse (m, utype (m), string (), string ());
+ }
+
+ virtual void
traverse (semantics::data_member&,
- semantics::class_& comp,
- std::string const& key_prefix,
- std::string const& default_name);
+ semantics::type&,
+ string const& key_prefix,
+ string const& default_name,
+ semantics::class_* top_object = 0); // If not 0, switch top object.
public:
// Return column prefix for composite data member.
//
static string
column_prefix (semantics::data_member&,
- std::string const& key_prefix = std::string (),
- std::string const& default_name = std::string ());
+ string const& key_prefix = string (),
+ string const& default_name = string ());
+
+ // Return column prefix up to (but not including) the last member
+ // in the path.
+ //
+ static string
+ column_prefix (data_member_path const&);
protected:
+ string key_prefix_;
+ string default_name_;
+
string column_prefix_;
data_member_path member_path_;
data_member_scope member_scope_;
+protected:
+ semantics::data_member*
+ id () const
+ {
+ if (root_ != 0)
+ return root_id_ ? root_ : 0; // Cannot have ids below root.
+ else
+ {
+ assert (!member_path_.empty ());
+ return context::id (member_path_);
+ }
+ }
+
+ string
+ column_type ()
+ {
+ if (member_path_.empty ())
+ {
+ assert (root_ != 0);
+ return context::column_type (*root_, key_prefix_);
+ }
+ else
+ return context::column_type (
+ member_path_, key_prefix_, (root_ != 0 && (root_id_ || root_op_)));
+ }
+
+private:
+ semantics::data_member* root_; // Root member if traversing from a member.
+ bool root_id_; // True if traversing root as object id.
+ bool root_op_; // True if traversing root as object pointer.
+
private:
void
init ()
@@ -227,21 +307,21 @@ private:
}
private:
+ virtual void
+ traverse_member (semantics::data_member&, semantics::type&);
+
struct member: traversal::data_member, context
{
- member (object_columns_base& oc)
- : oc_ (oc), first_ (true)
- {
- }
+ member (object_columns_base& oc): oc_ (oc) {}
virtual void
traverse (semantics::data_member&);
public:
object_columns_base& oc_;
- bool first_;
};
+ bool first_;
bool top_level_;
member member_;
@@ -249,6 +329,56 @@ private:
traversal::inherits inherits_;
};
+struct object_columns_list: object_columns_base
+{
+ object_columns_list (bool ignore_inverse = true)
+ : ignore_inverse_ (ignore_inverse)
+ {
+ }
+
+ object_columns_list (string const& column_prefix, bool ignore_inverse = true)
+ : object_columns_base (true, column_prefix),
+ ignore_inverse_ (ignore_inverse)
+ {
+ }
+
+ struct column
+ {
+ column (std::string const& n,
+ std::string const& t,
+ semantics::data_member& m)
+ : name (n), type (t), member (&m)
+ {
+ }
+
+ std::string name;
+ std::string type;
+ semantics::data_member* member;
+ };
+
+ typedef std::vector<column> columns;
+ typedef columns::const_iterator iterator;
+
+ iterator
+ begin () const {return columns_.begin ();}
+
+ iterator
+ end () const {return columns_.end ();}
+
+ columns::size_type
+ size () const {return columns_.size ();}
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual bool
+ traverse_column (semantics::data_member&, string const&, bool);
+
+private:
+ bool ignore_inverse_;
+ columns columns_;
+};
+
// Traverse composite values that are class template instantiations.
//
struct typedefs: traversal::typedefs, context
diff --git a/odb/context.cxx b/odb/context.cxx
index f4fa651..15579f3 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -210,6 +210,32 @@ context ()
context* context::current_;
+semantics::data_member* context::
+id (data_member_path const& mp)
+{
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ if (id (**i))
+ return *i;
+ }
+
+ return 0;
+}
+
+semantics::data_member* context::
+object_pointer (data_member_path const& mp)
+{
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ if (object_pointer (utype (**i)))
+ return *i;
+ }
+
+ return 0;
+}
+
bool context::
readonly (data_member_path const& mp, data_member_scope const& ms)
{
@@ -767,6 +793,9 @@ column_name (data_member_path const& mp) const
string context::
column_name (semantics::data_member& m, string const& p, string const& d) const
{
+ if (p.empty () && d.empty ())
+ return column_name (m);
+
// A container column name can be specified for the member or for the
// container type.
//
@@ -786,6 +815,23 @@ column_name (semantics::data_member& m, string const& p, string const& d) const
}
string context::
+column_type (const data_member_path& mp, string const& kp, bool id)
+{
+ if (kp.empty ())
+ {
+ // Return the id type if this member is or is a part of an object id
+ // or pointer to object.
+ //
+ return mp.back ()->get<string> (
+ id || context::id (mp) || object_pointer (mp)
+ ? "column-id-type"
+ : "column-type");
+ }
+ else
+ return indirect_value<string> (*mp.back (), kp + "-column-type");
+}
+
+string context::
column_type (semantics::data_member& m, string const& kp)
{
return kp.empty ()
@@ -1221,14 +1267,23 @@ namespace
struct column_count_impl: object_members_base
{
virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ size_t t (c_.total);
+
+ object_members_base::traverse_pointer (m, c);
+
+ if (context::inverse (m))
+ c_.inverse += (c_.total - t);
+ }
+
+ virtual void
traverse_simple (semantics::data_member& m)
{
c_.total++;
- if (m.count ("id"))
+ if (id ())
c_.id++;
- else if (context::inverse (m))
- c_.inverse++;
else if (context::readonly (member_path_, member_scope_))
c_.readonly++;
else if (context::version (m))
@@ -1268,6 +1323,15 @@ namespace
}
virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ if (context::is_a (member_path_, member_scope_, flags_))
+ r_++;
+
+ // No need to go inside.
+ }
+
+ virtual void
traverse_simple (semantics::data_member&)
{
if (context::is_a (member_path_, member_scope_, flags_))
diff --git a/odb/context.hxx b/odb/context.hxx
index e67af48..7e62161 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -364,6 +364,12 @@ public:
return t.get<semantics::class_*> ("element-type", 0);
}
+ // If this data member is or is part of an object pointer, then
+ // return the member that is the pointer. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ object_pointer (data_member_path const&);
+
static bool
abstract (semantics::class_& c)
{
@@ -385,6 +391,12 @@ public:
return m.count ("id");
}
+ // If this data member is or is part of an id member, then return
+ // the member that is marked as the id. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ id (data_member_path const&);
+
static bool
auto_ (semantics::data_member& m)
{
@@ -500,6 +512,11 @@ public:
string const& default_name) const;
string
+ column_type (const data_member_path&,
+ string const& key_prefix = string (),
+ bool id = false); // Pass true if this type is object id other
+ // than because of the members in the path.
+ string
column_type (semantics::data_member&, string const& key_prefix = string ());
string
diff --git a/odb/gcc.hxx b/odb/gcc.hxx
index a9da1bd..da77027 100644
--- a/odb/gcc.hxx
+++ b/odb/gcc.hxx
@@ -42,6 +42,7 @@ extern "C"
#endif
#include <diagnostic.h>
+#include <output.h>
}
#ifndef LOCATION_COLUMN
diff --git a/odb/odb.cxx b/odb/odb.cxx
index dd9b180..b17369a 100644
--- a/odb/odb.cxx
+++ b/odb/odb.cxx
@@ -692,6 +692,26 @@ main (int argc, char* argv[])
<< "#endif" << endl
<< endl;
+ // Add ODB compiler metaprogramming tests.
+ //
+ os << "namespace odb" << endl
+ << "{" << endl
+ << "namespace compiler" << endl
+ << "{" << endl;
+
+ // operator< test, used in validator.
+ //
+ os << "template <typename T>" << endl
+ << "bool" << endl
+ << "has_lt_operator (T const& x, T const& y)" << endl
+ << "{" << endl
+ << "bool r (x < y);" << endl
+ << "return r;" << endl
+ << "}" << endl;
+
+ os << "}" << endl
+ << "}" << endl;
+
// Add custom prologue if any.
//
// NOTE: if you change the format, you also need to update code
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 5149bee..6c92f0f 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -493,18 +493,8 @@ check_spec_decl_type (tree d,
return false;
}
}
- else if (p == "id_type")
- {
- // Id type can only be used for types.
- //
- if (!TYPE_P (d))
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type" << endl;
- return false;
- }
- }
else if (p == "type" ||
+ p == "id_type" ||
p == "value_type" ||
p == "index_type" ||
p == "key_type")
@@ -1841,6 +1831,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p)
p == "key_options" ||
p == "id_options" ||
p == "type" ||
+ p == "id_type" ||
p == "value_type" ||
p == "index_type" ||
p == "key_type" ||
diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx
index bf325f7..381d77f 100644
--- a/odb/relational/common.cxx
+++ b/odb/relational/common.cxx
@@ -78,16 +78,11 @@ namespace relational
}
}
- bool query_columns_base::
- traverse_column (semantics::data_member& m, string const& column, bool)
+ void query_columns_base::
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
- semantics::class_* ptr (object_pointer (utype (m)));
-
- if (ptr == 0)
- return false;
-
string name (public_name (m));
- bool inv (inverse (m));
+ bool inv (inverse (m, key_prefix_));
if (decl_)
{
@@ -101,7 +96,7 @@ namespace relational
os << "typedef" << endl
<< "odb::query_pointer<" << endl
<< " odb::pointer_query_columns<" << endl
- << " " << class_fq_name (*ptr) << "," << endl
+ << " " << class_fq_name (c) << "," << endl
<< " " << name << "_alias_ > >" << endl
<< name << "_type_ ;"
<< endl
@@ -111,11 +106,32 @@ namespace relational
}
else
{
- // For now use column name as table alias.
- // @@ This will become problematic when we add support for composite ids.
+ // Come up with a table alias. Generally, we want it to be based
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
//
+ string alias;
+
+ if (composite_wrapper (utype ((*id_member (c)))))
+ {
+ string p (column_prefix (m, key_prefix_, default_name_));
+
+ if (p.empty ())
+ p = public_name_db (m);
+ else
+ p.resize (p.size () - 1); // Remove trailing underscore.
+
+ alias = column_prefix_ + p;
+ }
+ else
+ alias = column_prefix_ + column_name (m, key_prefix_, default_name_);
+
os << "const char " << scope_ << "::" << name << "_alias_[] = " <<
- strlit (quote_id (column)) << ";"
+ strlit (quote_id (alias)) << ";"
<< endl;
if (inv)
@@ -123,8 +139,6 @@ namespace relational
<< scope_ << "::" << name << ";"
<< endl;
}
-
- return true;
}
// query_columns
@@ -132,13 +146,13 @@ namespace relational
query_columns::
query_columns (bool ptr)
- : ptr_ (ptr), decl_ (true)
+ : ptr_ (ptr), decl_ (true), in_ptr_ (false)
{
}
query_columns::
query_columns (bool ptr, semantics::class_& c) //@@ context::{cur,top}_object
- : ptr_ (ptr), decl_ (false)
+ : ptr_ (ptr), decl_ (false), in_ptr_ (false)
{
scope_ = ptr ? "pointer_query_columns" : "query_columns";
scope_ += "< " + class_fq_name (c) + ", table >";
@@ -164,12 +178,13 @@ namespace relational
}
string name (public_name (*m));
+ string suffix (in_ptr_ ? "_column_type_" : "_type_");
if (decl_)
{
os << "// " << name << endl
<< "//" << endl
- << "struct " << name << "_type_";
+ << "struct " << name << suffix;
// Derive from the base in query_columns_base. It contains columns
// data for the pointer members.
@@ -178,27 +193,29 @@ namespace relational
os << ": " << name << "_base_";
os << "{"
- << name << "_type_ (){}"; // For some reason GCC needs this c-tor
- // if we make the static member const.
+ << name << suffix << " (){}"; // For some reason GCC needs this c-tor
+ // if we make the static member const.
object_columns_base::traverse_composite (m, c);
- os << "};"
- << "static const " << name << "_type_ " << name << ";"
- << endl;
+ os << "};";
+
+ if (!in_ptr_)
+ os << "static const " << name << "_type_ " << name << ";"
+ << endl;
}
else
{
// Handle nested members first.
//
string old_scope (scope_);
- scope_ += "::" + name + "_type_";
+ scope_ += "::" + name + suffix;
object_columns_base::traverse_composite (m, c);
scope_ = old_scope;
- // Composite member.
+ // Composite member. Note that here we don't use suffix.
//
os << "template <const char* table>" << endl
<< "const typename " << scope_ << "::" << name << "_type_" << endl
@@ -216,39 +233,16 @@ namespace relational
<< "}";
}
- bool query_columns::
- traverse_column (semantics::data_member& m, string const& column, bool)
+ void query_columns::
+ column_common (semantics::data_member& m,
+ string const& type,
+ string const& column,
+ string const& suffix)
{
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
- semantics::class_* ptr (object_pointer (t));
-
- if (ptr != 0)
- {
- // If this is for the pointer_query_columns and the member is not
- // inverse, then create the normal member corresponding to the id
- // column. This will allow the user to check it for NULL or to
- // compare ids. In case this is for query_columns, then for the
- // inverse member everything has been generated in query_columns_base.
- //
- if (inverse (m))
- return false;
- }
-
string name (public_name (m));
if (decl_)
{
- string type;
- if (ptr != 0)
- {
- semantics::data_member& id (*id_member (*ptr));
- semantics::type& t (utype (id, hint));
- type = t.fq_name (hint);
- }
- else
- type = t.fq_name (hint);
-
string type_id (database_type_id (m));
os << "// " << name << endl
@@ -259,43 +253,14 @@ namespace relational
<< " " << db << "::value_traits<" << endl
<< " " << type << "," << endl
<< " " << type_id << " >::query_type," << endl
- << " " << type_id << " >" << endl;
-
- if (ptr == 0 || ptr_)
- os << name << "_type_;"
- << endl;
- else
- {
- os << name << "_column_type_;"
- << endl
- << "typedef" << endl
- << "odb::query_pointer<" << endl
- << " odb::pointer_query_columns<" << endl
- << " " << class_fq_name (*ptr) << "," << endl
- << " " << name << "_alias_ > >" << endl
- << name << "_pointer_type_;"
- << endl;
-
- // If this is a non-inverse relationship, then make the column have
- // a dual interface: that of an object pointer and of an id column.
- // The latter allows the user to, for example, use the is_null()
- // test in a natural way. For inverse relationships there is no
- // column and so the column interface is not available.
- //
- os << "struct " << name << "_type_: " <<
- name << "_pointer_type_, " << name << "_column_type_"
- << "{";
-
- column_ctor (name + "_type_", name + "_column_type_");
-
- os << "};";
- }
-
- os << "static const " << name << "_type_ " << name << ";"
+ << " " << type_id << " >" << endl
+ << name << suffix << ";"
<< endl;
}
else
{
+ // Note that here we don't use suffix.
+ //
os << "template <const char* table>" << endl
<< "const typename " << scope_ << "::" << name << "_type_" << endl
<< scope_ << "::" << name << " (" << "table, " <<
@@ -306,10 +271,134 @@ namespace relational
os << ");"
<< endl;
}
+ }
+
+ bool query_columns::
+ traverse_column (semantics::data_member& m, string const& column, bool)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ column_common (m, t.fq_name (hint), column);
+
+ if (decl_)
+ {
+ string name (public_name (m));
+
+ os << "static const " << name << "_type_ " << name << ";"
+ << endl;
+ }
return true;
}
+ void query_columns::
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // If this is for the pointer_query_columns and the member is not
+ // inverse, then create the normal member corresponding to the id
+ // column. This will allow the user to check it for NULL or to
+ // compare ids. In case this is for query_columns, then for the
+ // inverse member everything has been generated in query_columns_base.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ string name (public_name (m));
+
+ semantics::data_member& id (*id_member (c));
+ semantics::names* hint;
+ semantics::type& t (utype (id, hint));
+
+ if (composite_wrapper (t))
+ {
+ // Composite id.
+ //
+
+ // For pointer_query_columns generate normal composite mapping.
+ //
+ if (ptr_)
+ object_columns_base::traverse_pointer (m, c);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ in_ptr_ = true;
+ object_columns_base::traverse_pointer (m, c);
+ in_ptr_ = false;
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " << name << "_column_type_"
+ << "{"
+ << "};";
+
+ os << "static const " << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ }
+ else
+ {
+ // Simple id.
+ //
+ string type (t.fq_name (hint));
+ string column (column_prefix_ +
+ column_name (m, key_prefix_, default_name_));
+
+ // For pointer_query_columns generate normal column mapping.
+ //
+ if (ptr_)
+ column_common (m, type, column);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ column_common (m, type, column, "_column_type_");
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " << name << "_column_type_"
+ << "{";
+
+ column_ctor (name + "_type_", name + "_column_type_");
+
+ os << "};";
+ }
+ }
+
+ if (decl_)
+ os << "static const " << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+
//
// Dynamic traversal support.
//
diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx
index d552a72..276868c 100644
--- a/odb/relational/common.hxx
+++ b/odb/relational/common.hxx
@@ -42,7 +42,7 @@ namespace relational
protected:
// For virtual inheritance only. Should not be actually called.
//
- member_base (); // {assert (false);}
+ member_base ();
protected:
string var_override_;
@@ -51,6 +51,176 @@ namespace relational
string key_prefix_;
};
+ // Template argument is the database SQL type (sql_type).
+ //
+ template <typename T>
+ struct member_base_impl: virtual member_base
+ {
+ typedef member_base_impl base_impl;
+
+ member_base_impl (base const& x): base (x) {}
+
+ protected:
+ member_base_impl () {}
+
+ public:
+ virtual T const&
+ member_sql_type (semantics::data_member&) = 0;
+
+ virtual void
+ traverse (semantics::data_member&);
+
+ struct member_info
+ {
+ semantics::data_member& m; // Member.
+ semantics::type& t; // Cvr-unqualified member C++ type, note
+ // that m.type () may not be the same as t.
+ semantics::class_* ptr; // Pointed-to object if m is an object
+ // pointer. In this case t is the id type
+ // while fq_type_ is the pointer fq-type.
+ semantics::type* wrapper; // Wrapper type if member is a composite or
+ // container wrapper, also cvr-unqualified.
+ // In this case t is the wrapped type.
+ bool cq; // True if the original (wrapper) type
+ // is const-qualified.
+ T const* st; // Member SQL type (only simple values).
+ string& var; // Member variable name with trailing '_'.
+
+ // C++ type fq-name.
+ //
+ string
+ fq_type (bool unwrap = true) const
+ {
+ semantics::names* hint;
+
+ if (wrapper != 0 && unwrap)
+ {
+ // Use the hint from the wrapper unless the wrapped type
+ // is qualified.
+ //
+ hint = wrapper->get<semantics::names*> ("wrapper-hint");
+ utype (*context::wrapper (*wrapper), hint);
+ return t.fq_name (hint);
+ }
+
+ // Use the original type from 'm' instead of 't' since the hint may
+ // be invalid for a different type. Plus, if a type is overriden,
+ // then the fq_type must be as well.
+ //
+ if (ptr != 0)
+ {
+ semantics::type& t (utype (*id_member (*ptr), hint));
+ return t.fq_name (hint);
+ }
+ else if (fq_type_.empty ())
+ {
+ semantics::type& t (utype (m, hint));
+ return t.fq_name (hint);
+ }
+ else
+ return fq_type_;
+ }
+
+ string
+ ptr_fq_type () const
+ {
+ assert (ptr != 0);
+
+ if (fq_type_.empty ())
+ {
+ // If type is overridden so should fq_type so it is safe to
+ // get the type from the member.
+ //
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+ return t.fq_name (hint);
+ }
+ else
+ return fq_type_;
+ }
+
+ string const& fq_type_;
+
+ member_info (semantics::data_member& m_,
+ semantics::type& t_,
+ semantics::type* wrapper_,
+ bool cq_,
+ string& var_,
+ string const& fq_type)
+ : m (m_),
+ t (t_),
+ ptr (0),
+ wrapper (wrapper_),
+ cq (cq_),
+ st (0),
+ var (var_),
+ fq_type_ (fq_type)
+ {
+ }
+ };
+
+ bool
+ container (member_info& mi)
+ {
+ // This cannot be a container if we have a type override.
+ //
+ return type_override_ == 0 && context::container (mi.m);
+ }
+
+ // The false return value indicates that no further callbacks
+ // should be called for this member.
+ //
+ virtual bool
+ pre (member_info&)
+ {
+ return true;
+ }
+
+ virtual void
+ post (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_composite (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_container (member_info&)
+ {
+ }
+
+ // Note that by default traverse_object_pointer() will traverse the
+ // pointed-to object id type.
+ //
+ virtual void
+ traverse_object_pointer (member_info&);
+
+ virtual void
+ traverse_simple (member_info&) = 0;
+ };
+
+ //
+ //
+ struct member_database_type_id: virtual member_base
+ {
+ typedef member_database_type_id base;
+
+ member_database_type_id (semantics::type* type = 0,
+ string const& fq_type = string (),
+ string const& key_prefix = string ())
+ : member_base (type, fq_type, key_prefix)
+ {
+ }
+
+ virtual string
+ database_type_id (semantics::data_member&)
+ {
+ assert (false);
+ }
+ };
+
//
//
struct query_columns_base: object_columns_base, virtual context
@@ -66,8 +236,8 @@ namespace relational
virtual void
traverse_composite (semantics::data_member*, semantics::class_&);
- virtual bool
- traverse_column (semantics::data_member&, string const&, bool);
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
protected:
bool decl_;
@@ -103,13 +273,24 @@ namespace relational
virtual void
traverse_composite (semantics::data_member*, semantics::class_&);
+ virtual void
+ column_common (semantics::data_member&,
+ string const& type,
+ string const& column,
+ string const& suffix = "_type_");
+
virtual bool
traverse_column (semantics::data_member&, string const&, bool);
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
protected:
bool ptr_;
bool decl_;
+ bool in_ptr_; // True if we are "inside" an object pointer.
+
string scope_;
string table_;
string default_table_;
@@ -349,4 +530,6 @@ namespace relational
}
}
+#include <odb/relational/common.txx>
+
#endif // ODB_RELATIONAL_COMMON_HXX
diff --git a/odb/relational/common.txx b/odb/relational/common.txx
new file mode 100644
index 0000000..1d3ecbf
--- /dev/null
+++ b/odb/relational/common.txx
@@ -0,0 +1,112 @@
+// file : odb/relational/common.txx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace relational
+{
+ //
+ // member_base_impl
+ //
+
+ template <typename T>
+ void member_base_impl<T>::
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ string var;
+
+ if (!var_override_.empty ())
+ var = var_override_;
+ else
+ {
+ string const& name (m.name ());
+ var = name + (name[name.size () - 1] == '_' ? "" : "_");
+ }
+
+ bool cq (type_override_ != 0 ? false : const_type (m.type ()));
+ semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m));
+
+ semantics::type* cont;
+ if (semantics::class_* c = object_pointer (t))
+ {
+ semantics::type& t (utype (*id_member (*c)));
+ semantics::class_* comp (composite_wrapper (t));
+
+ member_info mi (m,
+ (comp != 0 ? *comp : t),
+ (comp != 0 && wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_); // Pointer type.
+
+ mi.ptr = c;
+
+ if (comp == 0)
+ mi.st = &member_sql_type (m);
+
+ if (pre (mi))
+ {
+ traverse_object_pointer (mi);
+ post (mi);
+ }
+ }
+ else if (semantics::class_* c = composite_wrapper (t))
+ {
+ // If t is a wrapper, pass the wrapped type. Also pass the
+ // original, wrapper type.
+ //
+ member_info mi (m,
+ *c,
+ (wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_composite (mi);
+ post (mi);
+ }
+ }
+ // This cannot be a container if we have a type override.
+ //
+ else if (type_override_ == 0 && (cont = context::container (m)))
+ {
+ // The same unwrapping logic as for composite values.
+ //
+ member_info mi (m,
+ *cont,
+ (wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_container (mi);
+ post (mi);
+ }
+ }
+ else
+ {
+ member_info mi (m, t, 0, cq, var, fq_type_override_);
+ mi.st = &member_sql_type (m);
+
+ if (pre (mi))
+ {
+ traverse_simple (mi);
+ post (mi);
+ }
+ }
+ }
+
+ template <typename T>
+ void member_base_impl<T>::
+ traverse_object_pointer (member_info& mi)
+ {
+ 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 cf103b5..dda9e2a 100644
--- a/odb/relational/header.hxx
+++ b/odb/relational/header.hxx
@@ -441,8 +441,11 @@ namespace relational
//
size_t n;
- if (class_* kc = composite_wrapper (*kt))
- n = column_count (*kc).total;
+ class_* ptr (object_pointer (*kt));
+ semantics::type& t (ptr == 0 ? *kt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ n = column_count (*comp).total;
else
n = 1;
@@ -461,19 +464,27 @@ namespace relational
//
// Value is also a key.
//
- //if (class_* vc = composite_wrapper (vt))
- // cond_columns += column_count (*vc).total;
- //else
- // cond_columns++;
-
+ // class_* ptr (object_pointer (vt));
+ // semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+ //
+ // if (class_* comp = composite_wrapper (t))
+ // cond_columns += column_count (*comp).total;
+ // else
+ // cond_columns++;
+ //
break;
}
}
- if (class_* vc = composite_wrapper (vt))
- data_columns += column_count (*vc).total;
- else
- data_columns++;
+ {
+ class_* ptr (object_pointer (vt));
+ semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ data_columns += column_count (*comp).total;
+ else
+ data_columns++;
+ }
// Store column counts for the source generator.
//
@@ -786,7 +797,7 @@ namespace relational
}
}
- os << "const data_image_type&, database&);"
+ os << "const data_image_type&, database*);"
<< endl;
// insert_one
@@ -1138,7 +1149,7 @@ namespace relational
// init (object, image)
//
os << "static void" << endl
- << "init (object_type&, const image_type&, database&);"
+ << "init (object_type&, const image_type&, database*);"
<< endl;
// init (id_image, id)
@@ -1503,7 +1514,7 @@ namespace relational
// init (view, image)
//
os << "static void" << endl
- << "init (view_type&, const image_type&, database&);"
+ << "init (view_type&, const image_type&, database*);"
<< endl;
// column_count
@@ -1605,7 +1616,7 @@ namespace relational
// init (value, image)
//
os << "static void" << endl
- << "init (value_type&, const image_type&, database&);"
+ << "init (value_type&, const image_type&, database*);"
<< endl;
os << "};";
diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx
index 5f6470d..66db3d6 100644
--- a/odb/relational/model.hxx
+++ b/odb/relational/model.hxx
@@ -26,12 +26,10 @@ namespace relational
{
typedef object_columns base;
- object_columns (sema_rel::model& model,
- sema_rel::table& table,
- string const& prefix = string ())
+ object_columns (sema_rel::model& model, sema_rel::table& table)
: model_ (model),
table_ (table),
- prefix_ (prefix),
+ pkey_ (0),
id_override_ (false)
{
}
@@ -77,20 +75,25 @@ namespace relational
virtual void
traverse (semantics::data_member& m,
- semantics::class_& c,
- std::string const& kp,
- std::string const& dn)
+ semantics::type& t,
+ string const& kp,
+ string const& dn,
+ semantics::class_* to = 0)
{
// This overrides the member name for a composite container value
// or key.
//
if (!kp.empty ())
{
- id_prefix_ = kp + ".";
- id_override_ = true;
+ semantics::class_* c (object_pointer (t));
+ if (composite_wrapper (c == 0 ? t : utype (*id_member (*c))))
+ {
+ id_prefix_ = kp + ".";
+ id_override_ = true;
+ }
}
- object_columns_base::traverse (m, c, kp, dn);
+ object_columns_base::traverse (m, t, kp, dn, to);
}
using object_columns_base::traverse;
@@ -98,23 +101,22 @@ namespace relational
virtual bool
traverse_column (semantics::data_member& m, string const& name, bool)
{
- // Ignore inverse object pointers.
- //
- if (inverse (m))
- return false;
+ bool id (object_columns_base::id ()); // Id or part of.
+ bool null (!id && context::null (m, key_prefix_));
- string id (id_prefix_ + (prefix_.empty () ? m.name () : prefix_));
+ string col_id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
sema_rel::column& c (
- model_.new_node<sema_rel::column> (
- id, column_type (m, prefix_), context::null (m, prefix_)));
+ model_.new_node<sema_rel::column> (col_id, column_type (), null));
+
c.set ("cxx-node", static_cast<semantics::node*> (&m));
model_.new_edge<sema_rel::unames> (table_, c, name);
// An id member cannot have a default value.
//
- if (!context::id (m))
+ if (!id)
{
string const& d (default_ (m));
@@ -124,14 +126,12 @@ namespace relational
// If we have options, add them.
//
- string const& o (column_options (m, prefix_));
+ string const& o (column_options (m, key_prefix_));
if (!o.empty ())
c.options (o);
- constraints (m, name, id, c);
- reference (m, name, id, c);
-
+ constraints (m, name, col_id, c);
return true;
}
@@ -188,58 +188,129 @@ namespace relational
string const& /* id */,
sema_rel::column& c)
{
- if (!id (m))
- return;
-
- sema_rel::primary_key& pk (
- model_.new_node<sema_rel::primary_key> (m.count ("auto")));
- pk.set ("cxx-node", static_cast<semantics::node*> (&m));
-
- model_.new_edge<sema_rel::contains> (pk, c);
-
- // In most databases the primary key constraint can be manipulated
- // without an explicit name. So we use the special empty name for
- // primary keys in order not to clash with columns and other
- // constraints. If the target database does not support unnamed
- // primary key manipulation, then the database-specific code will
- // have to come up with a suitable name.
- //
- model_.new_edge<sema_rel::unames> (table_, pk, "");
+ if (table_.is_a<sema_rel::object_table> ())
+ {
+ if (semantics::data_member* idm = id ())
+ {
+ if (pkey_ == 0)
+ {
+ pkey_ = &model_.new_node<sema_rel::primary_key> (
+ m.count ("auto"));
+ pkey_->set ("cxx-node", static_cast<semantics::node*> (idm));
+
+ // In most databases the primary key constraint can be
+ // manipulated without an explicit name. So we use the special
+ // empty name for primary keys in order not to clash with
+ // columns and other constraints. If the target database does
+ // not support unnamed primary key manipulation, then the
+ // database-specific code will have to come up with a suitable
+ // name.
+ //
+ model_.new_edge<sema_rel::unames> (table_, *pkey_, "");
+ }
+
+ model_.new_edge<sema_rel::contains> (*pkey_, c);
+ }
+ }
}
virtual void
- reference (semantics::data_member& m,
- string const& name,
- string const& id,
- sema_rel::column& c)
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
- semantics::class_* p (object_pointer (member_utype (m, prefix_)));
+ using sema_rel::column;
- if (p == 0)
+ // Ignore inverse object pointers.
+ //
+ if (inverse (m, key_prefix_))
return;
+ string id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
+
sema_rel::foreign_key& fk (
model_.new_node<sema_rel::foreign_key> (
id,
- table_name (*p),
+ table_name (c),
true)); // deferred
fk.set ("cxx-node", static_cast<semantics::node*> (&m));
- fk.referenced_columns ().push_back (column_name (*id_member (*p)));
+ bool simple;
+
+ // Get referenced columns.
+ //
+ {
+ semantics::data_member& idm (*id_member (c));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (idm);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+
+ simple = (fk.referenced_columns ().size () == 1);
+ }
+
+ // Get the position of the last column.
+ //
+ sema_rel::table::names_iterator i (table_.names_end ());
+
+ while (i != table_.names_begin ())
+ {
+ --i;
+
+ if (i->nameable ().is_a<column> ())
+ break;
+ }
+
+ // Traverse the object pointer as columns.
+ //
+ object_columns_base::traverse_pointer (m, c);
- model_.new_edge<sema_rel::contains> (fk, c);
+ // Add the newly added columns to the foreign key.
+ //
+ if (i != table_.names_end ())
+ ++i;
+ else
+ i = table_.names_begin ();
+
+ for (; i != table_.names_end (); ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
+ else
+ break;
+ }
// Derive the constraint name. Generally, we want it to be based
- // on the column name. This is straightforward for single column
- // references. In case of the composite ids, we will need to use
- // the column prefix which is based on the data member name,
- // unless overridden (see how the column pragma works for members
- // of composite value types). @@ This is a TODO. Perhaps use the
- // up-to-and-including composite member prefix? Though it can be
- // empty.
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
//
- model_.new_edge<sema_rel::unames> (table_, fk, name + "_fk");
+ string name;
+
+ if (simple)
+ name = fk.contains_begin ()->column ().name ();
+ else
+ {
+ string p (column_prefix (m, key_prefix_, default_name_));
+
+ if (p.empty ())
+ p = public_name_db (m);
+
+ name = column_prefix_ + p;
+ }
+
+ // Add an underscore unless we already have one.
+ //
+ if (name[name.size () - 1] != '_')
+ name += '_';
+
+ model_.new_edge<sema_rel::unames> (table_, fk, name + "fk");
}
protected:
@@ -249,7 +320,7 @@ namespace relational
protected:
sema_rel::model& model_;
sema_rel::table& table_;
- string prefix_;
+ sema_rel::primary_key* pkey_;
string id_prefix_;
bool id_override_;
};
@@ -310,13 +381,15 @@ namespace relational
using semantics::type;
using semantics::data_member;
+ using sema_rel::index;
+ using sema_rel::column;
+
// Ignore inverse containers of object pointers.
//
if (inverse (m, "value"))
return;
container_kind_type ck (container_kind (ct));
- type& vt (container_vt (ct));
qname const& name (table_name (m, table_prefix_));
@@ -332,15 +405,14 @@ namespace relational
model_.new_edge<sema_rel::qnames> (model_, t, name);
- // object_id (simple value, for now)
+ // object_id
//
- string id_name (column_name (m, "id", "object_id"));
{
- instance<object_columns> oc (model_, t, "id");
- oc->traverse_column (m, id_name, true);
+ instance<object_columns> oc (model_, t);
+ oc->traverse (m, container_idt (m), "id", "object_id");
}
- // Foreign key for the object id.
+ // Foreign key and index for the object id.
//
{
sema_rel::foreign_key& fk (
@@ -349,105 +421,91 @@ namespace relational
table_name (*context::top_object),
false, // immediate
sema_rel::foreign_key::cascade));
-
fk.set ("cxx-node", static_cast<semantics::node*> (&m));
- fk.referenced_columns ().push_back (
- column_name (
- *id_member (*context::top_object)));
+ index& in (model_.new_node<index> (id + ".id"));
+ in.set ("cxx-node", static_cast<semantics::node*> (&m));
+
+ // Get referenced columns.
+ //
+ {
+ data_member& idm (*id_member (*context::top_object));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (idm);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+ }
// All the columns we have in this table so far are for the
- // object id.
+ // object id. Add them to the foreign key and the index.
//
for (sema_rel::table::names_iterator i (t.names_begin ());
i != t.names_end ();
++i)
- model_.new_edge<sema_rel::contains> (
- fk, dynamic_cast<sema_rel::column&> (i->nameable ()));
+ {
+ column& c (dynamic_cast<column&> (i->nameable ()));
- // Derive the constraint name. See the comment for the other
- // foreign key code above.
+ model_.new_edge<sema_rel::contains> (fk, c);
+ model_.new_edge<sema_rel::contains> (in, c);
+ }
+
+ // Derive the names. See the comment for the other foreign key
+ // code above.
//
- model_.new_edge<sema_rel::unames> (t, fk, id_name + "_fk");
- }
+ // Note also that id_name can be a column prefix (if id is
+ // composite), in which case it can be empty and if not, then
+ // it will most likely already contain a trailing underscore.
+ //
+ string id_name (column_name (m, "id", "object_id"));
- // index (simple value)
- //
- string index_name;
- bool ordered (ck == ck_ordered && !unordered (m));
- if (ordered)
- {
- instance<object_columns> oc (model_, t, "index");
- index_name = column_name (m, "index", "index");
- oc->traverse_column (m, index_name, true);
- }
+ if (id_name.empty ())
+ id_name = "object_id";
- // key (simple or composite value)
- //
- if (ck == ck_map || ck == ck_multimap)
- {
- type& kt (container_kt (ct));
+ if (id_name[id_name.size () - 1] != '_')
+ id_name += '_';
- if (semantics::class_* ckt = composite_wrapper (kt))
- {
- instance<object_columns> oc (model_, t);
- oc->traverse (m, *ckt, "key", "key");
- }
- else
- {
- instance<object_columns> oc (model_, t, "key");
- string const& name (column_name (m, "key", "key"));
- oc->traverse_column (m, name, true);
- }
+ model_.new_edge<sema_rel::unames> (t, fk, id_name + "fk");
+
+ model_.new_edge<sema_rel::qnames> (
+ model_, in, name + "_" + id_name + "i");
}
- // value (simple or composite value)
+ // index (simple value)
//
+ bool ordered (ck == ck_ordered && !unordered (m));
+ if (ordered)
{
- if (semantics::class_* cvt = composite_wrapper (vt))
- {
- instance<object_columns> oc (model_, t);
- oc->traverse (m, *cvt, "value", "value");
- }
- else
- {
- instance<object_columns> oc (model_, t, "value");
- string const& name (column_name (m, "value", "value"));
- oc->traverse_column (m, name, true);
- }
- }
+ instance<object_columns> oc (model_, t);
+ oc->traverse (m, container_it (ct), "index", "index");
- // Create indexes.
- //
- using sema_rel::index;
- using sema_rel::column;
+ string col_name (column_name (m, "index", "index"));
- {
- index& i (model_.new_node<index> (id + ".id"));
- i.set ("cxx-node", static_cast<semantics::node*> (&m));
+ index& in (model_.new_node<index> (id + ".index"));
+ in.set ("cxx-node", static_cast<semantics::node*> (&m));
model_.new_edge<sema_rel::contains> (
- i, dynamic_cast<column&> (t.find (id_name)->nameable ()));
+ in, dynamic_cast<column&> (t.find (col_name)->nameable ()));
- //@@ Once id can be composite, we need to revise this (see
- // a comment for the foreign key generation above).
- //
model_.new_edge<sema_rel::qnames> (
- model_, i, name + "_" + id_name + "_i");
+ model_, in, name + "_" + col_name + "_i");
}
- if (ordered)
+ // key
+ //
+ if (ck == ck_map || ck == ck_multimap)
{
- index& i (model_.new_node<index> (id + ".index"));
- i.set ("cxx-node", static_cast<semantics::node*> (&m));
-
- model_.new_edge<sema_rel::contains> (
- i, dynamic_cast<column&> (t.find (index_name)->nameable ()));
+ instance<object_columns> oc (model_, t);
+ oc->traverse (m, container_kt (ct), "key", "key");
+ }
- // This is always a single column (simple value).
- //
- model_.new_edge<sema_rel::qnames> (
- model_, i, name + "_" + index_name + "_i");
+ // value
+ //
+ {
+ instance<object_columns> oc (model_, t);
+ oc->traverse (m, container_vt (ct), "value", "value");
}
}
diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx
index f4ce42e..20be733 100644
--- a/odb/relational/mssql/common.cxx
+++ b/odb/relational/mssql/common.cxx
@@ -16,91 +16,10 @@ namespace relational
// member_base
//
- void member_base::
- traverse (semantics::data_member& m)
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
{
- if (transient (m))
- return;
-
- string var;
-
- if (!var_override_.empty ())
- var = var_override_;
- else
- {
- string const& name (m.name ());
- var = name + (name[name.size () - 1] == '_' ? "" : "_");
- }
-
- bool cq (type_override_ != 0 ? false : const_type (m.type ()));
- semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m));
-
- semantics::type* cont;
- if (semantics::class_* c = composite_wrapper (t))
- {
- // If t is a wrapper, pass the wrapped type. Also pass the
- // original, wrapper type.
- //
- member_info mi (m,
- *c,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_composite (mi);
- post (mi);
- }
- }
- // This cannot be a container if we have a type override.
- //
- else if (type_override_ == 0 && (cont = context::container (m)))
- {
- // The same unwrapping logic as for composite values.
- //
- member_info mi (m,
- *cont,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_container (mi);
- post (mi);
- }
- }
- else
- {
- sql_type const& st (column_sql_type (m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (t))
- {
- member_info mi (m,
- utype (*id_member (*c)),
- 0,
- cq,
- var,
- fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_object_pointer (mi);
- post (mi);
- }
- }
- else
- {
- member_info mi (m, t, 0, cq, var, fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_simple (mi);
- post (mi);
- }
- }
- }
+ return parse_sql_type (column_type (m, key_prefix_), m);
}
void member_base::
@@ -406,15 +325,23 @@ namespace relational
};
member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_database_type_id::
member_database_type_id (semantics::type* type,
string const& fq_type,
string const& key_prefix)
- : relational::member_base (type, fq_type, key_prefix)
+ : member_base::base (type, fq_type, key_prefix), // virtual base
+ base (type, fq_type, key_prefix)
{
}
string member_database_type_id::
- database_type_id (type& m)
+ database_type_id (semantics::data_member& m)
{
type_id_.clear ();
member_base::traverse (m);
@@ -535,6 +462,8 @@ namespace relational
type_id_ = "mssql::id_rowversion";
}
+ entry<member_database_type_id> member_database_type_id_;
+
//
// query_columns
//
@@ -566,7 +495,7 @@ namespace relational
{
// For some types we need to pass precision and scale.
//
- sql_type const& st (column_sql_type (m));
+ sql_type const& st (parse_sql_type (column_type (), m));
switch (st.type)
{
diff --git a/odb/relational/mssql/common.hxx b/odb/relational/mssql/common.hxx
index 7f2009a..a04c9a3 100644
--- a/odb/relational/mssql/common.hxx
+++ b/odb/relational/mssql/common.hxx
@@ -12,118 +12,18 @@ namespace relational
{
namespace mssql
{
- struct member_base: virtual relational::member_base, context
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
{
- member_base (base const& x): base (x) {}
+ member_base (base const& x): base (x), base_impl (x) {}
// This c-tor is for the direct use inside the mssql namespace.
// If you do use this c-tor, you should also explicitly call
- // relational::member_base.
+ // relational::member_base (aka base).
//
member_base () {}
- virtual void
- traverse (semantics::data_member& m);
-
- struct member_info
- {
- semantics::data_member& m; // Member.
- semantics::type& t; // Cvr-unqualified member C++ type, note
- // that m.type () may not be the same as t.
- semantics::type* wrapper; // Wrapper type if member is a composite or
- // container wrapper, also cvr-unqualified.
- // In this case t is the wrapped type.
- bool cq; // True if the original (wrapper) type
- // is const-qualified.
- sql_type const* st; // Member SQL type (only simple values).
- string& var; // Member variable name with trailing '_'.
-
- // C++ type fq-name.
- //
- string
- fq_type (bool unwrap = true) const
- {
- semantics::names* hint;
-
- if (wrapper != 0 && unwrap)
- {
- // Use the hint from the wrapper unless the wrapped type
- // is qualified.
- //
- hint = wrapper->get<semantics::names*> ("wrapper-hint");
- utype (*context::wrapper (*wrapper), hint);
- return t.fq_name (hint);
- }
-
- // Use the original type from 'm' instead of 't' since the hint
- // may be invalid for a different type. Plus, if a type is
- // overriden, then the fq_type must be as well.
- //
- if (fq_type_.empty ())
- {
- semantics::type& t (utype (m, hint));
- return t.fq_name (hint);
- }
- else
- return fq_type_;
- }
-
- string const& fq_type_;
-
- member_info (semantics::data_member& m_,
- semantics::type& t_,
- semantics::type* wrapper_,
- bool cq_,
- string& var_,
- string const& fq_type)
- : m (m_),
- t (t_),
- wrapper (wrapper_),
- cq (cq_),
- st (0),
- var (var_),
- fq_type_ (fq_type)
- {
- }
- };
-
- bool
- container (member_info& mi)
- {
- // This cannot be a container if we have a type override.
- //
- return type_override_ == 0 && context::container (mi.m);
- }
-
- // The false return value indicates that no further callbacks
- // should be called for this member.
- //
- virtual bool
- pre (member_info&)
- {
- return true;
- }
-
- virtual void
- post (member_info&)
- {
- }
-
- virtual void
- traverse_composite (member_info&)
- {
- }
-
- virtual void
- traverse_container (member_info&)
- {
- }
-
- virtual void
- traverse_object_pointer (member_info& mi)
- {
- traverse_simple (mi);
- }
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
virtual void
traverse_simple (member_info&);
@@ -288,13 +188,17 @@ namespace relational
string type_;
};
- struct member_database_type_id: member_base
+ struct member_database_type_id: relational::member_database_type_id,
+ 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 ());
- string
- database_type_id (type&);
+
+ virtual string
+ database_type_id (semantics::data_member&);
virtual void
traverse_composite (member_info&);
@@ -361,13 +265,17 @@ namespace relational
{
has_long_data (bool& r): r_ (r) {}
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ if (!inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
virtual bool
traverse_column (semantics::data_member& m, string const&, bool)
{
- if (inverse (m))
- return false;
-
- sql_type const& st (column_sql_type (m));
+ sql_type const& st (parse_sql_type (column_type (), m));
switch (st.type)
{
diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx
index e882ba3..312cc03 100644
--- a/odb/relational/mssql/context.cxx
+++ b/odb/relational/mssql/context.cxx
@@ -557,17 +557,20 @@ namespace relational
}
sql_type const& context::
- column_sql_type (semantics::data_member& m, string const& kp)
+ parse_sql_type (string const& t, semantics::data_member& m)
{
- string key (kp.empty ()
- ? string ("mssql-column-sql-type")
- : "mssql-" + kp + "-column-sql-type");
+ // If this proves to be too expensive, we can maintain a
+ // cache of parsed types.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
- if (!m.count (key))
+ if (i != data_->sql_type_cache_.end ())
+ return i->second;
+ else
{
try
{
- m.set (key, parse_sql_type (column_type (m, kp)));
+ return (data_->sql_type_cache_[t] = parse_sql_type (t));
}
catch (invalid_sql_type const& e)
{
@@ -577,8 +580,6 @@ namespace relational
throw operation_failed ();
}
}
-
- return m.get<sql_type> (key);
}
sql_type context::
diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx
index 7530a9f..fe30746 100644
--- a/odb/relational/mssql/context.hxx
+++ b/odb/relational/mssql/context.hxx
@@ -5,6 +5,8 @@
#ifndef ODB_RELATIONAL_MSSQL_CONTEXT_HXX
#define ODB_RELATIONAL_MSSQL_CONTEXT_HXX
+#include <map>
+
#include <odb/relational/context.hxx>
namespace relational
@@ -83,8 +85,7 @@ namespace relational
{
public:
sql_type const&
- column_sql_type (semantics::data_member&,
- string const& key_prefix = string ());
+ parse_sql_type (string const&, semantics::data_member&);
public:
struct invalid_sql_type
@@ -132,6 +133,9 @@ namespace relational
struct data: base_context::data
{
data (std::ostream& os): base_context::data (os) {}
+
+ typedef std::map<string, sql_type> sql_type_cache;
+ sql_type_cache sql_type_cache_;
};
data* data_;
};
diff --git a/odb/relational/mssql/model.cxx b/odb/relational/mssql/model.cxx
index aec027c..c3f4948 100644
--- a/odb/relational/mssql/model.cxx
+++ b/odb/relational/mssql/model.cxx
@@ -28,7 +28,7 @@ namespace relational
{
// Make sure the column is mapped to an integer or DECIMAL type.
//
- switch (column_sql_type (m).type)
+ switch (parse_sql_type (column_type (), m).type)
{
case sql_type::BIT:
case sql_type::TINYINT:
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
index 1c11984..1b800aa 100644
--- a/odb/relational/mssql/source.cxx
+++ b/odb/relational/mssql/source.cxx
@@ -41,17 +41,16 @@ namespace relational
virtual void
column (semantics::data_member& m,
- string const& key_prefix,
string const& table,
string const& column)
{
// Don't add a column for auto id in the INSERT statement.
//
if (!(sk_ == statement_insert &&
- key_prefix.empty () &&
- id (m) && auto_(m)))
+ key_prefix_.empty () &&
+ context::id (m) && auto_(m))) // Only simple id can be auto.
{
- base::column (m, key_prefix, table, column);
+ base::column (m, table, column);
}
}
};
@@ -413,181 +412,21 @@ namespace relational
// init image
//
- struct init_image_member: relational::init_image_member, member_base
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
{
init_image_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- // Ignore containers (they get their own table) and inverse
- // object pointers (they are not present in this binding).
- //
- if (container (mi) || inverse (mi.m, key_prefix_))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- // If we are generating standard init() and this member
- // contains version, ignore it.
- //
- if (version (mi.m))
- return false;
-
- // For SQL Server we don't send auto id in INSERT statement
- // (nor in UPDATE, as for other databases). So ignore it
- // altogether.
- //
- if (id (mi.m) && auto_ (mi.m))
- return false;
-
- string const& name (mi.m.name ());
- member = "o." + name;
-
- os << "// " << name << endl
- << "//" << endl;
-
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk == statement_insert)";
- }
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "get_ref (" + member + ")";
- }
-
- if (composite (mi.t))
- {
- os << "{";
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- }
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;";
-
- if (weak_pointer (mt))
- {
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > wptr_traits;"
- << "typedef pointer_traits< wptr_traits::" <<
- "strong_pointer_type > ptr_traits;"
- << endl
- << "wptr_traits::strong_pointer_type sp (" <<
- "wptr_traits::lock (" << member << "));";
-
- member = "sp";
- }
- else
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl;
-
- os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
- << "if (!is_null)"
- << "{"
- << "const " << type << "& id (" << endl;
-
- if (lazy_pointer (mt))
- os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
- member << ")";
- else
- os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
-
- os << ");"
- << endl;
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- os << "{"
- << "bool is_null;";
- }
-
- traits = "mssql::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
}
virtual void
- post (member_info& mi)
+ set_null (member_info& mi)
{
- if (composite (mi.t))
- os << "}";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- if (object_pointer (member_utype (mi.m, key_prefix_)))
- {
- os << "}"
- << "else" << endl;
-
- if (!null (mi.m, key_prefix_))
- os << "throw null_pointer ();";
- else
- os << "i." << mi.var << "size_ind = SQL_NULL_DATA;";
- }
-
- os << "}";
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << traits << "::init (" << endl
- << "i." << mi.var << "value," << endl
- << member << "," << endl
- << "sk);";
+ os << "i." << mi.var << "size_ind = SQL_NULL_DATA;";
}
virtual void
@@ -800,14 +639,6 @@ namespace relational
<< "i." << mi.var << "value, is_null, " << member << ");"
<< "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;";
}
-
- private:
- string type;
- string db_type_id;
- string member;
- string traits;
-
- member_database_type_id member_database_type_id_;
};
entry<init_image_member> init_image_member_;
@@ -815,147 +646,21 @@ namespace relational
// init value
//
- struct init_value_member: relational::init_value_member, member_base
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
{
init_value_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- string const& name (mi.m.name ());
- member = "o." + name;
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
-
- os << "// " << name << endl
- << "//" << endl;
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "set_ref (\n" + member + ")";
- }
-
- if (composite (mi.t))
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;"
- << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl
- << "if (i." << mi.var << "size_ind == SQL_NULL_DATA)" << endl;
-
- if (null (mi.m, key_prefix_))
- os << member << " = ptr_traits::pointer_type ();";
- else
- os << "throw null_pointer ();";
-
- os << "else"
- << "{"
- << type << " id;";
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
- }
-
- traits = "mssql::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- return;
-
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (object_pointer (mt))
- {
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- member = "o." + mi.m.name ();
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
- }
-
- if (lazy_pointer (mt))
- os << member << " = ptr_traits::pointer_type (db, id);";
- else
- os << "// If a compiler error points to the line below, then" << endl
- << "// it most likely means that a pointer used in a member" << endl
- << "// cannot be initialized from an object pointer." << endl
- << "//" << endl
- << member << " = ptr_traits::pointer_type (" << endl
- << "db.load< obj_traits::object_type > (id));";
-
- os << "}"
- << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ get_null (member_info& mi)
{
- os << traits << "::init (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "db);"
- << endl;
+ os << "i." << mi.var << "size_ind == SQL_NULL_DATA";
}
virtual void
@@ -1141,14 +846,6 @@ namespace relational
<< "i." << mi.var << "size_ind == SQL_NULL_DATA);"
<< endl;
}
-
- private:
- string type;
- string db_type_id;
- string traits;
- string member;
-
- member_database_type_id member_database_type_id_;
};
entry<init_value_member> init_value_member_;
@@ -1172,7 +869,7 @@ namespace relational
size_t n (cols.size ());
for (statement_columns::iterator i (cols.begin ()); n != 0; --n)
{
- sql_type const& st (column_sql_type (*i->member, i->key_prefix));
+ sql_type const& st (parse_sql_type (i->type, *i->member));
bool l (false);
diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx
index c8bad12..b8fa4c3 100644
--- a/odb/relational/mysql/common.cxx
+++ b/odb/relational/mysql/common.cxx
@@ -16,91 +16,10 @@ namespace relational
// member_base
//
- void member_base::
- traverse (semantics::data_member& m)
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
{
- if (transient (m))
- return;
-
- string var;
-
- if (!var_override_.empty ())
- var = var_override_;
- else
- {
- string const& name (m.name ());
- var = name + (name[name.size () - 1] == '_' ? "" : "_");
- }
-
- bool cq (type_override_ != 0 ? false : const_type (m.type ()));
- semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m));
-
- semantics::type* cont;
- if (semantics::class_* c = composite_wrapper (t))
- {
- // If t is a wrapper, pass the wrapped type. Also pass the
- // original, wrapper type.
- //
- member_info mi (m,
- *c,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_composite (mi);
- post (mi);
- }
- }
- // This cannot be a container if we have a type override.
- //
- else if (type_override_ == 0 && (cont = context::container (m)))
- {
- // The same unwrapping logic as for composite values.
- //
- member_info mi (m,
- *cont,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_container (mi);
- post (mi);
- }
- }
- else
- {
- sql_type const& st (column_sql_type (m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (t))
- {
- member_info mi (m,
- utype (*id_member (*c)),
- 0,
- cq,
- var,
- fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_object_pointer (mi);
- post (mi);
- }
- }
- else
- {
- member_info mi (m, t, 0, cq, var, fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_simple (mi);
- post (mi);
- }
- }
- }
+ return parse_sql_type (column_type (m, key_prefix_), m);
}
void member_base::
@@ -363,10 +282,18 @@ namespace relational
};
member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_database_type_id::
member_database_type_id (semantics::type* type,
- string const& fq_type,
- string const& key_prefix)
- : relational::member_base (type, fq_type, key_prefix)
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, fq_type, key_prefix), // virtual base
+ base (type, fq_type, key_prefix)
{
}
@@ -437,6 +364,8 @@ namespace relational
type_id_ = "mysql::id_set";
}
+ entry<member_database_type_id> member_database_type_id_;
+
//
// query_columns
//
diff --git a/odb/relational/mysql/common.hxx b/odb/relational/mysql/common.hxx
index c49bd09..93f5fbd 100644
--- a/odb/relational/mysql/common.hxx
+++ b/odb/relational/mysql/common.hxx
@@ -12,118 +12,18 @@ namespace relational
{
namespace mysql
{
- struct member_base: virtual relational::member_base, context
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
{
- member_base (base const& x): base (x) {}
+ member_base (base const& x): base (x), base_impl (x) {}
// This c-tor is for the direct use inside the mysql namespace.
// If you do use this c-tor, you should also explicitly call
- // relational::member_base.
+ // relational::member_base (aka base).
//
member_base () {}
- virtual void
- traverse (semantics::data_member& m);
-
- struct member_info
- {
- semantics::data_member& m; // Member.
- semantics::type& t; // Cvr-unqualified member C++ type, note
- // that m.type () may not be the same as t.
- semantics::type* wrapper; // Wrapper type if member is a composite or
- // container wrapper, also cvr-unqualified.
- // In this case t is the wrapped type.
- bool cq; // True if the original (wrapper) type
- // is const-qualified.
- sql_type const* st; // Member SQL type (only simple values).
- string& var; // Member variable name with trailing '_'.
-
- // C++ type fq-name.
- //
- string
- fq_type (bool unwrap = true) const
- {
- semantics::names* hint;
-
- if (wrapper != 0 && unwrap)
- {
- // Use the hint from the wrapper unless the wrapped type
- // is qualified.
- //
- hint = wrapper->get<semantics::names*> ("wrapper-hint");
- utype (*context::wrapper (*wrapper), hint);
- return t.fq_name (hint);
- }
-
- // Use the original type from 'm' instead of 't' since the hint
- // may be invalid for a different type. Plus, if a type is
- // overriden, then the fq_type must be as well.
- //
- if (fq_type_.empty ())
- {
- semantics::type& t (utype (m, hint));
- return t.fq_name (hint);
- }
- else
- return fq_type_;
- }
-
- string const& fq_type_;
-
- member_info (semantics::data_member& m_,
- semantics::type& t_,
- semantics::type* wrapper_,
- bool cq_,
- string& var_,
- string const& fq_type)
- : m (m_),
- t (t_),
- wrapper (wrapper_),
- cq (cq_),
- st (0),
- var (var_),
- fq_type_ (fq_type)
- {
- }
- };
-
- bool
- container (member_info& mi)
- {
- // This cannot be a container if we have a type override.
- //
- return type_override_ == 0 && context::container (mi.m);
- }
-
- // The false return value indicates that no further callbacks
- // should be called for this member.
- //
- virtual bool
- pre (member_info&)
- {
- return true;
- }
-
- virtual void
- post (member_info&)
- {
- }
-
- virtual void
- traverse_composite (member_info&)
- {
- }
-
- virtual void
- traverse_container (member_info&)
- {
- }
-
- virtual void
- traverse_object_pointer (member_info& mi)
- {
- traverse_simple (mi);
- }
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
virtual void
traverse_simple (member_info&);
@@ -220,12 +120,16 @@ namespace relational
string type_;
};
- struct member_database_type_id: member_base
+ struct member_database_type_id: relational::member_database_type_id,
+ 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 ());
- string
+
+ virtual string
database_type_id (type&);
virtual void
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx
index b8327eb..ad7236c 100644
--- a/odb/relational/mysql/context.cxx
+++ b/odb/relational/mysql/context.cxx
@@ -316,17 +316,20 @@ namespace relational
//
sql_type const& context::
- column_sql_type (semantics::data_member& m, string const& kp)
+ parse_sql_type (string const& t, semantics::data_member& m)
{
- string key (kp.empty ()
- ? string ("mysql-column-sql-type")
- : "mysql-" + kp + "-column-sql-type");
+ // If this proves to be too expensive, we can maintain a
+ // cache of parsed types.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
- if (!m.count (key))
+ if (i != data_->sql_type_cache_.end ())
+ return i->second;
+ else
{
try
{
- m.set (key, parse_sql_type (column_type (m, kp)));
+ return (data_->sql_type_cache_[t] = parse_sql_type (t));
}
catch (invalid_sql_type const& e)
{
@@ -336,8 +339,6 @@ namespace relational
throw operation_failed ();
}
}
-
- return m.get<sql_type> (key);
}
sql_type context::
diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx
index 49b825e..294b5d1 100644
--- a/odb/relational/mysql/context.hxx
+++ b/odb/relational/mysql/context.hxx
@@ -5,6 +5,7 @@
#ifndef ODB_RELATIONAL_MYSQL_CONTEXT_HXX
#define ODB_RELATIONAL_MYSQL_CONTEXT_HXX
+#include <map>
#include <vector>
#include <odb/relational/context.hxx>
@@ -80,8 +81,7 @@ namespace relational
{
public:
sql_type const&
- column_sql_type (semantics::data_member&,
- string const& key_prefix = string ());
+ parse_sql_type (string const&, semantics::data_member&);
public:
struct invalid_sql_type
@@ -138,6 +138,9 @@ namespace relational
struct data: base_context::data
{
data (std::ostream& os): base_context::data (os) {}
+
+ typedef std::map<string, sql_type> sql_type_cache;
+ sql_type_cache sql_type_cache_;
};
data* data_;
};
diff --git a/odb/relational/mysql/model.cxx b/odb/relational/mysql/model.cxx
index 8e07f38..11ca157 100644
--- a/odb/relational/mysql/model.cxx
+++ b/odb/relational/mysql/model.cxx
@@ -35,7 +35,7 @@ namespace relational
{
// Make sure the column is mapped to an ENUM or integer type.
//
- sql_type const& t (column_sql_type (m));
+ sql_type const& t (parse_sql_type (column_type (), m));
switch (t.type)
{
diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx
index 91b3061..3a784af 100644
--- a/odb/relational/mysql/source.cxx
+++ b/odb/relational/mysql/source.cxx
@@ -68,7 +68,6 @@ namespace relational
virtual void
column (semantics::data_member& m,
- string const& key_prefix,
string const& table,
string const& column)
{
@@ -110,10 +109,12 @@ namespace relational
// to value_traits.
//
+ string type (column_type ());
+
if (sk_ != statement_select ||
- column_sql_type (m, key_prefix).type != sql_type::ENUM)
+ parse_sql_type (type, m).type != sql_type::ENUM)
{
- base::column (m, key_prefix, table, column);
+ base::column (m, table, column);
return;
}
@@ -140,7 +141,8 @@ namespace relational
r += ")";
- sc_.push_back (relational::statement_column (r, m, key_prefix));
+ sc_.push_back (
+ relational::statement_column (r, type, m, key_prefix_));
}
};
entry<object_columns> object_columns_;
@@ -154,7 +156,9 @@ namespace relational
{
// The same idea as in object_columns.
//
- if (column_sql_type (m).type != sql_type::ENUM)
+ string type (column_type ());
+
+ if (parse_sql_type (type, m).type != sql_type::ENUM)
{
base::column (m, column);
return;
@@ -168,7 +172,7 @@ namespace relational
r += column;
r += ")";
- sc_.push_back (relational::statement_column (r, m));
+ sc_.push_back (relational::statement_column (r, type, m));
}
};
entry<view_columns> view_columns_;
@@ -568,188 +572,37 @@ namespace relational
// init image
//
- struct init_image_member: relational::init_image_member, member_base
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
{
init_image_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
}
- virtual bool
- pre (member_info& mi)
- {
- // Ignore containers (they get their own table) and inverse
- // object pointers (they are not present in this binding).
- //
- if (container (mi) || inverse (mi.m, key_prefix_))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- // If we are generating standard init() and this member
- // contains version, ignore it.
- //
- if (version (mi.m))
- return false;
-
- string const& name (mi.m.name ());
- member = "o." + name;
-
- os << "// " << name << endl
- << "//" << endl;
-
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk == statement_insert)";
- }
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "get_ref (" + member + ")";
- }
-
- if (composite (mi.t))
- {
- os << "{";
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- }
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;";
-
- if (weak_pointer (mt))
- {
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > wptr_traits;"
- << "typedef pointer_traits< wptr_traits::" <<
- "strong_pointer_type > ptr_traits;"
- << endl
- << "wptr_traits::strong_pointer_type sp (" <<
- "wptr_traits::lock (" << member << "));";
-
- member = "sp";
- }
- else
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl;
-
- os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
- << "if (!is_null)"
- << "{"
- << "const " << type << "& id (" << endl;
-
- if (lazy_pointer (mt))
- os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
- member << ")";
- else
- os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
-
- os << ");"
- << endl;
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- os << "{"
- << "bool is_null;";
- }
-
- traits = "mysql::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
virtual void
- post (member_info& mi)
+ set_null (member_info& mi)
{
- if (composite (mi.t))
- os << "}";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- if (object_pointer (member_utype (mi.m, key_prefix_)))
- {
- os << "}";
-
- if (!null (mi.m, key_prefix_))
- os << "else" << endl
- << "throw null_pointer ();";
- }
-
- os << "i." << mi.var << "null = is_null;"
- << "}";
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << "if (" << traits << "::init (" << endl
- << "i." << mi.var << "value," << endl
- << member << "," << endl
- << "sk))" << endl
- << "grew = true;";
+ os << "i." << mi.var << "null = 1;";
}
virtual void
traverse_integer (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
traverse_float (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
@@ -764,6 +617,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = static_cast<unsigned long> (size);"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
@@ -772,7 +626,8 @@ namespace relational
traverse_date_time (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
@@ -787,6 +642,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = static_cast<unsigned long> (size);"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
@@ -801,6 +657,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = static_cast<unsigned long> (size);"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
@@ -817,6 +674,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = static_cast<unsigned long> (size);";
}
@@ -830,7 +688,9 @@ namespace relational
<< "i." << mi.var << "size," << endl
<< "is_null," << endl
<< member << "))" << endl
- << "grew = true;";
+ << "grew = true;"
+ << endl
+ << "i." << mi.var << "null = is_null;";
}
virtual void
@@ -845,17 +705,10 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = static_cast<unsigned long> (size);"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
-
- private:
- string type;
- string db_type_id;
- string member;
- string traits;
-
- member_database_type_id member_database_type_id_;
};
entry<init_image_member> init_image_member_;
@@ -863,147 +716,21 @@ namespace relational
// init value
//
- struct init_value_member: relational::init_value_member, member_base
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
{
init_value_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- string const& name (mi.m.name ());
- member = "o." + name;
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
-
- os << "// " << name << endl
- << "//" << endl;
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "set_ref (\n" + member + ")";
- }
-
- if (composite (mi.t))
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;"
- << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl
- << "if (i." << mi.var << "null)" << endl;
-
- if (null (mi.m, key_prefix_))
- os << member << " = ptr_traits::pointer_type ();";
- else
- os << "throw null_pointer ();";
-
- os << "else"
- << "{"
- << type << " id;";
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
- }
-
- traits = "mysql::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- return;
-
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (object_pointer (mt))
- {
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- member = "o." + mi.m.name ();
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
- }
-
- if (lazy_pointer (mt))
- os << member << " = ptr_traits::pointer_type (db, id);";
- else
- os << "// If a compiler error points to the line below, then" << endl
- << "// it most likely means that a pointer used in a member" << endl
- << "// cannot be initialized from an object pointer." << endl
- << "//" << endl
- << member << " = ptr_traits::pointer_type (" << endl
- << "db.load< obj_traits::object_type > (id));";
-
- os << "}"
- << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ get_null (member_info& mi)
{
- os << traits << "::init (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "db);"
- << endl;
+ os << "i." << mi.var << "null";
}
virtual void
@@ -1107,14 +834,6 @@ namespace relational
<< "i." << mi.var << "null);"
<< endl;
}
-
- private:
- string type;
- string db_type_id;
- string traits;
- string member;
-
- member_database_type_id member_database_type_id_;
};
entry<init_value_member> init_value_member_;
diff --git a/odb/relational/oracle/common.cxx b/odb/relational/oracle/common.cxx
index 88183cf..661bb4e 100644
--- a/odb/relational/oracle/common.cxx
+++ b/odb/relational/oracle/common.cxx
@@ -16,91 +16,10 @@ namespace relational
// member_base
//
- void member_base::
- traverse (semantics::data_member& m)
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
{
- if (transient (m))
- return;
-
- string var;
-
- if (!var_override_.empty ())
- var = var_override_;
- else
- {
- string const& name (m.name ());
- var = name + (name[name.size () - 1] == '_' ? "" : "_");
- }
-
- bool cq (type_override_ != 0 ? false : const_type (m.type ()));
- semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m));
-
- semantics::type* cont;
- if (semantics::class_* c = composite_wrapper (t))
- {
- // If t is a wrapper, pass the wrapped type. Also pass the
- // original, wrapper type.
- //
- member_info mi (m,
- *c,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_composite (mi);
- post (mi);
- }
- }
- // This cannot be a container if we have a type override.
- //
- else if (type_override_ == 0 && (cont = context::container (m)))
- {
- // The same unwrapping logic as for composite values.
- //
- member_info mi (m,
- *cont,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_container (mi);
- post (mi);
- }
- }
- else
- {
- sql_type const& st (column_sql_type (m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (t))
- {
- member_info mi (m,
- utype (*id_member (*c)),
- 0,
- cq,
- var,
- fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_object_pointer (mi);
- post (mi);
- }
- }
- else
- {
- member_info mi (m, t, 0, cq, var, fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_simple (mi);
- post (mi);
- }
- }
- }
+ return parse_sql_type (column_type (m, key_prefix_), m);
}
void member_base::
@@ -374,10 +293,18 @@ namespace relational
}
member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_database_type_id::
member_database_type_id (semantics::type* type,
string const& fq_type,
string const& key_prefix)
- : relational::member_base (type, fq_type, key_prefix)
+ : member_base::base (type, fq_type, key_prefix), // virtual base
+ base (type, fq_type, key_prefix)
{
}
@@ -469,6 +396,8 @@ namespace relational
lob_database_id[mi.st->type - sql_type::BLOB];
}
+ entry<member_database_type_id> member_database_type_id_;
+
//
// query_columns
//
@@ -494,7 +423,7 @@ namespace relational
{
// For some types we need to pass precision and scale.
//
- sql_type const& st (column_sql_type (m));
+ sql_type const& st (parse_sql_type (column_type (), m));
switch (st.type)
{
diff --git a/odb/relational/oracle/common.hxx b/odb/relational/oracle/common.hxx
index 0f4517d..15120fd 100644
--- a/odb/relational/oracle/common.hxx
+++ b/odb/relational/oracle/common.hxx
@@ -12,118 +12,18 @@ namespace relational
{
namespace oracle
{
- struct member_base: virtual relational::member_base, context
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
{
- member_base (base const& x): base (x) {}
+ member_base (base const& x): base (x), base_impl (x) {}
// This c-tor is for the direct use inside the oracle namespace.
// If you do use this c-tor, you should also explicitly call
- // relational::member_base.
+ // relational::member_base (aka base).
//
member_base () {}
- virtual void
- traverse (semantics::data_member& m);
-
- struct member_info
- {
- semantics::data_member& m; // Member.
- semantics::type& t; // Cvr-unqualified member C++ type, note
- // that m.type () may not be the same as t.
- semantics::type* wrapper; // Wrapper type if member is a composite or
- // container wrapper, also cvr-unqualified.
- // In this case t is the wrapped type.
- bool cq; // True if the original (wrapper) type
- // is const-qualified.
- sql_type const* st; // Member SQL type (only simple values).
- string& var; // Member variable name with trailing '_'.
-
- // C++ type fq-name.
- //
- string
- fq_type (bool unwrap = true) const
- {
- semantics::names* hint;
-
- if (wrapper != 0 && unwrap)
- {
- // Use the hint from the wrapper unless the wrapped type
- // is qualified.
- //
- hint = wrapper->get<semantics::names*> ("wrapper-hint");
- utype (*context::wrapper (*wrapper), hint);
- return t.fq_name (hint);
- }
-
- // Use the original type from 'm' instead of 't' since the hint
- // may be invalid for a different type. Plus, if a type is
- // overriden, then the fq_type must be as well.
- //
- if (fq_type_.empty ())
- {
- semantics::type& t (utype (m, hint));
- return t.fq_name (hint);
- }
- else
- return fq_type_;
- }
-
- string const& fq_type_;
-
- member_info (semantics::data_member& m_,
- semantics::type& t_,
- semantics::type* wrapper_,
- bool cq_,
- string& var_,
- string const& fq_type)
- : m (m_),
- t (t_),
- wrapper (wrapper_),
- cq (cq_),
- st (0),
- var (var_),
- fq_type_ (fq_type)
- {
- }
- };
-
- bool
- container (member_info& mi)
- {
- // This cannot be a container if we have a type override.
- //
- return type_override_ == 0 && context::container (mi.m);
- }
-
- // The false return value indicates that no further callbacks
- // should be called for this member.
- //
- virtual bool
- pre (member_info&)
- {
- return true;
- }
-
- virtual void
- post (member_info&)
- {
- }
-
- virtual void
- traverse_composite (member_info&)
- {
- }
-
- virtual void
- traverse_container (member_info&)
- {
- }
-
- virtual void
- traverse_object_pointer (member_info& mi)
- {
- traverse_simple (mi);
- }
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
virtual void
traverse_simple (member_info&);
@@ -240,12 +140,16 @@ namespace relational
string type_;
};
- struct member_database_type_id: member_base
+ struct member_database_type_id: relational::member_database_type_id,
+ 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 ());
- string
+
+ virtual string
database_type_id (type&);
virtual void
diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx
index 3d87729..dbe61af 100644
--- a/odb/relational/oracle/context.cxx
+++ b/odb/relational/oracle/context.cxx
@@ -159,17 +159,20 @@ namespace relational
//
sql_type const& context::
- column_sql_type (semantics::data_member& m, string const& kp)
+ parse_sql_type (string const& t, semantics::data_member& m)
{
- string key (kp.empty ()
- ? string ("oracle-column-sql-type")
- : "oracle-" + kp + "-column-sql-type");
+ // If this proves to be too expensive, we can maintain a
+ // cache of parsed types.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
- if (!m.count (key))
+ if (i != data_->sql_type_cache_.end ())
+ return i->second;
+ else
{
try
{
- m.set (key, parse_sql_type (column_type (m, kp)));
+ return (data_->sql_type_cache_[t] = parse_sql_type (t));
}
catch (invalid_sql_type const& e)
{
@@ -179,8 +182,6 @@ namespace relational
throw operation_failed ();
}
}
-
- return m.get<sql_type> (key);
}
sql_type context::
diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx
index 273f0e5..37716f0 100644
--- a/odb/relational/oracle/context.hxx
+++ b/odb/relational/oracle/context.hxx
@@ -5,6 +5,8 @@
#ifndef ODB_RELATIONAL_ORACLE_CONTEXT_HXX
#define ODB_RELATIONAL_ORACLE_CONTEXT_HXX
+#include <map>
+
#include <odb/relational/context.hxx>
namespace relational
@@ -73,8 +75,7 @@ namespace relational
{
public:
sql_type const&
- column_sql_type (semantics::data_member&,
- string const& key_prefix = string ());
+ parse_sql_type (string const&, semantics::data_member&);
public:
struct invalid_sql_type
@@ -126,6 +127,9 @@ namespace relational
struct data: base_context::data
{
data (std::ostream& os): base_context::data (os) {}
+
+ typedef std::map<string, sql_type> sql_type_cache;
+ sql_type_cache sql_type_cache_;
};
data* data_;
};
diff --git a/odb/relational/oracle/model.cxx b/odb/relational/oracle/model.cxx
index 347ea37..4b874c8 100644
--- a/odb/relational/oracle/model.cxx
+++ b/odb/relational/oracle/model.cxx
@@ -28,9 +28,7 @@ namespace relational
{
// Make sure the column is mapped to Oracle NUMBER.
//
- sql_type t (column_sql_type (m));
-
- if (t.type != sql_type::NUMBER)
+ if (parse_sql_type (column_type (), m).type != sql_type::NUMBER)
{
cerr << m.file () << ":" << m.line () << ":" << m.column ()
<< ": error: column with default value specified as C++ "
diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx
index fb0984a..1d7d81f 100644
--- a/odb/relational/oracle/source.cxx
+++ b/odb/relational/oracle/source.cxx
@@ -316,187 +316,37 @@ namespace relational
// init image
//
- struct init_image_member: relational::init_image_member, member_base
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
{
init_image_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- // Ignore containers (they get their own table) and inverse
- // object pointers (they are not present in this binding).
- //
- if (container (mi) || inverse (mi.m, key_prefix_))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- // If we are generating standard init() and this member
- // contains version, ignore it.
- //
- if (version (mi.m))
- return false;
-
- string const& name (mi.m.name ());
- member = "o." + name;
-
- os << "// " << name << endl
- << "//" << endl;
-
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk == statement_insert)";
- }
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "get_ref (" + member + ")";
- }
-
- if (composite (mi.t))
- {
- os << "{";
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- }
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;";
-
- if (weak_pointer (mt))
- {
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > wptr_traits;"
- << "typedef pointer_traits< wptr_traits::" <<
- "strong_pointer_type > ptr_traits;"
- << endl
- << "wptr_traits::strong_pointer_type sp (" <<
- "wptr_traits::lock (" << member << "));";
-
- member = "sp";
- }
- else
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl;
-
- os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
- << "if (!is_null)"
- << "{"
- << "const " << type << "& id (" << endl;
-
- if (lazy_pointer (mt))
- os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
- member << ")";
- else
- os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
-
- os << ");"
- << endl;
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- os << "{"
- << "bool is_null;";
- }
-
- traits = "oracle::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- os << "}";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- if (object_pointer (member_utype (mi.m, key_prefix_)))
- {
- os << "}";
-
- if (!null (mi.m, key_prefix_))
- os << "else" << endl
- << "throw null_pointer ();";
- }
-
- os << "i." << mi.var << "indicator = is_null ? -1 : 0;"
- << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ set_null (member_info& mi)
{
- os << traits << "::init (" << endl
- << "i." << mi.var << "value," << endl
- << member << "," << endl
- << "sk);";
+ os << "i." << mi.var << "indicator = -1;";
}
virtual void
traverse_int32 (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
traverse_int64 (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
@@ -508,6 +358,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
<< "i." << mi.var << "size = static_cast<ub2> (size);";
}
@@ -515,14 +366,16 @@ namespace relational
traverse_float (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
traverse_double (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
@@ -535,6 +388,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
<< "i." << mi.var << "size = static_cast<ub2> (size);";
}
@@ -542,28 +396,32 @@ namespace relational
traverse_date (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
traverse_timestamp (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
traverse_interval_ym (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
traverse_interval_ds (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
virtual void
@@ -576,6 +434,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
<< "i." << mi.var << "size = static_cast<ub2> (size);";
}
@@ -587,16 +446,9 @@ namespace relational
<< "i." << mi.var << "callback.callback.param," << endl
<< "i." << mi.var << "callback.context.param," << endl
<< "is_null," << endl
- << member << ");";
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
}
-
- private:
- string type;
- string db_type_id;
- string member;
- string traits;
-
- member_database_type_id member_database_type_id_;
};
entry<init_image_member> init_image_member_;
@@ -604,147 +456,21 @@ namespace relational
// init value
//
- struct init_value_member: relational::init_value_member, member_base
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
{
init_value_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- string const& name (mi.m.name ());
- member = "o." + name;
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
-
- os << "// " << name << endl
- << "//" << endl;
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "set_ref (\n" + member + ")";
- }
-
- if (composite (mi.t))
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;"
- << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl
- << "if (i." << mi.var << "indicator == -1)" << endl;
-
- if (null (mi.m, key_prefix_))
- os << member << " = ptr_traits::pointer_type ();";
- else
- os << "throw null_pointer ();";
-
- os << "else"
- << "{"
- << type << " id;";
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
- }
-
- traits = "oracle::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- return;
-
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (object_pointer (mt))
- {
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- member = "o." + mi.m.name ();
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
- }
-
- if (lazy_pointer (mt))
- os << member << " = ptr_traits::pointer_type (db, id);";
- else
- os << "// If a compiler error points to the line below, then" << endl
- << "// it most likely means that a pointer used in a member" << endl
- << "// cannot be initialized from an object pointer." << endl
- << "//" << endl
- << member << " = ptr_traits::pointer_type (" << endl
- << "db.load< obj_traits::object_type > (id));";
-
- os << "}"
- << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ get_null (member_info& mi)
{
- os << traits << "::init (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "db);"
- << endl;
+ os << "i." << mi.var << "indicator == -1";
}
virtual void
@@ -870,14 +596,6 @@ namespace relational
<< "i." << mi.var << "indicator == -1);"
<< endl;
}
-
- private:
- string type;
- string db_type_id;
- string traits;
- string member;
-
- member_database_type_id member_database_type_id_;
};
entry<init_value_member> init_value_member_;
diff --git a/odb/relational/pgsql/common.cxx b/odb/relational/pgsql/common.cxx
index 0ec9f7c..3d7e01e 100644
--- a/odb/relational/pgsql/common.cxx
+++ b/odb/relational/pgsql/common.cxx
@@ -16,91 +16,10 @@ namespace relational
// member_base
//
- void member_base::
- traverse (semantics::data_member& m)
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
{
- if (transient (m))
- return;
-
- string var;
-
- if (!var_override_.empty ())
- var = var_override_;
- else
- {
- string const& name (m.name ());
- var = name + (name[name.size () - 1] == '_' ? "" : "_");
- }
-
- bool cq (type_override_ != 0 ? false : const_type (m.type ()));
- semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m));
-
- semantics::type* cont;
- if (semantics::class_* c = composite_wrapper (t))
- {
- // If t is a wrapper, pass the wrapped type. Also pass the
- // original, wrapper type.
- //
- member_info mi (m,
- *c,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_composite (mi);
- post (mi);
- }
- }
- // This cannot be a container if we have a type override.
- //
- else if (type_override_ == 0 && (cont = context::container (m)))
- {
- // The same unwrapping logic as for composite values.
- //
- member_info mi (m,
- *cont,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_container (mi);
- post (mi);
- }
- }
- else
- {
- sql_type const& st (column_sql_type (m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (t))
- {
- member_info mi (m,
- utype (*id_member (*c)),
- 0,
- cq,
- var,
- fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_object_pointer (mi);
- post (mi);
- }
- }
- else
- {
- member_info mi (m, t, 0, cq, var, fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_simple (mi);
- post (mi);
- }
- }
- }
+ return parse_sql_type (column_type (m, key_prefix_), m);
}
void member_base::
@@ -310,13 +229,21 @@ namespace relational
"id_string", // TEXT,
"id_bytea" // BYTEA
};
- }
+ }
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
member_database_type_id::
member_database_type_id (semantics::type* type,
string const& fq_type,
string const& key_prefix)
- : relational::member_base (type, fq_type, key_prefix)
+ : member_base::base (type, fq_type, key_prefix), // virtual base
+ base (type, fq_type, key_prefix)
{
}
@@ -386,6 +313,8 @@ namespace relational
type_id_ = "pgsql::id_uuid";
}
+ entry<member_database_type_id> member_database_type_id_;
+
//
// query_columns
//
diff --git a/odb/relational/pgsql/common.hxx b/odb/relational/pgsql/common.hxx
index 4f3ee4d..4b9bbd5 100644
--- a/odb/relational/pgsql/common.hxx
+++ b/odb/relational/pgsql/common.hxx
@@ -12,118 +12,18 @@ namespace relational
{
namespace pgsql
{
- struct member_base: virtual relational::member_base, context
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
{
- member_base (base const& x): base (x) {}
+ member_base (base const& x): base (x), base_impl (x) {}
// This c-tor is for the direct use inside the pgsql namespace.
// If you do use this c-tor, you should also explicitly call
- // relational::member_base.
+ // relational::member_base (aka base).
//
member_base () {}
- virtual void
- traverse (semantics::data_member& m);
-
- struct member_info
- {
- semantics::data_member& m; // Member.
- semantics::type& t; // Cvr-unqualified member C++ type, note
- // that m.type () may not be the same as t.
- semantics::type* wrapper; // Wrapper type if member is a composite or
- // container wrapper, also cvr-unqualified.
- // In this case t is the wrapped type.
- bool cq; // True if the original (wrapper) type
- // is const-qualified.
- sql_type const* st; // Member SQL type (only simple values).
- string& var; // Member variable name with trailing '_'.
-
- // C++ type fq-name.
- //
- string
- fq_type (bool unwrap = true) const
- {
- semantics::names* hint;
-
- if (wrapper != 0 && unwrap)
- {
- // Use the hint from the wrapper unless the wrapped type
- // is qualified.
- //
- hint = wrapper->get<semantics::names*> ("wrapper-hint");
- utype (*context::wrapper (*wrapper), hint);
- return t.fq_name (hint);
- }
-
- // Use the original type from 'm' instead of 't' since the hint
- // may be invalid for a different type. Plus, if a type is
- // overriden, then the fq_type must be as well.
- //
- if (fq_type_.empty ())
- {
- semantics::type& t (utype (m, hint));
- return t.fq_name (hint);
- }
- else
- return fq_type_;
- }
-
- string const& fq_type_;
-
- member_info (semantics::data_member& m_,
- semantics::type& t_,
- semantics::type* wrapper_,
- bool cq_,
- string& var_,
- string const& fq_type)
- : m (m_),
- t (t_),
- wrapper (wrapper_),
- cq (cq_),
- st (0),
- var (var_),
- fq_type_ (fq_type)
- {
- }
- };
-
- bool
- container (member_info& mi)
- {
- // This cannot be a container if we have a type override.
- //
- return type_override_ == 0 && context::container (mi.m);
- }
-
- // The false return value indicates that no further callbacks
- // should be called for this member.
- //
- virtual bool
- pre (member_info&)
- {
- return true;
- }
-
- virtual void
- post (member_info&)
- {
- }
-
- virtual void
- traverse_composite (member_info&)
- {
- }
-
- virtual void
- traverse_container (member_info&)
- {
- }
-
- virtual void
- traverse_object_pointer (member_info& mi)
- {
- traverse_simple (mi);
- }
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
virtual void
traverse_simple (member_info&);
@@ -208,12 +108,16 @@ namespace relational
string type_;
};
- struct member_database_type_id: member_base
+ struct member_database_type_id: relational::member_database_type_id,
+ 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 ());
- string
+
+ virtual string
database_type_id (type&);
virtual void
diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx
index 8cc4eca..17ceaaa 100644
--- a/odb/relational/pgsql/context.cxx
+++ b/odb/relational/pgsql/context.cxx
@@ -236,17 +236,20 @@ namespace relational
//
sql_type const& context::
- column_sql_type (semantics::data_member& m, string const& kp)
+ parse_sql_type (string const& t, semantics::data_member& m)
{
- string key (kp.empty ()
- ? string ("pgsql-column-sql-type")
- : "pgsql-" + kp + "-column-sql-type");
+ // If this proves to be too expensive, we can maintain a
+ // cache of parsed types.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
- if (!m.count (key))
+ if (i != data_->sql_type_cache_.end ())
+ return i->second;
+ else
{
try
{
- m.set (key, parse_sql_type (column_type (m, kp)));
+ return (data_->sql_type_cache_[t] = parse_sql_type (t));
}
catch (invalid_sql_type const& e)
{
@@ -256,8 +259,6 @@ namespace relational
throw operation_failed ();
}
}
-
- return m.get<sql_type> (key);
}
sql_type context::
diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx
index a79cceb..1da353a 100644
--- a/odb/relational/pgsql/context.hxx
+++ b/odb/relational/pgsql/context.hxx
@@ -5,6 +5,8 @@
#ifndef ODB_RELATIONAL_PGSQL_CONTEXT_HXX
#define ODB_RELATIONAL_PGSQL_CONTEXT_HXX
+#include <map>
+
#include <odb/relational/context.hxx>
namespace relational
@@ -69,8 +71,7 @@ namespace relational
{
public:
sql_type const&
- column_sql_type (semantics::data_member&,
- string const& key_prefix = string ());
+ parse_sql_type (string const&, semantics::data_member&);
public:
struct invalid_sql_type
@@ -124,6 +125,9 @@ namespace relational
struct data: base_context::data
{
data (std::ostream& os): base_context::data (os) {}
+
+ typedef std::map<string, sql_type> sql_type_cache;
+ sql_type_cache sql_type_cache_;
};
data* data_;
};
diff --git a/odb/relational/pgsql/model.cxx b/odb/relational/pgsql/model.cxx
index ebd04af..06a7dde 100644
--- a/odb/relational/pgsql/model.cxx
+++ b/odb/relational/pgsql/model.cxx
@@ -34,7 +34,7 @@ namespace relational
{
// Make sure the column is mapped to an integer type.
//
- switch (column_sql_type (m).type)
+ switch (parse_sql_type (column_type (), m).type)
{
case sql_type::SMALLINT:
case sql_type::INTEGER:
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
index ceb7dbd..ae19e05 100644
--- a/odb/relational/pgsql/source.cxx
+++ b/odb/relational/pgsql/source.cxx
@@ -103,7 +103,21 @@ namespace relational
struct statement_oids: object_columns_base, context
{
- statement_oids (statement_kind sk): sk_ (sk) {}
+ statement_oids (statement_kind sk, bool first = true)
+ : object_columns_base (first), sk_ (sk)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (!(inverse (m, key_prefix_) && sk_ != statement_select))
+ object_columns_base::traverse_pointer (m, c);
+ }
virtual bool
traverse_column (semantics::data_member& m,
@@ -114,10 +128,7 @@ namespace relational
// generating. See object_columns in common source generator for
// details.
//
- if (inverse (m) && sk_ != statement_select)
- return false;
-
- if ((id (m) || readonly (member_path_, member_scope_)) &&
+ if ((id () || readonly (member_path_, member_scope_)) &&
sk_ == statement_update)
return false;
@@ -128,7 +139,7 @@ namespace relational
if (!first)
os << ',' << endl;
- os << oids[column_sql_type (m).type];
+ os << oids[parse_sql_type (column_type (), m).type];
return true;
}
@@ -471,188 +482,37 @@ namespace relational
// init image
//
- struct init_image_member: relational::init_image_member, member_base
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
{
init_image_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- // Ignore containers (they get their own table) and inverse
- // object pointers (they are not present in this binding).
- //
- if (container (mi) || inverse (mi.m, key_prefix_))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- // If we are generating standard init() and this member
- // contains version, ignore it.
- //
- if (version (mi.m))
- return false;
-
- string const& name (mi.m.name ());
- member = "o." + name;
-
- os << "// " << name << endl
- << "//" << endl;
-
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk == statement_insert)";
- }
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "get_ref (" + member + ")";
- }
-
- if (composite (mi.t))
- {
- os << "{";
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- }
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;";
-
- if (weak_pointer (mt))
- {
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > wptr_traits;"
- << "typedef pointer_traits< wptr_traits::" <<
- "strong_pointer_type > ptr_traits;"
- << endl
- << "wptr_traits::strong_pointer_type sp (" <<
- "wptr_traits::lock (" << member << "));";
-
- member = "sp";
- }
- else
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl;
-
- os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
- << "if (!is_null)"
- << "{"
- << "const " << type << "& id (" << endl;
-
- if (lazy_pointer (mt))
- os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
- member << ")";
- else
- os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
-
- os << ");"
- << endl;
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- os << "{"
- << "bool is_null;";
- }
-
- traits = "pgsql::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- os << "}";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- if (object_pointer (member_utype (mi.m, key_prefix_)))
- {
- os << "}";
-
- if (!null (mi.m, key_prefix_))
- os << "else" << endl
- << "throw null_pointer ();";
- }
-
- os << "i." << mi.var << "null = is_null;"
- << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ set_null (member_info& mi)
{
- os << "if (" << traits << "::init (" << endl
- << "i." << mi.var << "value," << endl
- << member << "," << endl
- << "sk))" << endl
- << "grew = true;";
+ os << "i." << mi.var << "null = true;";
}
virtual void
traverse_integer (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
traverse_float (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
@@ -667,6 +527,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = size;"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
@@ -675,7 +536,8 @@ namespace relational
traverse_date_time (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
@@ -688,6 +550,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = size;"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
@@ -702,6 +565,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = size;";
}
@@ -715,6 +579,7 @@ namespace relational
<< "size," << endl
<< "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "i." << mi.var << "size = size;"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
@@ -723,16 +588,9 @@ namespace relational
traverse_uuid (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
-
- private:
- string type;
- string db_type_id;
- string member;
- string traits;
-
- member_database_type_id member_database_type_id_;
};
entry<init_image_member> init_image_member_;
@@ -740,147 +598,21 @@ namespace relational
// init value
//
- struct init_value_member: relational::init_value_member, member_base
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
{
init_value_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- string const& name (mi.m.name ());
- member = "o." + name;
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
-
- os << "// " << name << endl
- << "//" << endl;
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "set_ref (\n" + member + ")";
- }
-
- if (composite (mi.t))
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;"
- << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl
- << "if (i." << mi.var << "null)" << endl;
-
- if (null (mi.m, key_prefix_))
- os << member << " = ptr_traits::pointer_type ();";
- else
- os << "throw null_pointer ();";
-
- os << "else"
- << "{"
- << type << " id;";
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
- }
-
- traits = "pgsql::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- return;
-
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (object_pointer (mt))
- {
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- member = "o." + mi.m.name ();
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
- }
-
- if (lazy_pointer (mt))
- os << member << " = ptr_traits::pointer_type (db, id);";
- else
- os << "// If a compiler error points to the line below, then" << endl
- << "// it most likely means that a pointer used in a member" << endl
- << "// cannot be initialized from an object pointer." << endl
- << "//" << endl
- << member << " = ptr_traits::pointer_type (" << endl
- << "db.load< obj_traits::object_type > (id));";
-
- os << "}"
- << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ get_null (member_info& mi)
{
- os << traits << "::init (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "db);"
- << endl;
+ os << "i." << mi.var << "null";
}
virtual void
@@ -970,14 +702,6 @@ namespace relational
<< "i." << mi.var << "null);"
<< endl;
}
-
- private:
- string type;
- string db_type_id;
- string traits;
- string member;
-
- member_database_type_id member_database_type_id_;
};
entry<init_value_member> init_value_member_;
@@ -1090,7 +814,7 @@ namespace relational
<< "{";
instance<statement_oids> st (statement_select);
- st->traverse_column (*id, "", true);
+ st->traverse (*id);
os << "};";
}
@@ -1111,16 +835,11 @@ namespace relational
bool first (cc.total == cc.id + cc.inverse + cc.readonly +
cc.optimistic_managed);
- {
- instance<statement_oids> st (statement_where);
- st->traverse_column (*id, "", first);
- }
+ instance<statement_oids> st (statement_where, first);
+ st->traverse (*id);
if (optimistic != 0)
- {
- instance<statement_oids> st (statement_where);
- st->traverse_column (*optimistic, "", false);
- }
+ st->traverse (*optimistic);
os << "};";
}
@@ -1134,7 +853,7 @@ namespace relational
<< "{";
instance<statement_oids> st (statement_where);
- st->traverse_column (*id, "", true);
+ st->traverse (*id);
os << "};";
}
@@ -1145,15 +864,9 @@ namespace relational
<< "optimistic_erase_statement_types[] ="
<< "{";
- {
- instance<statement_oids> st (statement_where);
- st->traverse_column (*id, "", true);
- }
-
- {
- instance<statement_oids> st (statement_where);
- st->traverse_column (*optimistic, "", false);
- }
+ instance<statement_oids> st (statement_where);
+ st->traverse (*id);
+ st->traverse (*optimistic);
os << "};";
}
@@ -1258,8 +971,7 @@ namespace relational
bool inv (inv_m != 0);
semantics::type& vt (container_vt (t));
-
- string id_oid (oids[column_sql_type (m, "id").type]);
+ semantics::type& idt (container_idt (m));
// select_all statement types.
//
@@ -1268,20 +980,22 @@ namespace relational
<< "select_all_types[] ="
<< "{";
+ instance<statement_oids> so (statement_where);
+
if (inv)
{
// many(i)-to-many
//
if (container (*inv_m))
- os << oids[column_sql_type (*inv_m, "value").type];
+ so->traverse (*inv_m, idt, "value", "value");
// many(i)-to-one
//
else
- os << oids[column_sql_type (*inv_m).type];
+ so->traverse (*inv_m);
}
else
- os << id_oid;
+ so->traverse (m, idt, "id", "object_id");
os << "};";
}
@@ -1295,30 +1009,22 @@ namespace relational
if (!inv)
{
- os << id_oid << ",";
+ instance<statement_oids> so (statement_insert);
+
+ so->traverse (m, idt, "id", "object_id");
switch (container_kind (t))
{
case ck_ordered:
{
if (!unordered (m))
- os << oids[column_sql_type (m, "index").type] << ",";
-
+ so->traverse (m, container_it (t), "index", "index");
break;
}
case ck_map:
case ck_multimap:
{
- if (semantics::class_* ktc =
- composite_wrapper (container_kt (t)))
- {
- instance<statement_oids> st (statement_insert);
- st->traverse (m, *ktc, "key", "key");
- os << ",";
- }
- else
- os << oids[column_sql_type (m, "key").type] << ",";
-
+ so->traverse (m, container_kt (t), "key", "key");
break;
}
case ck_set:
@@ -1328,14 +1034,7 @@ namespace relational
}
}
- if (semantics::class_* vtc = composite_wrapper (vt))
- {
- instance <statement_oids> st (statement_insert);
- st->traverse (m, *vtc, "value", "value");
- }
- else
- os << oids[column_sql_type (m, "value").type];
-
+ so->traverse (m, vt, "value", "value");
}
else
// MSVC does not allow zero length arrays or uninitialized
@@ -1354,7 +1053,10 @@ namespace relational
<< "{";
if (!inv)
- os << id_oid;
+ {
+ instance<statement_oids> so (statement_where);
+ so->traverse (m, idt, "id", "object_id");
+ }
else
os << "0";
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
index 8c18f02..0bede14 100644
--- a/odb/relational/processor.cxx
+++ b/odb/relational/processor.cxx
@@ -142,7 +142,7 @@ namespace relational
// const auto_ptr<T> - can modify by changing the pointed-to value
//
if (const_type (m.type ()) &&
- !(m.count ("id") || m.count ("version") || m.count ("inverse")))
+ !(id (m) || version (m) || m.count ("inverse")))
{
if (qwt == 0 || const_type (*qwt))
m.set ("readonly", true);
@@ -153,11 +153,19 @@ namespace relational
if (composite_wrapper (t))
return;
- string type, ref_type;
+ string type, id_type;
+
+ if (m.count ("id-type"))
+ id_type = m.get<string> ("id-type");
if (m.count ("type"))
+ {
type = m.get<string> ("type");
+ if (id_type.empty ())
+ id_type = type;
+ }
+
if (semantics::class_* c = process_object_pointer (m, t))
{
// This is an object pointer. The column type is the pointed-to
@@ -168,26 +176,55 @@ namespace relational
semantics::names* idhint;
semantics::type& idt (utype (id, idhint));
+ semantics::type* wt (0);
+ semantics::names* whint (0);
+ if (process_wrapper (idt))
+ {
+ whint = idt.get<semantics::names*> ("wrapper-hint");
+ wt = &utype (*idt.get<semantics::type*> ("wrapper-type"), whint);
+ }
+
+ // Nothing to do if this is a composite value type.
+ //
+ if (composite_wrapper (idt))
+ return;
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
if (type.empty () && id.count ("type"))
type = id.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 () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
if (type.empty () && idt.count ("type"))
type = idt.get<string> ("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
{
- if (type.empty () && m.count ("id") && t.count ("id-type"))
- type = t.get<string> ("id-type");
+ if (id_type.empty () && t.count ("id-type"))
+ id_type = t.get<string> ("id-type");
- if (type.empty () && wt != 0 && m.count ("id") &&
- wt->count ("id-type"))
- type = wt->get<string> ("id-type");
+ if (id_type.empty () && wt != 0 && wt->count ("id-type"))
+ id_type = wt->get<string> ("id-type");
if (type.empty () && t.count ("type"))
type = t.get<string> ("type");
@@ -195,16 +232,29 @@ namespace relational
if (type.empty () && wt != 0 && wt->count ("type"))
type = wt->get<string> ("type");
+ if (id_type.empty ())
+ id_type = type;
+
+ if (id_type.empty ())
+ id_type = database_type (t, hint, true);
+
if (type.empty ())
- type = database_type (t, hint, m.count ("id"));
+ type = database_type (t, hint, false);
+
+ if (id_type.empty () && wt != 0)
+ id_type = database_type (*wt, whint, true);
if (type.empty () && wt != 0)
- type = database_type (*wt, whint, m.count ("id"));
+ type = database_type (*wt, whint, false);
+
+ if (id (m))
+ type = id_type;
}
if (!type.empty ())
{
m.set ("column-type", type);
+ m.set ("column-id-type", id_type);
// Issue a warning if we are relaxing null-ness.
//
@@ -274,24 +324,53 @@ namespace relational
if (obj_ptr && (c = process_object_pointer (m, t, prefix)))
{
// This is an object pointer. The column type is the pointed-to
- // object id type. Except by default it can be NULL.
+ // object id type.
//
semantics::data_member& id (*id_member (*c));
- semantics::names* hint;
- semantics::type& idt (utype (id, hint));
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ semantics::type* wt (0);
+ semantics::names* whint (0);
+ if (process_wrapper (idt))
+ {
+ whint = idt.get<semantics::names*> ("wrapper-hint");
+ wt = &utype (*idt.get<semantics::type*> ("wrapper-type"), whint);
+ }
+
+ // Nothing to do if this is a composite value type.
+ //
+ if (composite_wrapper (idt))
+ return;
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
if (type.empty () && id.count ("type"))
type = id.get<string> ("type");
+ // The rest of the code is identical to the else block except here
+ // we have to check for "id-type" before checking for "type".
+ //
+
if (type.empty () && idt.count ("id-type"))
type = idt.get<string> ("id-type");
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
if (type.empty () && idt.count ("type"))
type = idt.get<string> ("type");
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
if (type.empty ())
- type = database_type (idt, hint, true);
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
}
else
{
@@ -311,6 +390,7 @@ namespace relational
if (!type.empty ())
{
m.set (prefix + "-column-type", type);
+ m.set (prefix + "-column-id-type", type);
return;
}
@@ -1094,7 +1174,7 @@ namespace relational
TREE_VEC_ELT (args, 0) = arg;
// This step should succeed regardles of whether there is a
- // container traits specialization for this type.
+ // specialization for this type.
//
tree inst (
lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error));
@@ -1939,30 +2019,27 @@ namespace relational
}
virtual void
- traverse_simple (semantics::data_member& m)
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
- if (semantics::class_* c = object_pointer (utype (m)))
- {
- // 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.
- //
- if (inverse (m))
- return;
+ // 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.
+ //
+ if (inverse (m))
+ return;
- // Ignore self-pointers if requested.
- //
- if (!self_pointer_ && pointer_->obj == c)
- return;
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == &c)
+ return;
- if (pointee_.obj == c)
- {
- relationships_.push_back (relationship ());
- relationships_.back ().member = &m;
- relationships_.back ().name = member_prefix_ + m.name ();
- relationships_.back ().pointer = pointer_;
- relationships_.back ().pointee = &pointee_;
- }
+ if (pointee_.obj == &c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
}
}
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index 52ff093..7f94410 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -28,13 +28,15 @@ namespace relational
{
statement_column (): member (0) {}
statement_column (std::string const& c,
+ std::string const& t,
semantics::data_member& m,
std::string const& kp = "")
- : column (c), member (&m), key_prefix (kp)
+ : column (c), type (t), member (&m), key_prefix (kp)
{
}
- std::string column;
+ std::string column; // Column name.
+ std::string type; // Column SQL type.
semantics::data_member* member;
std::string key_prefix;
};
@@ -83,73 +85,108 @@ namespace relational
{
}
- virtual bool
- traverse_column (semantics::data_member& m, string const& name, bool)
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
- semantics::data_member* im (inverse (m));
+ semantics::data_member* im (inverse (m, key_prefix_));
// Ignore certain columns depending on what kind statement we are
// generating. Columns corresponding to the inverse members are
- // only present in the select statements while the id and readonly
- // columns are not present in the update statements.
+ // only present in the select statements.
//
if (im != 0 && sk_ != statement_select)
- return false;
-
- if ((id (m) || readonly (member_path_, member_scope_)) &&
- sk_ == statement_update)
- return false;
+ return;
// Inverse object pointers come from a joined table.
//
if (im != 0)
{
- semantics::class_* c (object_pointer (utype (m)));
+ semantics::data_member& id (*id_member (c));
+ semantics::type& idt (utype (id));
if (container (*im))
{
// This container is a direct member of the class so the table
// prefix is just the class table name. We don't assign join
// aliases for container tables so use the actual table name.
- // Note that the (table_name_.empty () ? :) test may look wrong
- // at first but it is no.
+ // Note that the if(!table_name_.empty ()) test may look wrong
+ // at first but it is not; if table_name_ is empty then we are
+ // generating a container table where we don't qualify columns
+ // with tables.
//
- column (
- *im,
- "id",
- table_name_.empty ()
- ? table_name_
- : table_qname (*im,
- table_prefix (schema (c->scope ()),
- table_name (*c) + "_",
- 1)),
- column_qname (*im, "id", "object_id"));
+ string table;
+
+ if (!table_name_.empty ())
+ {
+ table_prefix tp (schema (c.scope ()), table_name (c) + "_", 1);
+ table = table_qname (*im, tp);
+ }
+
+ instance<object_columns> oc (table, sk_, sc_);
+ oc->traverse (*im, idt, "id", "object_id", &c);
}
else
{
- semantics::data_member& id (*id_member (*c));
-
- // Use the join alias (column name) instead of the actual
- // table name unless we are handling a container. Note that
- // the (table_name_.empty () ? :) test may look wrong at
- // first but it is no.
+ // Use the join alias instead of the actual table name unless we
+ // are handling a container. Generally, we want the join alias
+ // to be based on the column name. This is straightforward for
+ // single-column references. In case of a composite id, we will
+ // need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name. Note that the
+ // if(!table_name_.empty ()) test may look wrong at first but
+ // it is not; if table_name_ is empty then we are generating a
+ // container table where we don't qualify columns with tables.
//
- column (
- id,
- "",
- table_name_.empty () ? table_name_ : quote_id (name),
- column_qname (id));
+ string table;
+
+ if (!table_name_.empty ())
+ {
+ if (composite_wrapper (idt))
+ {
+ string p (column_prefix (m, key_prefix_, default_name_));
+
+ if (p.empty ())
+ p = public_name_db (m);
+ else
+ p.resize (p.size () - 1); // Remove trailing underscore.
+
+ table = column_prefix_ + p;
+ }
+ else
+ table = column_prefix_ +
+ column_name (m, key_prefix_, default_name_);
+
+ table = quote_id (table);
+ }
+
+ instance<object_columns> oc (table, sk_, sc_);
+ oc->traverse (id);
}
}
else
- column (m, "", table_name_, quote_id (name));
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. Id and readonly columns are not present in the update
+ // statements.
+ //
+ if ((id () || readonly (member_path_, member_scope_)) &&
+ sk_ == statement_update)
+ return false;
+
+ column (m, table_name_, quote_id (name));
return true;
}
virtual void
column (semantics::data_member& m,
- string const& key_prefix,
string const& table,
string const& column)
{
@@ -177,7 +214,7 @@ namespace relational
r += param_->next ();
}
- sc_.push_back (statement_column (r, m, key_prefix));
+ sc_.push_back (statement_column (r, column_type (), m, key_prefix_));
}
protected:
@@ -350,7 +387,7 @@ namespace relational
virtual void
column (semantics::data_member& m, string const& column)
{
- sc_.push_back (statement_column (column, m));
+ sc_.push_back (statement_column (column, column_type (), m));
}
protected:
@@ -370,6 +407,7 @@ namespace relational
table_ (table_qname (scope)),
id_ (*id_member (scope))
{
+ id_cols_->traverse (id_);
}
size_t
@@ -399,31 +437,63 @@ namespace relational
}
}
- virtual bool
- traverse_column (semantics::data_member& m, string const& column, bool)
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
- semantics::class_* c (object_pointer (utype (m)));
-
- if (c == 0)
- return false;
-
string t, a, dt, da;
std::ostringstream cond, dcond; // @@ diversion?
- if (semantics::data_member* im = inverse (m))
+ // Derive table alias for this member. Generally, we want the
+ // alias to be based on the column name. This is straightforward
+ // for single-column references. In case of a composite id, we
+ // will need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name.
+ //
+ string alias;
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ string p (column_prefix (m, key_prefix_, default_name_));
+
+ if (p.empty ())
+ p = public_name_db (m);
+ else
+ p.resize (p.size () - 1); // Remove trailing underscore.
+
+ alias = column_prefix_ + p;
+ }
+ else
+ alias = column_prefix_ +
+ column_name (m, key_prefix_, default_name_);
+
+ if (semantics::data_member* im = inverse (m, key_prefix_))
{
if (container (*im))
{
// This container is a direct member of the class so the table
// prefix is just the class table name.
//
- qname const& ct (table_name (*c));
- table_prefix tp (schema (c->scope ()), ct + "_", 1);
+ qname const& ct (table_name (c));
+ table_prefix tp (schema (c.scope ()), ct + "_", 1);
t = table_qname (*im, tp);
- string const& val (column_qname (*im, "value", "value"));
- cond << t << '.' << val << " = " <<
- table_ << "." << column_qname (id_);
+ // Container's value is our id.
+ //
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*im, utype (id_), "value", "value");
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << t << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
// Add the join for the object itself so that we are able to
// use it in the WHERE clause.
@@ -431,21 +501,44 @@ namespace relational
if (query_)
{
dt = quote_id (ct);
- da = quote_id (column);
+ da = quote_id (alias);
+
+ semantics::data_member& id (*id_member (c));
+
+ instance<object_columns_list> oid_cols, cid_cols;
+ oid_cols->traverse (id);
+ cid_cols->traverse (*im, utype (id), "id", "object_id", &c);
+
+ for (object_columns_list::iterator b (cid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != cid_cols->end (); ++i, ++j)
+ {
- string const& id (column_qname (*im, "id", "object_id"));
+ if (i != b)
+ dcond << " AND ";
- dcond << da << '.' << column_qname (*id_member (*c)) << " = " <<
- t << '.' << id;
+ dcond << da << '.' << quote_id (j->name) << '=' <<
+ t << '.' << quote_id (i->name);
+ }
}
}
else
{
- t = table_qname (*c);
- a = quote_id (column);
+ t = table_qname (c);
+ a = quote_id (alias);
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*im);
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
- cond << a << '.' << column_qname (*im) << " = " <<
- table_ << "." << column_qname (id_);
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
}
}
else if (query_)
@@ -453,11 +546,25 @@ namespace relational
// We need the join to be able to use the referenced object
// in the WHERE clause.
//
- t = table_qname (*c);
- a = quote_id (column);
+ t = table_qname (c);
+ a = quote_id (alias);
- cond << a << '.' << column_qname (*id_member (*c)) << " = " <<
- table_ << "." << quote_id (column);
+ instance<object_columns_list> oid_cols (column_prefix_);
+ oid_cols->traverse (m);
+
+ instance<object_columns_list> pid_cols;
+ pid_cols->traverse (*id_member (c));
+
+ for (object_columns_list::iterator b (pid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != pid_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
}
if (!t.empty ())
@@ -478,14 +585,13 @@ namespace relational
joins_.back ().alias = da;
joins_.back ().cond = dcond.str ();
}
-
- return true;
}
private:
bool query_;
string table_;
semantics::data_member& id_;
+ instance<object_columns_list> id_cols_;
struct join
{
@@ -690,6 +796,207 @@ namespace relational
string member_override_;
};
+ template <typename T>
+ struct init_image_member_impl: init_image_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_image_member_impl base_impl;
+
+ init_image_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ virtual void
+ set_null (member_info&) = 0;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // Ignore containers (they get their own table) and inverse
+ // object pointers (they are not present in this binding).
+ //
+ if (container (mi) || inverse (mi.m, key_prefix_))
+ return false;
+
+ if (!member_override_.empty ())
+ member = member_override_;
+ else
+ {
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
+ // If we don't send auto id in INSERT statement, ignore this
+ // member altogether (we never send auto id in UPDATE).
+ //
+ if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
+ return false;
+
+ string const& name (mi.m.name ());
+ member = "o." + name;
+
+ os << "// " << name << endl
+ << "//" << endl;
+
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ if (!readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c))) // Can't be id.
+ os << "if (sk == statement_insert)";
+ }
+ }
+
+ bool comp (composite (mi.t));
+
+ // If this is a wrapped composite value, then we need to "unwrap"
+ // it. For simple values this is taken care of by the value_traits
+ // specializations.
+ //
+ if (mi.wrapper != 0 && comp)
+ {
+ // Here we need the wrapper type, not the wrapped type.
+ //
+ member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
+ "get_ref (" + member + ")";
+ }
+
+ if (mi.ptr != 0)
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (member_utype (mi.m, key_prefix_));
+
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "{"
+ << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;";
+
+ if (weak_pointer (pt))
+ {
+ os << "typedef pointer_traits< " << mi.ptr_fq_type () <<
+ " > wptr_traits;"
+ << "typedef pointer_traits< wptr_traits::" <<
+ "strong_pointer_type > ptr_traits;"
+ << endl
+ << "wptr_traits::strong_pointer_type sp (" <<
+ "wptr_traits::lock (" << member << "));";
+
+ member = "sp";
+ }
+ else
+ os << "typedef pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
+ << "if (!is_null)"
+ << "{"
+ << "const " << type << "& id (" << endl;
+
+ if (lazy_pointer (pt))
+ os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
+ member << ")";
+ else
+ os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
+
+ os << ");"
+ << endl;
+
+ member = "id";
+ }
+ else if (comp)
+ {
+ type = mi.fq_type ();
+
+ os << "{";
+ }
+ else
+ {
+ type = mi.fq_type ();
+
+ os << "{"
+ << "bool is_null;";
+ }
+
+ if (comp)
+ traits = "composite_value_traits< " + type + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = string (db.string ()) + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (mi.ptr != 0)
+ {
+ os << "}"
+ << "else" << endl;
+
+ // @@ Composite value currently cannot be NULL.
+ //
+ if (!null (mi.m, key_prefix_) || composite (mi.t))
+ os << "throw null_pointer ();";
+ else
+ set_null (mi);
+ }
+
+ os << "}";
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ bool grow (generate_grow && context::grow (mi.m, mi.t, key_prefix_));
+
+ if (grow)
+ os << "if (";
+
+ os << traits << "::init (" << endl
+ << "i." << mi.var << "value," << endl
+ << member << "," << endl
+ << "sk)";
+
+ if (grow)
+ os << ")" << endl
+ << "grew = true";
+
+ os << ";";
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string member;
+ string traits;
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
struct init_image_base: traversal::class_, virtual context
{
typedef init_image_base base;
@@ -755,6 +1062,171 @@ namespace relational
string member_override_;
};
+ template <typename T>
+ struct init_value_member_impl: init_value_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_value_member_impl base_impl;
+
+ init_value_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ virtual void
+ get_null (member_info&) = 0;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ if (!member_override_.empty ())
+ member = member_override_;
+ else
+ {
+ string const& name (mi.m.name ());
+ member = "o." + name;
+
+ if (mi.cq)
+ {
+ string t (mi.ptr == 0 ? mi.fq_type (false) : mi.ptr_fq_type ());
+ member = "const_cast< " + t + "& > (" + member + ")";
+ }
+
+ os << "// " << name << endl
+ << "//" << endl;
+ }
+
+ bool comp (composite (mi.t));
+
+ // If this is a wrapped composite value, then we need to
+ // "unwrap" it. For simple values this is taken care of
+ // by the value_traits specializations.
+ //
+ if (mi.wrapper != 0 && comp)
+ {
+ // Here we need the wrapper type, not the wrapped type.
+ //
+ member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
+ "set_ref (\n" + member + ")";
+ }
+
+ if (mi.ptr != 0)
+ {
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "{"
+ << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;"
+ << "typedef pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ // @@ Composite value currently cannot be NULL.
+ //
+ if (!comp)
+ {
+ os << "if (";
+ get_null (mi);
+ os << ")" << endl;
+
+ if (!null (mi.m, key_prefix_) )
+ os << "throw null_pointer ();";
+ else
+ os << member << " = ptr_traits::pointer_type ();";
+
+ os << "else"
+ << "{";
+ }
+
+ os << type << " id;";
+
+ member = "id";
+ }
+ else
+ type = mi.fq_type ();
+
+ if (comp)
+ traits = "composite_value_traits< " + type + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = string (db.string ()) + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (mi.ptr != 0)
+ {
+ if (!member_override_.empty ())
+ member = member_override_;
+ else
+ {
+ member = "o." + mi.m.name ();
+
+ if (mi.cq)
+ member = "const_cast< " + mi.ptr_fq_type () +
+ "& > (" + member + ")";
+ }
+
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (member_utype (mi.m, key_prefix_));
+
+ if (lazy_pointer (pt))
+ os << member << " = ptr_traits::pointer_type (*db, id);";
+ else
+ os << "// If a compiler error points to the line below, then" << endl
+ << "// it most likely means that a pointer used in a member" << endl
+ << "// cannot be initialized from an object pointer." << endl
+ << "//" << endl
+ << member << " = ptr_traits::pointer_type (" << endl
+ << "db->load< obj_traits::object_type > (id));";
+
+ // @@ Composite value currently cannot be NULL.
+ //
+ if (!composite (mi.t))
+ os << "}";
+
+ os << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << traits << "::init (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "db);"
+ << endl;
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string traits;
+ string member;
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
struct init_value_base: traversal::class_, virtual context
{
typedef init_value_base base;
@@ -930,7 +1402,10 @@ namespace relational
//
if (!abst)
{
+ semantics::type& idt (container_idt (m));
+
string table (table_qname (m, table_prefix_));
+ instance<object_columns_list> id_cols;
// select_all_statement
//
@@ -940,10 +1415,12 @@ namespace relational
if (inverse)
{
semantics::class_* c (object_pointer (vt));
+ semantics::data_member& inv_id (*id_member (*c));
- string inv_table; // Other table name.
- string inv_id; // Other id column.
- string inv_fid; // Other foreign id column (ref to us).
+ string inv_table; // Other table name.
+ instance<object_columns_list> inv_id_cols; // Other id column.
+ instance<object_columns_list> inv_fid_cols; // Other foreign id
+ // column (ref to us).
statement_columns sc;
if (container (*im))
@@ -956,23 +1433,42 @@ namespace relational
//
table_prefix tp (schema (c->scope ()), table_name (*c) + "_", 1);
inv_table = table_qname (*im, tp);
- inv_id = column_qname (*im, "id", "object_id");
- inv_fid = column_qname (*im, "value", "value");
- sc.push_back (statement_column (
- inv_table + "." + inv_id, *im, "id"));
+ inv_id_cols->traverse (*im, utype (inv_id), "id", "object_id", c);
+ inv_fid_cols->traverse (*im, idt, "value", "value");
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ // If this is a simple id, then pass the "id" key prefix. If
+ // it is a composite id, then the members have no prefix.
+ //
+ sc.push_back (
+ statement_column (
+ inv_table + "." + quote_id (i->name),
+ i->type,
+ *i->member,
+ inv_id_cols->size () == 1 ? "id" : ""));
+ }
}
else
{
// many(i)-to-one
//
- semantics::data_member& id (*id_member (*c));
-
inv_table = table_qname (*c);
- inv_id = column_qname (id);
- inv_fid = column_qname (*im);
- sc.push_back (statement_column (inv_table + "." + inv_id, id));
+ inv_id_cols->traverse (inv_id);
+ inv_fid_cols->traverse (*im);
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ sc.push_back (
+ statement_column (
+ inv_table + "." + quote_id (i->name),
+ i->type,
+ *i->member));
+ }
}
process_statement_columns (sc, statement_select);
@@ -987,12 +1483,20 @@ namespace relational
}
instance<query_parameters> qp;
- os << strlit (" FROM " + inv_table +
- " WHERE " + inv_table + "." + inv_fid + "=" +
- qp->next ());
+ os << strlit (" FROM " + inv_table);
+
+ for (object_columns_list::iterator b (inv_fid_cols->begin ()),
+ i (b); i != inv_fid_cols->end (); ++i)
+ {
+ os << endl
+ << strlit ((i == b ? " WHERE " : " AND ") + inv_table + "." +
+ quote_id (i->name) + "=" + qp->next ());
+ }
}
else
{
+ id_cols->traverse (m, idt, "id", "object_id");
+
statement_columns sc;
statement_kind sk (statement_select); // Imperfect forwarding.
instance<object_columns> t (table, sk, sc);
@@ -1002,22 +1506,13 @@ namespace relational
case ck_ordered:
{
if (ordered)
- {
- string const& col (column_qname (m, "index", "index"));
- t->column (m, "index", table, col);
- }
+ t->traverse (m, *it, "index", "index");
break;
}
case ck_map:
case ck_multimap:
{
- if (semantics::class_* ckt = composite_wrapper (*kt))
- t->traverse (m, *ckt, "key", "key");
- else
- {
- string const& col (column_qname (m, "key", "key"));
- t->column (m, "key", table, col);
- }
+ t->traverse (m, *kt, "key", "key");
break;
}
case ck_set:
@@ -1027,13 +1522,7 @@ namespace relational
}
}
- if (semantics::class_* cvt = composite_wrapper (vt))
- t->traverse (m, *cvt, "value", "value");
- else
- {
- string const& col (column_qname (m, "value", "value"));
- t->column (m, "value", table, col);
- }
+ t->traverse (m, vt, "value", "value");
process_statement_columns (sc, statement_select);
@@ -1047,18 +1536,22 @@ namespace relational
}
instance<query_parameters> qp;
- string const& id_col (column_qname (m, "id", "object_id"));
+ os << strlit (" FROM " + table);
- os << strlit (" FROM " + table +
- " WHERE " + table + "." + id_col + "=" +
- qp->next ());
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ os << endl
+ << strlit ((i == b ? " WHERE " : " AND ") + table + "." +
+ quote_id (i->name) + "=" + qp->next ());
+ }
if (ordered)
{
string const& col (column_qname (m, "index", "index"));
os << endl
- << strlit (" ORDER BY " + table + "." + col) << endl;
+ << strlit (" ORDER BY " + table + "." + col);
}
}
@@ -1076,29 +1569,23 @@ namespace relational
else
{
statement_columns sc;
- sc.push_back (
- statement_column (
- column_qname (m, "id", "object_id"), m, "id"));
-
statement_kind sk (statement_insert); // Imperfect forwarding.
instance<object_columns> t (sk, sc);
+ t->traverse (m, idt, "id", "object_id");
+
switch (ck)
{
case ck_ordered:
{
if (ordered)
- t->column (
- m, "index", "", column_qname (m, "index", "index"));
+ t->traverse (m, *it, "index", "index");
break;
}
case ck_map:
case ck_multimap:
{
- if (semantics::class_* ckt = composite_wrapper (*kt))
- t->traverse (m, *ckt, "key", "key");
- else
- t->column (m, "key", "", column_qname (m, "key", "key"));
+ t->traverse (m, *kt, "key", "key");
break;
}
case ck_set:
@@ -1108,10 +1595,7 @@ namespace relational
}
}
- if (semantics::class_* cvt = composite_wrapper (vt))
- t->traverse (m, *cvt, "value", "value");
- else
- t->column (m, "value", "", column_qname (m, "value", "value"));
+ t->traverse (m, vt, "value", "value");
process_statement_columns (sc, statement_insert);
@@ -1151,9 +1635,17 @@ namespace relational
{
instance<query_parameters> qp;
- os << strlit ("DELETE FROM " + table) << endl
- << strlit (" WHERE " + column_qname (m, "id", "object_id") +
- "=" + qp->next ()) << ";"
+ os << strlit ("DELETE FROM " + table);
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ os << endl
+ << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ os << ";"
<< endl;
}
}
@@ -1462,10 +1954,10 @@ namespace relational
{
if (ordered)
os << "init (index_type& j, value_type& v, " <<
- "const data_image_type& i, database& db)";
+ "const data_image_type& i, database* db)";
else
os << "init (value_type& v, const data_image_type& i, " <<
- "database& db)";
+ "database* db)";
os << "{"
<< "ODB_POTENTIALLY_UNUSED (db);"
@@ -1487,7 +1979,7 @@ namespace relational
case ck_multimap:
{
os << "init (key_type& k, value_type& v, " <<
- "const data_image_type& i, database& db)"
+ "const data_image_type& i, database* db)"
<< "{"
<< "ODB_POTENTIALLY_UNUSED (db);"
<< endl
@@ -1504,7 +1996,7 @@ namespace relational
case ck_multiset:
{
os << "init (value_type& v, const data_image_type& i, " <<
- "database& db)"
+ "database* db)"
<< "{"
<< "ODB_POTENTIALLY_UNUSED (db);"
<< endl;
@@ -1652,21 +2144,21 @@ namespace relational
case ck_ordered:
{
os << "init (" << (ordered ? "i, " : "") <<
- "v, di, sts.connection ().database ());"
+ "v, di, &sts.connection ().database ());"
<< endl;
break;
}
case ck_map:
case ck_multimap:
{
- os << "init (k, v, di, sts.connection ().database ());"
+ os << "init (k, v, di, &sts.connection ().database ());"
<< endl;
break;
}
case ck_set:
case ck_multiset:
{
- os << "init (v, di, sts.connection ().database ());"
+ os << "init (v, di, &sts.connection ().database ());"
<< endl;
break;
}
@@ -2130,26 +2622,30 @@ namespace relational
}
virtual void
- traverse_simple (semantics::data_member& m)
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
if (!inverse (m))
- {
- string p;
+ object_members_base::traverse_pointer (m, c);
+ }
- if (version (m))
- p = "1";
- else if (id (m) && auto_ (m))
- p = qp_.auto_id ();
- else
- p = qp_.next ();
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ string p;
- if (!p.empty ())
- {
- if (count_++ != 0)
- params_ += ',';
+ if (version (m))
+ p = "1";
+ else if (context::id (m) && auto_ (m)) // Only simple id can be auto.
+ p = qp_.auto_id ();
+ else
+ p = qp_.next ();
- params_ += p;
- }
+ if (!p.empty ())
+ {
+ if (count_++ != 0)
+ params_ += ',';
+
+ params_ += p;
}
}
@@ -2434,6 +2930,9 @@ namespace relational
<< traits << "::" << endl
<< "id (const image_type& i)"
<< "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
<< "id_type id;";
init_id_value_member_->traverse (*id);
os << "return id;"
@@ -2505,11 +3004,14 @@ namespace relational
<< "{"
<< "std::size_t n (0);";
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
bind_id_member_->traverse (*id);
if (optimistic != 0)
{
- os << "n++;" //@@ composite id
+ os << "n += " << column_count (c).id << ";"
<< endl;
bind_version_member_->traverse (*optimistic);
@@ -2549,7 +3051,7 @@ namespace relational
// init (object, image)
//
os << "void " << traits << "::" << endl
- << "init (object_type& o, const image_type& i, database& db)"
+ << "init (object_type& o, const image_type& i, database* db)"
<< "{"
<< "ODB_POTENTIALLY_UNUSED (o);"
<< "ODB_POTENTIALLY_UNUSED (i);"
@@ -2571,8 +3073,10 @@ namespace relational
<< "{";
if (grow_id)
- os << "bool grew (false);"
- << endl;
+ os << "bool grew (false);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
init_id_image_member_->traverse (*id);
@@ -2677,7 +3181,8 @@ namespace relational
if (id != 0)
{
- string const& id_col (column_qname (*id));
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id);
// find_statement
//
@@ -2702,14 +3207,23 @@ namespace relational
os << strlit (" FROM " + table) << endl;
- bool f (false);
+ bool f (false); // @@ (im)perfect forwarding
instance<object_joins> j (c, f); // @@ (im)perfect forwarding
j->traverse (c);
j->write ();
instance<query_parameters> qp;
- os << strlit (" WHERE " + table + "." + id_col + "=" +
- qp->next ()) << ";"
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ os << endl;
+
+ os << strlit ((i == b ? " WHERE " : " AND ") + table + "." +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ os << ";"
<< endl;
}
@@ -2739,13 +3253,22 @@ namespace relational
os << strlit (c + (++i != e ? "," : "")) << endl;
}
- string where (" WHERE " + id_col + "=" + qp->next ());
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ os << endl;
+
+ os << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" + qp->next ());
+ }
if (optimistic != 0)
- where += " AND " + column_qname (*optimistic) + "=" +
- qp->next ();
+ os << endl
+ << strlit (" AND " + column_qname (*optimistic) +
+ "=" + qp->next ());
- os << strlit (where) << ";"
+ os << ";"
<< endl;
}
@@ -2754,8 +3277,18 @@ namespace relational
{
instance<query_parameters> qp;
os << "const char " << traits << "::erase_statement[] =" << endl
- << strlit ("DELETE FROM " + table) << endl
- << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";"
+ << strlit ("DELETE FROM " + table);
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+
+ os << endl
+ << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ os << ";"
<< endl;
}
@@ -2763,13 +3296,21 @@ namespace relational
{
instance<query_parameters> qp;
- string where (" WHERE " + id_col + "=" + qp->next ());
- where += " AND " + column_qname (*optimistic) + "=" + qp->next ();
-
os << "const char " << traits <<
"::optimistic_erase_statement[] =" << endl
- << strlit ("DELETE FROM " + table) << endl
- << strlit (where) << ";"
+ << strlit ("DELETE FROM " + table);
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ os << endl
+ << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ os << endl
+ << strlit (" AND " + column_qname (*optimistic) +
+ "=" + qp->next ()) << ";"
<< endl;
}
}
@@ -2786,10 +3327,6 @@ namespace relational
process_statement_columns (sc, statement_select);
}
- bool t (true);
- instance<object_joins> oj (c, t); //@@ (im)perfect forwarding
- oj->traverse (c);
-
os << "const char " << traits << "::query_statement[] =" << endl
<< strlit ("SELECT ") << endl;
@@ -2801,7 +3338,15 @@ namespace relational
}
os << strlit (" FROM " + table) << endl;
- oj->write ();
+
+ if (id != 0)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ instance<object_joins> oj (c, t); //@@ (im)perfect forwarding
+ oj->traverse (c);
+ oj->write ();
+ }
+
os << strlit (" ") << ";"
<< endl;
@@ -3202,7 +3747,7 @@ namespace relational
<< "if (l.locked ())"
<< "{"
<< "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), db);";
+ << "init (obj, sts.image (), &db);";
init_value_extra ();
free_statement_result_delayed ();
@@ -3248,7 +3793,7 @@ namespace relational
<< "reference_cache_traits< object_type >::insert (db, id, obj));"
<< endl
<< "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), db);";
+ << "init (obj, sts.image (), &db);";
init_value_extra ();
free_statement_result_delayed ();
@@ -3298,7 +3843,7 @@ namespace relational
}
os << "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), db);";
+ << "init (obj, sts.image (), &db);";
init_value_extra ();
free_statement_result_delayed ();
@@ -3766,7 +4311,7 @@ namespace relational
// init (view, image)
//
os << "void " << traits << "::" << endl
- << "init (view_type& o, const image_type& i, database& db)"
+ << "init (view_type& o, const image_type& i, database* db)"
<< "{"
<< "ODB_POTENTIALLY_UNUSED (o);"
<< "ODB_POTENTIALLY_UNUSED (i);"
@@ -4080,7 +4625,7 @@ namespace relational
if (im != 0)
{
// For now a direct member can only be directly in
- // the object scope. When this changes, the inverse()
+ // the object scope. If this changes, the inverse()
// function would have to return a member path instead
// of just a single member.
//
@@ -4102,34 +4647,36 @@ namespace relational
// 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.
+ // 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 (i->obj == c)
{
// container.value = pointer.id
//
- l = ct;
- l += '.';
- l += column_qname (*im, "value", "value");
- l += "=";
- l += quote_id (lt);
- l += '.';
- l += column_qname (*id_member (*e.vo->obj));
+ 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
//
- l = ct;
- l += '.';
- l += column_qname (*im, "id", "object_id");
- l += "=";
- l += quote_id (rt);
- l += '.';
- l += column_qname (*id_member (*vo->obj));
+ 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
@@ -4138,29 +4685,43 @@ namespace relational
{
// container.id = pointer.id
//
- l = ct;
- l += '.';
- l += column_qname (m, "id", "object_id");
- l += "=";
- l += quote_id (lt);
- l += '.';
- l += column_qname (*id_member (*e.vo->obj));
+ 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
//
- l = ct;
- l += '.';
- l += column_qname (m, "value", "value");
- l += "=";
- l += quote_id (rt);
- l += '.';
- l += column_qname (*id_member (*vo->obj));
+ semantics::data_member& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
}
}
- os << "r += " << strlit (l) << ";";
+ 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);
+
+ os << "r += " << strlit (l) << ";";
+ }
}
l = "LEFT JOIN ";
@@ -4174,31 +4735,33 @@ namespace relational
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 (i->obj == c)
{
// container.id = pointed-to.id
//
- l = ct;
- l += '.';
- l += column_qname (*im, "id", "object_id");
- l += "=";
- l += quote_id (rt);
- l += '.';
- l += column_qname (*id_member (*vo->obj));
+ 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
//
- l = ct;
- l += '.';
- l += column_qname (*im, "value", "value");
- l += "=";
- l += quote_id (lt);
- l += '.';
- l += column_qname (*id_member (*e.vo->obj));
+ semantics::data_member& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (*im, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
}
}
else
@@ -4207,58 +4770,91 @@ namespace relational
{
// container.value = pointed-to.id
//
- l = ct;
- l += '.';
- l += column_qname (m, "value", "value");
- l += "=";
- l += quote_id (rt);
- l += '.';
- l += column_qname (*id_member (*vo->obj));
+ 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
//
- l = ct;
- l += '.';
- l += column_qname (m, "id", "object_id");
- l += "=";
- l += quote_id (lt);
- l += '.';
- l += column_qname (*id_member (*e.vo->obj));
+ 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);
+
+ os << "r += " << strlit (l) << ";";
+ }
}
else
{
+ string col_prefix;
+
+ if (im == 0)
+ col_prefix =
+ object_columns_base::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 = quote_id (lt);
- l += '.';
- l += column_qname (*id_member (*e.vo->obj));
- l += " = ";
- l += quote_id (rt);
- l += '.';
- l += column_qname (*im);
+ l_cols->traverse (*id_member (*e.vo->obj));
+ r_cols->traverse (*im);
}
else
{
// our.pointer = pointed-to.id
//
- l = quote_id (lt);
+ 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 += column_qname (e.member_path);
- l += " = ";
+ l += quote_id (i->name);
+ l += '=';
l += quote_id (rt);
l += '.';
- l += column_qname (*id_member (*vo->obj));
+ l += quote_id (j->name);
+
+ os << "r += " << strlit (l) << ";";
}
}
- os << "r += " << strlit (l) << ";"
- << endl;
+ os << endl;
}
// Generate the query condition.
@@ -4499,7 +5095,7 @@ namespace relational
// init (value, image)
//
os << "void " << traits << "::" << endl
- << "init (value_type& o, const image_type& i, database& db)"
+ << "init (value_type& o, const image_type& i, database* db)"
<< "{"
<< "ODB_POTENTIALLY_UNUSED (o);"
<< "ODB_POTENTIALLY_UNUSED (i);"
@@ -4588,8 +5184,12 @@ namespace relational
<< "#include <odb/" << db << "/connection.hxx>" << endl
<< "#include <odb/" << db << "/statement.hxx>" << endl
<< "#include <odb/" << db << "/statement-cache.hxx>" << endl
- << "#include <odb/" << db << "/object-statements.hxx>" << endl
- << "#include <odb/" << db << "/container-statements.hxx>" << endl
+ << "#include <odb/" << db << "/object-statements.hxx>" << endl;
+
+ if (options.generate_query ())
+ os << "#include <odb/" << db << "/view-statements.hxx>" << endl;
+
+ os << "#include <odb/" << db << "/container-statements.hxx>" << endl
<< "#include <odb/" << db << "/exceptions.hxx>" << endl;
if (options.generate_query ())
diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx
index 480b69a..7164ef3 100644
--- a/odb/relational/sqlite/common.cxx
+++ b/odb/relational/sqlite/common.cxx
@@ -16,91 +16,10 @@ namespace relational
// member_base
//
- void member_base::
- traverse (semantics::data_member& m)
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
{
- if (transient (m))
- return;
-
- string var;
-
- if (!var_override_.empty ())
- var = var_override_;
- else
- {
- string const& name (m.name ());
- var = name + (name[name.size () - 1] == '_' ? "" : "_");
- }
-
- bool cq (type_override_ != 0 ? false : const_type (m.type ()));
- semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m));
-
- semantics::type* cont;
- if (semantics::class_* c = composite_wrapper (t))
- {
- // If t is a wrapper, pass the wrapped type. Also pass the
- // original, wrapper type.
- //
- member_info mi (m,
- *c,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_composite (mi);
- post (mi);
- }
- }
- // This cannot be a container if we have a type override.
- //
- else if (type_override_ == 0 && (cont = context::container (m)))
- {
- // The same unwrapping logic as for composite values.
- //
- member_info mi (m,
- *cont,
- (wrapper (t) ? &t : 0),
- cq,
- var,
- fq_type_override_);
- if (pre (mi))
- {
- traverse_container (mi);
- post (mi);
- }
- }
- else
- {
- sql_type const& st (column_sql_type (m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (t))
- {
- member_info mi (m,
- utype (*id_member (*c)),
- 0,
- cq,
- var,
- fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_object_pointer (mi);
- post (mi);
- }
- }
- else
- {
- member_info mi (m, t, 0, cq, var, fq_type_override_);
- mi.st = &st;
- if (pre (mi))
- {
- traverse_simple (mi);
- post (mi);
- }
- }
- }
+ return parse_sql_type (column_type (m, key_prefix_), m);
}
void member_base::
@@ -185,10 +104,18 @@ namespace relational
//
member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_database_type_id::
member_database_type_id (semantics::type* type,
- string const& fq_type,
- string const& key_prefix)
- : relational::member_base (type, fq_type, key_prefix)
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, fq_type, key_prefix), // virtual base
+ base (type, fq_type, key_prefix)
{
}
@@ -230,6 +157,8 @@ namespace relational
type_id_ = "sqlite::id_blob";
}
+ entry<member_database_type_id> member_database_type_id_;
+
//
// query_columns
//
diff --git a/odb/relational/sqlite/common.hxx b/odb/relational/sqlite/common.hxx
index 7885a04..941e200 100644
--- a/odb/relational/sqlite/common.hxx
+++ b/odb/relational/sqlite/common.hxx
@@ -12,118 +12,18 @@ namespace relational
{
namespace sqlite
{
- struct member_base: virtual relational::member_base, context
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
{
- member_base (base const& x): base (x) {}
+ member_base (base const& x): base (x), base_impl (x) {}
// This c-tor is for the direct use inside the sqlite namespace.
// If you do use this c-tor, you should also explicitly call
- // relational::member_base.
+ // relational::member_base (aka base).
//
member_base () {}
- virtual void
- traverse (semantics::data_member& m);
-
- struct member_info
- {
- semantics::data_member& m; // Member.
- semantics::type& t; // Cvr-unqualified member C++ type, note
- // that m.type () may not be the same as t.
- semantics::type* wrapper; // Wrapper type if member is a composite or
- // container wrapper, also cvr-unqualified.
- // In this case t is the wrapped type.
- bool cq; // True if the original (wrapper) type
- // is const-qualified.
- sql_type const* st; // Member SQL type (only simple values).
- string& var; // Member variable name with trailing '_'.
-
- // C++ type fq-name.
- //
- string
- fq_type (bool unwrap = true) const
- {
- semantics::names* hint;
-
- if (wrapper != 0 && unwrap)
- {
- // Use the hint from the wrapper unless the wrapped type
- // is qualified.
- //
- hint = wrapper->get<semantics::names*> ("wrapper-hint");
- utype (*context::wrapper (*wrapper), hint);
- return t.fq_name (hint);
- }
-
- // Use the original type from 'm' instead of 't' since the hint
- // may be invalid for a different type. Plus, if a type is
- // overriden, then the fq_type must be as well.
- //
- if (fq_type_.empty ())
- {
- semantics::type& t (utype (m, hint));
- return t.fq_name (hint);
- }
- else
- return fq_type_;
- }
-
- string const& fq_type_;
-
- member_info (semantics::data_member& m_,
- semantics::type& t_,
- semantics::type* wrapper_,
- bool cq_,
- string& var_,
- string const& fq_type)
- : m (m_),
- t (t_),
- wrapper (wrapper_),
- cq (cq_),
- st (0),
- var (var_),
- fq_type_ (fq_type)
- {
- }
- };
-
- bool
- container (member_info& mi)
- {
- // This cannot be a container if we have a type override.
- //
- return type_override_ == 0 && context::container (mi.m);
- }
-
- // The false return value indicates that no further callbacks
- // should be called for this member.
- //
- virtual bool
- pre (member_info&)
- {
- return true;
- }
-
- virtual void
- post (member_info&)
- {
- }
-
- virtual void
- traverse_composite (member_info&)
- {
- }
-
- virtual void
- traverse_container (member_info&)
- {
- }
-
- virtual void
- traverse_object_pointer (member_info& mi)
- {
- traverse_simple (mi);
- }
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
virtual void
traverse_simple (member_info&);
@@ -182,12 +82,16 @@ namespace relational
string type_;
};
- struct member_database_type_id: member_base
+ struct member_database_type_id: relational::member_database_type_id,
+ 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 ());
- string
+
+ virtual string
database_type_id (type&);
virtual void
diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx
index b04dcb9..46689bd 100644
--- a/odb/relational/sqlite/context.cxx
+++ b/odb/relational/sqlite/context.cxx
@@ -365,17 +365,20 @@ namespace relational
}
sql_type const& context::
- column_sql_type (semantics::data_member& m, string const& kp)
+ parse_sql_type (string const& t, semantics::data_member& m)
{
- string key (kp.empty ()
- ? string ("sqlite-column-sql-type")
- : "sqlite-" + kp + "-column-sql-type");
+ // If this proves to be too expensive, we can maintain a
+ // cache of parsed types.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
- if (!m.count (key))
+ if (i != data_->sql_type_cache_.end ())
+ return i->second;
+ else
{
try
{
- m.set (key, parse_sql_type (column_type (m, kp)));
+ return (data_->sql_type_cache_[t] = parse_sql_type (t));
}
catch (invalid_sql_type const& e)
{
@@ -385,8 +388,6 @@ namespace relational
throw operation_failed ();
}
}
-
- return m.get<sql_type> (key);
}
sql_type context::
diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx
index e9f755c..4123b0c 100644
--- a/odb/relational/sqlite/context.hxx
+++ b/odb/relational/sqlite/context.hxx
@@ -5,6 +5,8 @@
#ifndef ODB_RELATIONAL_SQLITE_CONTEXT_HXX
#define ODB_RELATIONAL_SQLITE_CONTEXT_HXX
+#include <map>
+
#include <odb/relational/context.hxx>
namespace relational
@@ -33,8 +35,7 @@ namespace relational
{
public:
sql_type const&
- column_sql_type (semantics::data_member&,
- string const& key_prefix = string ());
+ parse_sql_type (string const&, semantics::data_member&);
public:
struct invalid_sql_type
@@ -87,6 +88,9 @@ namespace relational
struct data: base_context::data
{
data (std::ostream& os): base_context::data (os) {}
+
+ typedef std::map<string, sql_type> sql_type_cache;
+ sql_type_cache sql_type_cache_;
};
data* data_;
diff --git a/odb/relational/sqlite/model.cxx b/odb/relational/sqlite/model.cxx
index 60522a3..41d5d6c 100644
--- a/odb/relational/sqlite/model.cxx
+++ b/odb/relational/sqlite/model.cxx
@@ -28,7 +28,7 @@ namespace relational
{
// Make sure the column is mapped to INTEGER.
//
- if (column_sql_type (m).type != sql_type::INTEGER)
+ if (parse_sql_type (column_type (), m).type != sql_type::INTEGER)
{
cerr << m.file () << ":" << m.line () << ":" << m.column ()
<< ": error: column with default value specified as C++ "
diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx
index 024694f..c170904 100644
--- a/odb/relational/sqlite/source.cxx
+++ b/odb/relational/sqlite/source.cxx
@@ -269,173 +269,21 @@ namespace relational
// init image
//
- struct init_image_member: relational::init_image_member, member_base
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
{
init_image_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- // Ignore containers (they get their own table) and inverse
- // object pointers (they are not present in this binding).
- //
- if (container (mi) || inverse (mi.m, key_prefix_))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- // If we are generating standard init() and this member
- // contains version, ignore it.
- //
- if (version (mi.m))
- return false;
-
- string const& name (mi.m.name ());
- member = "o." + name;
-
- os << "// " << name << endl
- << "//" << endl;
-
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk == statement_insert)";
- }
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "get_ref (" + member + ")";
- }
-
- if (composite (mi.t))
- {
- os << "{";
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- }
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;";
-
- if (weak_pointer (mt))
- {
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > wptr_traits;"
- << "typedef pointer_traits< wptr_traits::" <<
- "strong_pointer_type > ptr_traits;"
- << endl
- << "wptr_traits::strong_pointer_type sp (" <<
- "wptr_traits::lock (" << member << "));";
-
- member = "sp";
- }
- else
- os << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl;
-
- os << "i." << mi.var << "null = ptr_traits::null_ptr (" <<
- member << ");"
- << "if (!i." << mi.var << "null)"
- << "{"
- << "const " << type << "& id (" << endl;
-
- if (lazy_pointer (mt))
- os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
- member << ")";
- else
- os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
-
- os << ");"
- << endl;
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- os << "{";
- }
-
- traits = "sqlite::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- os << "}";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- if (object_pointer (member_utype (mi.m, key_prefix_)))
- {
- os << "}";
-
- if (!null (mi.m, key_prefix_))
- os << "else" << endl
- << "throw null_pointer ();";
- }
-
- os << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ set_null (member_info& mi)
{
- os << "if (" << traits << "::init (" << endl
- << "i." << mi.var << "value," << endl
- << member << "," << endl
- << "sk))" << endl
- << "grew = true;";
+ os << "i." << mi.var << "null = true;";
}
virtual void
@@ -443,8 +291,9 @@ namespace relational
{
os << traits << "::set_image (" << endl
<< "i." << mi.var << "value," << endl
- << "i." << mi.var << "null," << endl
- << member << ");";
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
@@ -452,8 +301,9 @@ namespace relational
{
os << traits << "::set_image (" << endl
<< "i." << mi.var << "value," << endl
- << "i." << mi.var << "null," << endl
- << member << ");";
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
}
virtual void
@@ -463,18 +313,11 @@ namespace relational
<< traits << "::set_image (" << endl
<< "i." << mi.var << "value," << endl
<< "i." << mi.var << "size," << endl
- << "i." << mi.var << "null," << endl
+ << "is_null," << endl
<< member << ");"
+ << "i." << mi.var << "null = is_null;"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
-
- private:
- string type;
- string db_type_id;
- string member;
- string traits;
-
- member_database_type_id member_database_type_id_;
};
entry<init_image_member> init_image_member_;
@@ -482,147 +325,21 @@ namespace relational
// init value
//
- struct init_value_member: relational::init_value_member, member_base
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
{
init_value_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_base (x),
- member_database_type_id_ (base::type_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- string const& name (mi.m.name ());
- member = "o." + name;
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
-
- os << "// " << name << endl
- << "//" << endl;
- }
-
- // If this is a wrapped composite value, then we need to
- // "unwrap" it. For simple values this is taken care of
- // by the value_traits specializations.
- //
- if (mi.wrapper != 0 && composite (mi.t))
- {
- // Here we need the wrapper type, not the wrapped type.
- //
- member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
- "set_ref (\n" + member + ")";
- }
-
- if (composite (mi.t))
- traits = "composite_value_traits< " + mi.fq_type () + " >";
- else
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (semantics::class_* c = object_pointer (mt))
- {
- type = "obj_traits::id_type";
- db_type_id = member_database_type_id_.database_type_id (mi.m);
-
- // Handle NULL pointers and extract the id.
- //
- os << "{"
- << "typedef object_traits< " << class_fq_name (*c) <<
- " > obj_traits;"
- << "typedef pointer_traits< " << mi.fq_type () <<
- " > ptr_traits;"
- << endl
- << "if (i." << mi.var << "null)" << endl;
-
- if (null (mi.m, key_prefix_))
- os << member << " = ptr_traits::pointer_type ();";
- else
- os << "throw null_pointer ();";
-
- os << "else"
- << "{"
- << type << " id;";
-
- member = "id";
- }
- else
- {
- type = mi.fq_type ();
- db_type_id = member_database_type_id_.database_type_id (mi.m);
- }
-
- traits = "sqlite::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
{
- if (composite (mi.t))
- return;
-
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& mt (member_utype (mi.m, key_prefix_));
-
- if (object_pointer (mt))
- {
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- member = "o." + mi.m.name ();
-
- if (mi.cq)
- member = "const_cast< " + mi.fq_type (false) + "& > (" +
- member + ")";
- }
-
- if (lazy_pointer (mt))
- os << member << " = ptr_traits::pointer_type (db, id);";
- else
- os << "// If a compiler error points to the line below, then" << endl
- << "// it most likely means that a pointer used in a member" << endl
- << "// cannot be initialized from an object pointer." << endl
- << "//" << endl
- << member << " = ptr_traits::pointer_type (" << endl
- << "db.load< obj_traits::object_type > (id));";
-
- os << "}"
- << "}";
- }
}
virtual void
- traverse_composite (member_info& mi)
+ get_null (member_info& mi)
{
- os << traits << "::init (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "db);"
- << endl;
+ os << "i." << mi.var << "null";
}
virtual void
@@ -655,14 +372,6 @@ namespace relational
<< "i." << mi.var << "null);"
<< endl;
}
-
- private:
- string type;
- string db_type_id;
- string traits;
- string member;
-
- member_database_type_id member_database_type_id_;
};
entry<init_value_member> init_value_member_;
@@ -680,7 +389,6 @@ namespace relational
};
entry<container_traits> container_traits_;
-
struct class_: relational::class_, context
{
class_ (base const& x): base (x) {}
diff --git a/odb/validator.cxx b/odb/validator.cxx
index b8f7c91..c051487 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -42,7 +42,7 @@ namespace
// Pass 1.
//
- struct data_member: traversal::data_member
+ struct data_member: traversal::data_member, context
{
data_member (bool& valid)
: valid_ (valid)
@@ -52,45 +52,42 @@ namespace
virtual void
traverse (type& m)
{
- if (context::transient (m))
+ if (transient (m))
return;
count_++;
semantics::names* hint;
- semantics::type& t (context::utype (m, hint));
+ semantics::type& t (utype (m, hint));
if (t.fq_anonymous (hint))
{
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: unnamed type in data member declaration" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unnamed type in data member declaration" << endl;
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " info: use 'typedef' to name this type" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use 'typedef' to name this type" << endl;
valid_ = false;
}
// Make sure id or inverse member is not marked readonly since we
- // depend on these three sets not having overlaps. Once we support
- // composite ids, we will also need to make sure there are no
- // nested readonly members (probably move it to pass 2 and use
- // column_count()).
+ // depend on these three sets not having overlaps.
//
- if (m.count ("readonly"))
+ if (readonly (m))
{
- if (m.count ("id"))
+ if (id (m))
{
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: object id should not be declared readonly" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: object id should not be declared readonly" << endl;
valid_ = false;
}
- if (m.count ("inverse"))
+ if (inverse (m))
{
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: inverse object pointer should not be declared "
- << "readonly" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse object pointer should not be declared "
+ << "readonly" << endl;
valid_ = false;
}
@@ -108,9 +105,9 @@ namespace
// Find special members (id, version).
//
- struct special_members: traversal::class_
+ struct special_members: traversal::class_, context
{
- special_members (class_kind kind,
+ special_members (class_kind_type kind,
bool& valid,
semantics::data_member*& id,
semantics::data_member*& optimistic)
@@ -131,7 +128,7 @@ namespace
{
case class_object:
{
- if (!context::object (c))
+ if (!object (c))
return;
break;
}
@@ -141,7 +138,7 @@ namespace
}
case class_composite:
{
- if (!context::composite (c))
+ if (!composite (c))
return;
break;
}
@@ -161,7 +158,7 @@ namespace
}
private:
- struct member: traversal::data_member
+ struct member: traversal::data_member, context
{
member (bool& valid,
semantics::data_member*& id,
@@ -173,17 +170,15 @@ namespace
virtual void
traverse (semantics::data_member& m)
{
- if (m.count ("id"))
+ if (id (m))
{
if (id_ != 0)
{
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: multiple object id members" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple object id members" << endl;
- semantics::data_member& i (*id_);
-
- cerr << i.file () << ":" << i.line () << ":" << i.column ()
- << ": info: previous id member is declared here" << endl;
+ os << id_->file () << ":" << id_->line () << ":" << id_->column ()
+ << ": info: previous id member is declared here" << endl;
valid_ = false;
}
@@ -191,17 +186,17 @@ namespace
id_ = &m;
}
- if (m.count ("version"))
+ if (version (m))
{
if (optimistic_ != 0)
{
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: multiple version members" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple version members" << endl;
semantics::data_member& o (*optimistic_);
- cerr << o.file () << ":" << o.line () << ":" << o.column ()
- << ": info: previous version member is declared here" << endl;
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": info: previous version member is declared here" << endl;
valid_ = false;
}
@@ -215,7 +210,7 @@ namespace
semantics::data_member*& optimistic_;
};
- class_kind kind_;
+ class_kind_type kind_;
member member_;
traversal::names names_;
traversal::inherits inherits_;
@@ -223,7 +218,7 @@ namespace
//
//
- struct value_type: traversal::type
+ struct value_type: traversal::type, context
{
value_type (bool& valid): valid_ (valid) {}
@@ -241,17 +236,10 @@ namespace
//
//
- struct class1: traversal::class_
+ struct class1: traversal::class_, context
{
- class1 (bool& valid,
- options const& ops,
- semantics::unit& unit,
- value_type& vt)
- : valid_ (valid),
- options_ (ops),
- unit_ (unit),
- vt_ (vt),
- member_ (valid)
+ class1 (bool& valid, value_type& vt)
+ : valid_ (valid), vt_ (vt), member_ (valid)
{
*this >> names_ >> member_;
}
@@ -259,13 +247,13 @@ namespace
virtual void
traverse (type& c)
{
- if (context::object (c))
+ if (object (c))
traverse_object (c);
- else if (context::view (c))
+ else if (view (c))
traverse_view (c);
else
{
- if (context::composite (c))
+ if (composite (c))
traverse_composite (c);
vt_.dispatch (c);
@@ -286,10 +274,10 @@ namespace
if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ": "
- << "error: unable to resolve member function '" << name << "' "
- << "specified with '#pragma db callback' for class '"
- << context::class_name (c) << "'" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
valid_ = false;
}
@@ -324,25 +312,25 @@ namespace
{
type& b (i->base ());
- if (context::object (b))
+ if (object (b))
base = true;
- else if (context::view (b) || context::composite (b))
+ else if (view (b) || composite (b))
{
// @@ Should we use hint here?
//
- string name (context::class_fq_name (b));
+ string name (class_fq_name (b));
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is a view or value type"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or value type"
+ << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: object types cannot derive from view or value "
- << "types"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object types cannot derive from view or value "
+ << "types"
+ << endl;
- cerr << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
valid_ = false;
}
@@ -359,21 +347,21 @@ namespace
if (id == 0)
{
- // An object without an id should either be abstract or
- // explicitly marked as such.
+ // An object without an id should either be abstract or explicitly
+ // marked as such.
//
- if (!(c.count ("id") || context::abstract (c)))
+ if (!(c.count ("id") || abstract (c)))
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no data member designated as an object id" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no data member designated as an object id" << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db id' to specify an object id member"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db id' to specify an object id member"
+ << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: or explicitly declare that this persistent class "
- << "has no object id" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: or explicitly declare that this persistent class "
+ << "has no object id" << endl;
valid_ = false;
}
@@ -387,9 +375,9 @@ namespace
//
if (id->count ("default"))
{
- cerr << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: object id member cannot have default value"
- << endl;
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: object id member cannot have default value"
+ << endl;
valid_ = false;
}
@@ -399,8 +387,8 @@ namespace
//
if (id->count ("null"))
{
- cerr << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: object id member cannot be null" << endl;
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: object id member cannot be null" << endl;
valid_ = false;
}
@@ -416,13 +404,13 @@ namespace
//
if (&optimistic->scope () == &c && !c.count ("optimistic"))
{
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: version data member in a class not declared "
- << "optimistic" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version data member in a class not declared "
+ << "optimistic" << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db optimistic' to declare this "
- << "class optimistic" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db optimistic' to declare this "
+ << "class optimistic" << endl;
valid_ = false;
}
@@ -431,8 +419,8 @@ namespace
//
if (id == 0)
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class without an object id" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without an object id" << endl;
valid_ = false;
}
@@ -442,34 +430,33 @@ namespace
//
if (id != 0 && &id->scope () != &optimistic->scope ())
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: object id and version members are in different "
- << "classes" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: object id and version members are in different "
+ << "classes" << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: object id and version members must be in the same "
- << "class" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object id and version members must be in the same "
+ << "class" << endl;
- cerr << id->file () << ":" << id->line () << ":" << id->column ()
- << ": info: object id member is declared here" << endl;
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": info: object id member is declared here" << endl;
- cerr << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: version member is declared here" << endl;
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version member is declared here" << endl;
valid_ = false;
}
// Make sure this class is not readonly.
//
- if (c.count ("readonly"))
+ if (readonly (c))
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class cannot be readonly" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class cannot be readonly" << endl;
valid_ = false;
}
-
// This takes care of also marking derived classes as optimistic.
//
c.set ("optimistic-member", optimistic);
@@ -481,12 +468,12 @@ namespace
//
if (c.count ("optimistic"))
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class without a version member" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without a version member" << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db version' to declare on of the "
- << "data members as a version" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db version' to declare on of the "
+ << "data members as a version" << endl;
valid_ = false;
}
@@ -499,8 +486,8 @@ namespace
if (member_.count_ == 0 && !base)
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no persistent data members in the class" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
valid_ = false;
}
@@ -511,16 +498,16 @@ namespace
{
// Views require query support.
//
- if (!options_.generate_query ())
+ if (!options.generate_query ())
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: query support is required when using views"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: query support is required when using views"
+ << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use the --generate-query option to enable query "
- << "support"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use the --generate-query option to enable query "
+ << "support"
+ << endl;
valid_ = false;
}
@@ -533,26 +520,24 @@ namespace
{
type& b (i->base ());
- if (context::object (b) ||
- context::view (b) ||
- context::composite (b))
+ if (object (b) || view (b) || composite (b))
{
// @@ Should we use hint here?
//
- string name (context::class_fq_name (b));
+ string name (class_fq_name (b));
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is an object, "
- << "view, or value type"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is an object, "
+ << "view, or value type"
+ << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: view types cannot derive from view, object or "
- << "value types"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: view types cannot derive from view, object or "
+ << "value types"
+ << endl;
- cerr << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
valid_ = false;
}
@@ -569,9 +554,9 @@ namespace
if (id != 0)
{
- cerr << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: view type data member cannot be designated as an "
- << "object id" << endl;
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: view type data member cannot be designated as an "
+ << "object id" << endl;
valid_ = false;
}
@@ -580,9 +565,9 @@ namespace
{
semantics::data_member& o (*optimistic);
- cerr << o.file () << ":" << o.line () << ":" << o.column ()
- << ": error: view type data member cannot be designated as a "
- << "version" << endl;
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: view type data member cannot be designated as a "
+ << "version" << endl;
valid_ = false;
}
@@ -594,8 +579,8 @@ namespace
if (member_.count_ == 0)
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no persistent data members in the class" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
valid_ = false;
}
@@ -612,25 +597,25 @@ namespace
{
type& b (i->base ());
- if (context::composite (b))
+ if (composite (b))
base = true;
- else if (context::object (b) || context::view (b))
+ else if (object (b) || view (b))
{
// @@ Should we use hint here?
//
- string name (context::class_fq_name (b));
+ string name (class_fq_name (b));
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is a view or object "
- << "type"
- << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or object "
+ << "type"
+ << endl;
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: composite value types cannot derive from object "
- << "or view types" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: composite value types cannot derive from object "
+ << "or view types" << endl;
- cerr << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
valid_ = false;
}
@@ -647,9 +632,9 @@ namespace
if (id != 0)
{
- cerr << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: value type data member cannot be designated as an "
- << "object id" << endl;
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: value type data member cannot be designated as an "
+ << "object id" << endl;
valid_ = false;
}
@@ -658,9 +643,9 @@ namespace
{
semantics::data_member& o (*optimistic);
- cerr << o.file () << ":" << o.line () << ":" << o.column ()
- << ": error: value type data member cannot be designated as a "
- << "version" << endl;
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: value type data member cannot be designated as a "
+ << "version" << endl;
valid_ = false;
}
@@ -672,16 +657,14 @@ namespace
if (member_.count_ == 0 && !base)
{
- cerr << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no persistent data members in the class" << endl;
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
valid_ = false;
}
}
bool& valid_;
- options const& options_;
- semantics::unit& unit_;
value_type& vt_;
data_member member_;
@@ -720,15 +703,80 @@ namespace
}
virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ if (inverse (m))
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: inverse object pointer member '" << member_prefix_
+ << m.name () << "' in an object without an object id" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: container member '" << member_prefix_ << m.name ()
+ << "' in an object without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ semantics::data_member* old_dm (dm_);
+
+ if (dm_ == 0)
+ dm_ = m;
+
+ object_members_base::traverse_composite (m, c);
+
+ dm_ = old_dm;
+ }
+
+ private:
+ bool& valid_;
+ semantics::data_member* dm_; // Direct object data member.
+ };
+
+ struct composite_id_members: object_members_base
+ {
+ composite_id_members (bool& valid)
+ : object_members_base (false, false, true), valid_ (valid), dm_ (0)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: object pointer member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id" << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
traverse_simple (semantics::data_member& m)
{
- if (m.count ("inverse"))
+ if (readonly (member_path_, member_scope_))
{
semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
- cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
- << " error: inverse object pointer member '" << member_prefix_
- << m.name () << "' in an object without an object id" << endl;
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: readonly member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id"
+ << endl;
valid_ = false;
}
@@ -739,9 +787,9 @@ namespace
{
semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
- cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
- << " error: container member '" << member_prefix_ << m.name ()
- << "' in an object without an object id" << endl;
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: container member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id" << endl;
valid_ = false;
}
@@ -761,7 +809,7 @@ namespace
private:
bool& valid_;
- semantics::data_member* dm_; // Direct view data member.
+ semantics::data_member* dm_; // Direct composite member.
};
struct view_members: object_members_base
@@ -774,16 +822,16 @@ namespace
virtual void
traverse_simple (semantics::data_member& m)
{
- if (context::object_pointer (utype (m)))
+ if (object_pointer (utype (m)))
{
semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
- cerr << 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 () << ":"
+ << " error: view data member '" << member_prefix_ << m.name ()
+ << "' is an object pointer" << endl;
- cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
- << ": info: views cannot contain object pointers" << endl;
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << ": info: views cannot contain object pointers" << endl;
valid_ = false;
}
@@ -794,12 +842,12 @@ namespace
{
semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
- cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
- << " error: view data member '" << member_prefix_ << m.name ()
- << "' is a container" << endl;
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: view data member '" << member_prefix_ << m.name ()
+ << "' is a container" << endl;
- cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
- << ": info: views cannot contain containers" << endl;
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << ": info: views cannot contain containers" << endl;
valid_ = false;
}
@@ -824,36 +872,175 @@ namespace
//
//
- struct class2: traversal::class_
+ struct class2: traversal::class_, context
{
- class2 (bool& valid, options const& ops, semantics::unit& unit)
+ class2 (bool& valid)
: valid_ (valid),
- options_ (ops),
- unit_ (unit),
object_no_id_members_ (valid),
+ composite_id_members_ (valid),
view_members_ (valid)
{
+ // Find the has_lt_operator function template..
+ //
+ has_lt_operator_ = 0;
+
+ tree odb (
+ lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false));
+
+ if (odb != error_mark_node)
+ {
+ tree compiler (
+ lookup_qualified_name (
+ odb, get_identifier ("compiler"), false, false));
+
+ if (compiler != error_mark_node)
+ {
+ has_lt_operator_ = lookup_qualified_name (
+ compiler, get_identifier ("has_lt_operator"), false, false);
+
+ if (has_lt_operator_ != error_mark_node)
+ has_lt_operator_ = OVL_CURRENT (has_lt_operator_);
+ else
+ {
+ os << unit.file () << ": error: unable to resolve has_lt_operator "
+ << "function template inside odb::compiler" << endl;
+ has_lt_operator_ = 0;
+ }
+ }
+ else
+ os << unit.file () << ": error: unable to resolve compiler "
+ << "namespace inside odb" << endl;
+ }
+ else
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ if (has_lt_operator_ == 0)
+ valid_ = false;
}
virtual void
traverse (type& c)
{
- if (context::object (c))
+ if (object (c))
traverse_object (c);
- else if (context::view (c))
+ else if (view (c))
traverse_view (c);
- else if (context::composite (c))
+ else if (composite (c))
traverse_composite (c);
}
virtual void
traverse_object (type& c)
{
- if (context::id_member (c) == 0 && !context::abstract (c))
+ semantics::data_member* id (id_member (c));
+
+ if (id != 0)
{
- // Make sure we don't have any containers or inverse pointers.
- //
- object_no_id_members_.traverse (c);
+ if (semantics::class_* c = composite_wrapper (utype (*id)))
+ {
+ // Composite id cannot be auto.
+ //
+ if (auto_ (*id))
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: composite id cannot be automatically assigned"
+ << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure we don't have any containers or pointers in this
+ // composite value type.
+ //
+ if (valid_)
+ {
+ composite_id_members_.traverse (*c);
+
+ if (!valid_)
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": info: composite id is defined here" << endl;
+ }
+
+ // Check that the composite value type is default-constructible.
+ //
+ if (!c->default_ctor ())
+ {
+ os << c->file () << ":" << c->line () << ":" << c->column ()
+ << ": error: composite value type that is used as object id "
+ << "is not default-constructible" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column ()
+ << ": info: provide default constructor for this value type"
+ << endl;
+
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": info: composite id is defined here" << endl;
+
+ valid_ = false;
+ }
+
+ // Check that composite values can be compared (used in session).
+ //
+ if (has_lt_operator_ != 0)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = c->tree_node ();
+
+ tree inst (
+ instantiate_template (
+ has_lt_operator_, args, tf_none));
+
+ bool v (inst != error_mark_node);
+
+ if (v &&
+ DECL_TEMPLATE_INSTANTIATION (inst) &&
+ !DECL_TEMPLATE_INSTANTIATED (inst))
+ {
+ // Instantiate this function template to see if the value type
+ // provides operator<. Unfortunately, GCC instantiate_decl()
+ // does not provide any control over the diagnostics it issues
+ // in case of an error. To work around this, we are going to
+ // temporarily redirect diagnostics to /dev/null, which is
+ // where asm_out_file points to (see plugin.cxx).
+ //
+ int ec (errorcount);
+ FILE* s (global_dc->printer->buffer->stream);
+ global_dc->printer->buffer->stream = asm_out_file;
+
+ instantiate_decl (inst, false, false);
+
+ global_dc->printer->buffer->stream = s;
+ v = (ec == errorcount);
+ }
+
+ if (!v)
+ {
+ os << c->file () << ":" << c->line () << ":" << c->column ()
+ << ": error: composite value type that is used as object id "
+ << "does not support the less than (<) comparison"
+ << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column ()
+ << ": info: provide operator< for this value type" << endl;
+
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": info: composite id is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!abstract (c))
+ {
+ // Make sure we don't have any containers or inverse pointers.
+ //
+ object_no_id_members_.traverse (c);
+ }
}
}
@@ -871,10 +1058,10 @@ namespace
}
bool& valid_;
- options const& options_;
- semantics::unit& unit_;
+ tree has_lt_operator_;
object_no_id_members object_no_id_members_;
+ composite_id_members composite_id_members_;
view_members view_members_;
};
}
@@ -897,7 +1084,7 @@ validate (options const& ops,
typedefs1 unit_typedefs (unit_declares);
traversal::namespace_ ns;
value_type vt (valid);
- class1 c (valid, ops, u, vt);
+ class1 c (valid, vt);
unit >> unit_defines >> ns;
unit_defines >> c;
@@ -921,7 +1108,7 @@ validate (options const& ops,
traversal::defines unit_defines;
typedefs unit_typedefs (true);
traversal::namespace_ ns;
- class2 c (valid, ops, u);
+ class2 c (valid);
unit >> unit_defines >> ns;
unit_defines >> c;