aboutsummaryrefslogtreecommitdiff
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
parentb8554760aa3a5c5697c77d11e507a2bb46dbf8e4 (diff)
Document custom database type mapping support
-rw-r--r--NEWS8
-rw-r--r--doc/manual.xhtml277
2 files changed, 260 insertions, 25 deletions
diff --git a/NEWS b/NEWS
index 3110918..0fdda4f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,13 @@
Version 2.1.0
+ * Support for mapping additional database types, such as geospatial types,
+ user-defined types, and collections. This mechanism allows you to map
+ any database type to one of the types for which ODB provides built-in
+ support (normally string or binary). The text or binary representation
+ of the data can then be extracted into a C++ data type of your choice.
+ For more information, refer to Section 12.6, "Database Type Mapping
+ Pragmas" in the ODB manual.
+
* The session constructor now accepts an options bool argument (true by
default) which indicates whether to make this session current for this
thread. For more information, refer to Chapter 10, "Session" in the ODB
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