aboutsummaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-07-11 11:18:35 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-07-11 11:18:35 +0200
commit6cea23266b1c309af6d90e2b0a39fc0778a9acc8 (patch)
tree31455c54a71bf363b7f7c200d97e0c9aae10b730 /doc
parentb8554760aa3a5c5697c77d11e507a2bb46dbf8e4 (diff)
Document custom database type mapping support
Diffstat (limited to 'doc')
-rw-r--r--doc/manual.xhtml277
1 files changed, 252 insertions, 25 deletions
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
index 095f540..39ba2b5 100644
--- a/doc/manual.xhtml
+++ b/doc/manual.xhtml
@@ -541,14 +541,17 @@ for consistency.
</td>
</tr>
<tr>
- <th>12.6</th><td><a href="#12.6">C++ Compiler Warnings</a>
+ <th>12.6</th><td><a href="#12.6">Database Type Mapping Pragmas</a></td>
+ </tr>
+ <tr>
+ <th>12.7</th><td><a href="#12.7">C++ Compiler Warnings</a>
<table class="toc">
- <tr><th>12.6.1</th><td><a href="#12.6.1">GNU C++</a></td></tr>
- <tr><th>12.6.2</th><td><a href="#12.6.2">Visual C++</a></td></tr>
- <tr><th>12.6.3</th><td><a href="#12.6.3">Sun C++</a></td></tr>
- <tr><th>12.6.4</th><td><a href="#12.6.4">IBM XL C++</a></td></tr>
- <tr><th>12.6.5</th><td><a href="#12.6.5">HP aC++</a></td></tr>
- <tr><th>12.6.6</th><td><a href="#12.6.6">Clang</a></td></tr>
+ <tr><th>12.7.1</th><td><a href="#12.7.1">GNU C++</a></td></tr>
+ <tr><th>12.7.2</th><td><a href="#12.7.2">Visual C++</a></td></tr>
+ <tr><th>12.7.3</th><td><a href="#12.7.3">Sun C++</a></td></tr>
+ <tr><th>12.7.4</th><td><a href="#12.7.4">IBM XL C++</a></td></tr>
+ <tr><th>12.7.5</th><td><a href="#12.7.5">HP aC++</a></td></tr>
+ <tr><th>12.7.6</th><td><a href="#12.7.6">Clang</a></td></tr>
</table>
</td>
</tr>
@@ -8045,7 +8048,7 @@ result r (db.query&lt;employee_retirement> ());
words, we may need to combine a constant query expression specified
in the <code>db&nbsp;query</code> pragma with the varying expression
specified at the query execution time. To allow this, the
- <code>db&nbsp;query</code> pragma syntax supports the use of a special
+ <code>db&nbsp;query</code> pragma syntax supports the use of the special
<code>(?)</code> placeholder that indicates the position in the
constant query expression where the runtime expression should be
inserted. For example:</p>
@@ -8869,17 +8872,19 @@ for (bool done (false); !done; )
<p>The <em>qualifier</em> tells the ODB compiler what kind of C++ construct
this pragma describes. Valid qualifiers are <code>object</code>,
- <code>view</code>, <code>value</code>, <code>member</code>, and
- <code>namespace</code>. A pragma with the <code>object</code>
- qualifier describes a persistent object type. It tells the ODB
- compiler that the C++ class it describes is a persistent class.
- Similarly, pragmas with the <code>view</code> qualifier describe
+ <code>view</code>, <code>value</code>, <code>member</code>,
+ <code>namespace</code>, and <code>map</code>. A pragma with the
+ <code>object</code> qualifier describes a persistent object type. It
+ tells the ODB compiler that the C++ class it describes is a persistent
+ class. Similarly, pragmas with the <code>view</code> qualifier describe
view types, the <code>value</code> qualifier describes value types
and the <code>member</code> qualifier is used to describe data
members of persistent object, view, and value types. The
<code>namespace</code> qualifier is used to describe common
properties of objects, views, and value types that belong to
- a C++ namespace.</p>
+ a C++ namespace. The <code>map</code> qualifier describes a
+ mapping between additional database types and types for
+ which ODB provides built-in support.</p>
<p>The <em>specifier</em> informs the ODB compiler about a particular
database-related property of the C++ declaration. For example, the
@@ -9010,9 +9015,8 @@ class person
--odb-epilogue '#include "person-pragmas.hxx"'
</pre>
- <p>The following three sections cover the specifiers applicable
- to the <code>object</code>, <code>value</code>, and <code>member</code>
- qualifiers.</p>
+ <p>The following sections cover the specifiers applicable to all the
+ qualifiers mentioned above.</p>
<p>The C++ header file that defines our persistent classes and
normally contains one or more ODB pragmas is compiled by both
@@ -9020,7 +9024,7 @@ class person
the C++ compiler to build the application. Some C++ compilers
issue warnings about pragmas that they do not recognize. There
are several ways to deal with this problem which are covered
- at the end of this chapter in <a href="#12.6">Section 12.6,
+ at the end of this chapter in <a href="#12.7">Section 12.7,
"C++ Compiler Warnings"</a>.</p>
<h2><a name="12.1">12.1 Object Type Pragmas</a></h2>
@@ -11688,7 +11692,202 @@ namespace hr
"<code>session</code>"</a>). For more information on sessions,
refer to <a href="#10">Chapter 10, "Session"</a>.</p>
- <h2><a name="12.6">12.6 C++ Compiler Warnings</a></h2>
+ <h2><a name="12.6">12.6 Database Type Mapping Pragmas</a></h2>
+
+ <p>A pragma with the <code>map</code> qualifier describes a
+ mapping between two database types. For each database system
+ ODB provides built-in support for a core set of database types,
+ such as integers, strings, binary, etc. However, many database
+ systems provide additional types such as extensions (geospatial,
+ key-value stores, etc.), user-defined types, and collections (arrays,
+ table types, etc). In order to support such additional types, ODB
+ allows us to map them to one of the built-in types, normally
+ a string or a binary. Given the text or binary representation
+ of the data we can then extract it into our chosen C++ data type
+ and thus establish a mapping between an additional database type and
+ its C++ equivalent.</p>
+
+ <p>The <code>map</code> pragma has the following format:</p>
+
+<pre class="cxx">
+#pragma db map type("regex") as("subst") [to("subst")] [from("subst")]
+</pre>
+
+ <p>The <code>type</code> clause specifies the name of the database type
+ that we are mapping. We will refer to it as the <em>mapped type</em>
+ from now on. The name of the mapped type is a Perl-like regular
+ expression pattern that is matched in the case-insensitive mode.</p>
+
+ <p>The <code>as</code> clause specifies the name of the database type
+ that we are mapping the mapped type to. We will refer to it as
+ the <em>interface type</em> from now on. The name of the interface
+ type is a regular expression substitution and should expand to a
+ name of a database type for which ODB provides built-in support.</p>
+
+ <p>The optional <code>to</code> and <code>from</code> clauses specify the
+ database conversion expressions between the mapped type and the
+ interface type. The <code>to</code> expression converts from the
+ interface type to the mapped type and <code>from</code> converts
+ in the other direction. If no explicit conversion is required for
+ either direction, then the corresponding clause can be omitted.
+ The conversion expressions are regular expression substitutions.
+ They must contain the special <code>(?)</code> placeholder which will
+ be replaced with the actual value to be converted. Turning on SQL
+ statement tracing (<a href="#3.12">Section 3.12, "Tracing SQL
+ Statement Execution"</a>) can be useful for debugging conversion
+ expressions. This allows you to see the substituted expressions
+ as used in the actual statements.</p>
+
+ <p>As an example, the following <code>map</code> pragma maps the
+ PostgreSQL array of <code>INTEGER</code>'s to <code>TEXT</code>:</p>
+
+<pre class="cxx">
+#pragma db map type("INTEGER *\\[(\\d*)\\]") \
+ as("TEXT") \
+ to("(?)::INTEGER[$1]") \
+ from("(?)::TEXT")
+</pre>
+
+ <p>With the above mapping we can now have a persistent class that
+ has a member of the PostgreSQL array type:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ #pragma db type("INTEGER[]")
+ std::string array_;
+};
+</pre>
+
+ <p>In PostgreSQL the array literal has the <code>{n1,n2,...}</code> form.
+ As a result, we need to make sure that we pass the correct text
+ representation in the <code>array_</code> member, for example:</p>
+
+<pre class="cxx">
+object o;
+o.array_ = "{1,2,3}";
+db.persist (o);
+</pre>
+
+ <p>Of course, <code>std::string</code> is not the most natural
+ representation of an array of integers in C++. Instead,
+ <code>std::vector&lt;int></code> would have been much more
+ appropriate. To add support for mapping
+ <code>std::vector&lt;int></code> to PostgreSQL <code>INTEGER[]</code>
+ we need to provide a <code>value_traits</code> specialization
+ that implements conversion between the PostgreSQL text representation
+ of an array and <code>std::vector&lt;int></code>. Below is a sample
+ implementation:</p>
+
+<pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ template &lt;>
+ class value_traits&lt;std::vector&lt;int>, id_string>
+ {
+ public:
+ typedef std::vector&lt;int> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type&amp; v,
+ const details::buffer&amp; b,
+ std::size_t n,
+ bool is_null)
+ {
+ v.clear ();
+
+ if (!is_null)
+ {
+ char c;
+ std::istringstream is (std::string (b.data (), n));
+
+ is >> c; // '{'
+
+ for (c = static_cast&lt;char> (is.peek ()); c != '}'; is >> c)
+ {
+ v.push_back (int ());
+ is >> v.back ();
+ }
+ }
+ }
+
+ static void
+ set_image (details::buffer&amp; b,
+ std::size_t&amp; n,
+ bool&amp; is_null,
+ const value_type&amp; v)
+ {
+ is_null = false;
+ std::ostringstream os;
+
+ os &lt;&lt; '{';
+
+ for (value_type::const_iterator i (v.begin ()), e (v.end ());
+ i != e;)
+ {
+ os &lt;&lt; *i;
+
+ if (++i != e)
+ os &lt;&lt; ',';
+ }
+
+ os &lt;&lt; '}';
+
+ const std::string&amp; s (os.str ());
+ n = s.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), s.c_str (), n);
+ }
+ };
+ }
+}
+</pre>
+
+ <p>Once this specialization is included in the generated code (see
+ the <code>mapping</code> example in the <code>odb-examples</code>
+ package for details), we can use <code>std::vector&lt;int></code>
+ instead of <code>std::string</code> in our persistent class:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ #pragma db type("INTEGER[]")
+ std::vector&lt;int> array_;
+};
+</pre>
+
+ <p>If we wanted to always map <code>std::vector&lt;int></code>
+ to PostgreSQL <code>INTEGER[]</code>, then we could instead
+ write:</p>
+
+<pre class="cxx">
+typedef std::vector&lt;int> int_vector;
+#pragma db value(int_vector) type("INTEGER[]")
+
+#pragma db object
+class object
+{
+ std::vector&lt;int> array_; // Mapped to INTEGER[].
+};
+</pre>
+
+ <p>While the above example only shows how to handle PostgreSQL arrays,
+ other types in PostgreSQL and in other databases can be supported
+ in a similar way. The <code>odb-tests</code> package contains a
+ set of tests in the <code>&lt;database>/custom</code> directories that,
+ for each database, shows how to provide custom mapping for some of
+ the additional types.</p>
+
+ <h2><a name="12.7">12.7 C++ Compiler Warnings</a></h2>
<p>When a C++ header file defining persistent classes and containing
ODB pragmas is used to build the application, the C++ compiler may
@@ -11741,7 +11940,7 @@ class person
<p>The disadvantage of this approach is that it can quickly become
overly verbose when positioned pragmas are used.</p>
- <h3><a name="12.6.1">12.6.1 GNU C++</a></h3>
+ <h3><a name="12.7.1">12.7.1 GNU C++</a></h3>
<p>GNU g++ does not issue warnings about unknown pragmas
unless requested with the <code>-Wall</code> command line option.
@@ -11753,7 +11952,7 @@ class person
g++ -Wall -Wno-unknown-pragmas ...
</pre>
- <h3><a name="12.6.2">12.6.2 Visual C++</a></h3>
+ <h3><a name="12.7.2">12.7.2 Visual C++</a></h3>
<p>Microsoft Visual C++ issues an unknown pragma warning (C4068) at
warning level 1 or higher. This means that unless we have disabled
@@ -11787,7 +11986,7 @@ class person
#pragma warning (pop)
</pre>
- <h3><a name="12.6.3">12.6.3 Sun C++</a></h3>
+ <h3><a name="12.7.3">12.7.3 Sun C++</a></h3>
<p>The Sun C++ compiler does not issue warnings about unknown pragmas
unless the <code>+w</code> or <code>+w2</code> option is specified.
@@ -11799,7 +11998,7 @@ class person
CC +w -erroff=unknownpragma ...
</pre>
- <h3><a name="12.6.4">12.6.4 IBM XL C++</a></h3>
+ <h3><a name="12.7.4">12.7.4 IBM XL C++</a></h3>
<p>IBM XL C++ issues an unknown pragma warning (1540-1401) by default.
To disable this warning we can add the <code>-qsuppress=1540-1401</code>
@@ -11809,7 +12008,7 @@ CC +w -erroff=unknownpragma ...
xlC -qsuppress=1540-1401 ...
</pre>
- <h3><a name="12.6.5">12.6.5 HP aC++</a></h3>
+ <h3><a name="12.7.5">12.7.5 HP aC++</a></h3>
<p>HP aC++ (aCC) issues an unknown pragma warning (2161) by default.
To disable this warning we can add the <code>+W2161</code>
@@ -11819,7 +12018,7 @@ xlC -qsuppress=1540-1401 ...
aCC +W2161 ...
</pre>
- <h3><a name="12.6.6">12.6.6 Clang</a></h3>
+ <h3><a name="12.7.6">12.7.6 Clang</a></h3>
<p>Clang does not issue warnings about unknown pragmas
unless requested with the <code>-Wall</code> command line option.
@@ -12076,6 +12275,11 @@ class object
};
</pre>
+ <p>It is also possible to add support for additional MySQL types,
+ such as geospatial types. For more information, refer to
+ <a href="#12.6">Section 12.6, "Database Type Mapping
+ Pragmas"</a>.</p>
+
<h2><a name="13.2">13.2 MySQL Database Class</a></h2>
<p>The MySQL <code>database</code> class has the following
@@ -12689,6 +12893,11 @@ class object
<code>unsigned&nbsp;long&nbsp;long</code> values will be represented in
the database as negative values.</p>
+ <p>It is also possible to add support for additional SQLite types,
+ such as <code>NUMERIC</code>. For more information, refer to
+ <a href="#12.6">Section 12.6, "Database Type Mapping
+ Pragmas"</a>.</p>
+
<h2><a name="14.2">14.2 SQLite Database Class</a></h2>
<p>The SQLite <code>database</code> class has the following
@@ -13442,6 +13651,13 @@ class object
the most significant bit of the actual unsigned value being
persisted.</p>
+ <p>It is also possible to add support for additional PostgreSQL types,
+ such as <code>NUMERIC</code>, geometry types, <code>XML</code>,
+ <code>JSON</code>, enumeration types, composite types, arrays,
+ geospatial types, and the key-value store (<code>HSTORE</code>).
+ For more information, refer to <a href="#12.6">Section 12.6,
+ "Database Type Mapping Pragmas"</a>.</p>
+
<h2><a name="15.2">15.2 PostgreSQL Database Class</a></h2>
<p>The PostgreSQL <code>database</code> class has the following
@@ -14072,6 +14288,12 @@ class object
mapped to <code>NUMBER(10)</code> with the default <code>NULL</code>
semantics being <code>NOT NULL</code>.</p>
+ <p>It is also possible to add support for additional Oracle types,
+ such as <code>XML</code>, geospatial types, user-defined types,
+ and collections (arrays, table types). For more information, refer to
+ <a href="#12.6">Section 12.6, "Database Type Mapping
+ Pragmas"</a>.</p>
+
<h2><a name="16.2">16.2 Oracle Database Class</a></h2>
<p>The Oracle <code>database</code> class encapsulates the OCI environment
@@ -14927,6 +15149,11 @@ t.commit ();
the value stored by the database for these types will contain the
sign bit of the actual signed value being persisted.</p>
+ <p>It is also possible to add support for additional SQL Server types,
+ such as geospatial types, <code>XML</code>, and user-defined types.
+ For more information, refer to <a href="#12.6">Section 12.6, "Database
+ Type Mapping Pragmas"</a>.</p>
+
<h2><a name="17.2">17.2 SQL Server Database Class</a></h2>
<p>The SQL Server <code>database</code> class encapsulates the ODBC