From 6cea23266b1c309af6d90e2b0a39fc0778a9acc8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 11 Jul 2012 11:18:35 +0200 Subject: Document custom database type mapping support --- NEWS | 8 ++ doc/manual.xhtml | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 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. - 12.6C++ Compiler Warnings + 12.6Database Type Mapping Pragmas + + + 12.7C++ Compiler Warnings - - - - - - + + + + + +
12.6.1GNU C++
12.6.2Visual C++
12.6.3Sun C++
12.6.4IBM XL C++
12.6.5HP aC++
12.6.6Clang
12.7.1GNU C++
12.7.2Visual C++
12.7.3Sun C++
12.7.4IBM XL C++
12.7.5HP aC++
12.7.6Clang
@@ -8045,7 +8048,7 @@ result r (db.query<employee_retirement> ()); words, we may need to combine a constant query expression specified in the db query pragma with the varying expression specified at the query execution time. To allow this, the - db query pragma syntax supports the use of a special + db query pragma syntax supports the use of the special (?) placeholder that indicates the position in the constant query expression where the runtime expression should be inserted. For example:

@@ -8869,17 +8872,19 @@ for (bool done (false); !done; )

The qualifier tells the ODB compiler what kind of C++ construct this pragma describes. Valid qualifiers are object, - view, value, member, and - namespace. A pragma with the object - 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 view qualifier describe + view, value, member, + namespace, and map. A pragma with the + object 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 view qualifier describe view types, the value qualifier describes value types and the member qualifier is used to describe data members of persistent object, view, and value types. The namespace qualifier is used to describe common properties of objects, views, and value types that belong to - a C++ namespace.

+ a C++ namespace. The map qualifier describes a + mapping between additional database types and types for + which ODB provides built-in support.

The specifier 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"' -

The following three sections cover the specifiers applicable - to the object, value, and member - qualifiers.

+

The following sections cover the specifiers applicable to all the + qualifiers mentioned above.

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 Section 12.6, + at the end of this chapter in Section 12.7, "C++ Compiler Warnings".

12.1 Object Type Pragmas

@@ -11688,7 +11692,202 @@ namespace hr "session"). For more information on sessions, refer to Chapter 10, "Session".

-

12.6 C++ Compiler Warnings

+

12.6 Database Type Mapping Pragmas

+ +

A pragma with the map 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.

+ +

The map pragma has the following format:

+ +
+#pragma db map type("regex") as("subst") [to("subst")] [from("subst")]
+
+ +

The type clause specifies the name of the database type + that we are mapping. We will refer to it as the mapped type + from now on. The name of the mapped type is a Perl-like regular + expression pattern that is matched in the case-insensitive mode.

+ +

The as clause specifies the name of the database type + that we are mapping the mapped type to. We will refer to it as + the interface type 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.

+ +

The optional to and from clauses specify the + database conversion expressions between the mapped type and the + interface type. The to expression converts from the + interface type to the mapped type and from 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 (?) placeholder which will + be replaced with the actual value to be converted. Turning on SQL + statement tracing (Section 3.12, "Tracing SQL + Statement Execution") can be useful for debugging conversion + expressions. This allows you to see the substituted expressions + as used in the actual statements.

+ +

As an example, the following map pragma maps the + PostgreSQL array of INTEGER's to TEXT:

+ +
+#pragma db map type("INTEGER *\\[(\\d*)\\]") \
+               as("TEXT")                    \
+               to("(?)::INTEGER[$1]")        \
+               from("(?)::TEXT")
+
+ +

With the above mapping we can now have a persistent class that + has a member of the PostgreSQL array type:

+ +
+#pragma db object
+class object
+{
+  #pragma db type("INTEGER[]")
+  std::string array_;
+};
+
+ +

In PostgreSQL the array literal has the {n1,n2,...} form. + As a result, we need to make sure that we pass the correct text + representation in the array_ member, for example:

+ +
+object o;
+o.array_ = "{1,2,3}";
+db.persist (o);
+
+ +

Of course, std::string is not the most natural + representation of an array of integers in C++. Instead, + std::vector<int> would have been much more + appropriate. To add support for mapping + std::vector<int> to PostgreSQL INTEGER[] + we need to provide a value_traits specialization + that implements conversion between the PostgreSQL text representation + of an array and std::vector<int>. Below is a sample + implementation:

+ +
+namespace odb
+{
+  namespace pgsql
+  {
+    template <>
+    class value_traits<std::vector<int>, id_string>
+    {
+    public:
+      typedef std::vector<int> value_type;
+      typedef value_type query_type;
+      typedef details::buffer image_type;
+
+      static void
+      set_value (value_type& v,
+                 const details::buffer& 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<char> (is.peek ()); c != '}'; is >> c)
+          {
+            v.push_back (int ());
+            is >> v.back ();
+          }
+        }
+      }
+
+      static void
+      set_image (details::buffer& b,
+                 std::size_t& n,
+                 bool& is_null,
+                 const value_type& v)
+      {
+        is_null = false;
+        std::ostringstream os;
+
+        os << '{';
+
+        for (value_type::const_iterator i (v.begin ()), e (v.end ());
+             i != e;)
+        {
+          os << *i;
+
+          if (++i != e)
+            os << ',';
+        }
+
+        os << '}';
+
+        const std::string& s (os.str ());
+        n = s.size ();
+
+        if (n > b.capacity ())
+          b.capacity (n);
+
+        std::memcpy (b.data (), s.c_str (), n);
+      }
+    };
+  }
+}
+
+ +

Once this specialization is included in the generated code (see + the mapping example in the odb-examples + package for details), we can use std::vector<int> + instead of std::string in our persistent class:

+ +
+#pragma db object
+class object
+{
+  #pragma db type("INTEGER[]")
+  std::vector<int> array_;
+};
+
+ +

If we wanted to always map std::vector<int> + to PostgreSQL INTEGER[], then we could instead + write:

+ +
+typedef std::vector<int> int_vector;
+#pragma db value(int_vector) type("INTEGER[]")
+
+#pragma db object
+class object
+{
+  std::vector<int> array_; // Mapped to INTEGER[].
+};
+
+ +

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 odb-tests package contains a + set of tests in the <database>/custom directories that, + for each database, shows how to provide custom mapping for some of + the additional types.

+ +

12.7 C++ Compiler Warnings

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

The disadvantage of this approach is that it can quickly become overly verbose when positioned pragmas are used.

-

12.6.1 GNU C++

+

12.7.1 GNU C++

GNU g++ does not issue warnings about unknown pragmas unless requested with the -Wall command line option. @@ -11753,7 +11952,7 @@ class person g++ -Wall -Wno-unknown-pragmas ... -

12.6.2 Visual C++

+

12.7.2 Visual C++

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) -

12.6.3 Sun C++

+

12.7.3 Sun C++

The Sun C++ compiler does not issue warnings about unknown pragmas unless the +w or +w2 option is specified. @@ -11799,7 +11998,7 @@ class person CC +w -erroff=unknownpragma ... -

12.6.4 IBM XL C++

+

12.7.4 IBM XL C++

IBM XL C++ issues an unknown pragma warning (1540-1401) by default. To disable this warning we can add the -qsuppress=1540-1401 @@ -11809,7 +12008,7 @@ CC +w -erroff=unknownpragma ... xlC -qsuppress=1540-1401 ... -

12.6.5 HP aC++

+

12.7.5 HP aC++

HP aC++ (aCC) issues an unknown pragma warning (2161) by default. To disable this warning we can add the +W2161 @@ -11819,7 +12018,7 @@ xlC -qsuppress=1540-1401 ... aCC +W2161 ... -

12.6.6 Clang

+

12.7.6 Clang

Clang does not issue warnings about unknown pragmas unless requested with the -Wall command line option. @@ -12076,6 +12275,11 @@ class object }; +

It is also possible to add support for additional MySQL types, + such as geospatial types. For more information, refer to + Section 12.6, "Database Type Mapping + Pragmas".

+

13.2 MySQL Database Class

The MySQL database class has the following @@ -12689,6 +12893,11 @@ class object unsigned long long values will be represented in the database as negative values.

+

It is also possible to add support for additional SQLite types, + such as NUMERIC. For more information, refer to + Section 12.6, "Database Type Mapping + Pragmas".

+

14.2 SQLite Database Class

The SQLite database class has the following @@ -13442,6 +13651,13 @@ class object the most significant bit of the actual unsigned value being persisted.

+

It is also possible to add support for additional PostgreSQL types, + such as NUMERIC, geometry types, XML, + JSON, enumeration types, composite types, arrays, + geospatial types, and the key-value store (HSTORE). + For more information, refer to Section 12.6, + "Database Type Mapping Pragmas".

+

15.2 PostgreSQL Database Class

The PostgreSQL database class has the following @@ -14072,6 +14288,12 @@ class object mapped to NUMBER(10) with the default NULL semantics being NOT NULL.

+

It is also possible to add support for additional Oracle types, + such as XML, geospatial types, user-defined types, + and collections (arrays, table types). For more information, refer to + Section 12.6, "Database Type Mapping + Pragmas".

+

16.2 Oracle Database Class

The Oracle database 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.

+

It is also possible to add support for additional SQL Server types, + such as geospatial types, XML, and user-defined types. + For more information, refer to Section 12.6, "Database + Type Mapping Pragmas".

+

17.2 SQL Server Database Class

The SQL Server database class encapsulates the ODBC -- cgit v1.1