From 2ad2e5500dd075db421a516502c9e522fdc34ee0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 5 Dec 2012 17:37:46 +0200 Subject: Documentation for multi-database support --- NEWS | 16 +- doc/manual.xhtml | 1332 +++++++++++++++++++++++++++++++++++++++--------- doc/odb-prologue.1 | 24 +- doc/odb-prologue.xhtml | 25 +- odb/options.cli | 30 +- 5 files changed, 1180 insertions(+), 247 deletions(-) diff --git a/NEWS b/NEWS index 1636642..e4fd9f3 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,14 @@ Version 2.2.0 - * Static multi-database support. It allows an application to simultaneously - work with multiple database systems using their static interfaces (i.e., - odb::::database instead of odb::database). + * Multi-database support. It allows an application to simultaneously work + with multiple database systems and comes in two flavors: static and + dynamic. With static support the application uses the static database + interfaces (that is, odb::::database instead of odb::database). With + dynamic support the same application code can access multiple databases + via a common interface. Dynamic multi-database supports also allows the + application to dynamically load the database support code for individual + databases if and when necessary. For more information, refer to Chapter + 13, "Multi-Database Support" in the ODB manual. * Support for prepared queries. Prepared queries are a thin wrapper around the underlying database system's prepared statements functionality. They @@ -11,8 +17,8 @@ Version 2.2.0 refer to Section 4.5, "Prepared Queries" in the ODB manual as well as the 'prepared' example in the odb-examples package. - * New options, --export-symbol and --extern-symbol, allow DLL exporting - of the generated database support code. + * New options, --export-symbol and --extern-symbol, allow DLL exporting of + the generated database support code. * Support for early connection release. Now the database connection is released when commit()/rollback() is called rather than when the diff --git a/doc/manual.xhtml b/doc/manual.xhtml index 05fdc9b..32a339e 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -311,7 +311,8 @@ for consistency. 2.6Updating Persistent Objects 2.7Defining and Using Views 2.8Deleting Persistent Objects - 2.9Summary + 2.9Accessing Multiple Databases + 2.10Summary @@ -580,118 +581,133 @@ for consistency. - 13MySQL Database + 13Multi-Database Support - - - - + + + + +
13.1MySQL Type Mapping
13.2MySQL Database Class
13.3MySQL Connection and Connection Factory
13.4MySQL Exceptions
13.1Static Multi-Database Support
13.2Dynamic Multi-Database Support + + +
13.2.213.2.2 Dynamic Loading of Database Support Code
+
+ + + + + 14MySQL Database + + + + + - - +
14.1MySQL Type Mapping
14.2MySQL Database Class
14.3MySQL Connection and Connection Factory
14.4MySQL Exceptions
13.5MySQL Limitations + 14.5MySQL Limitations - +
13.5.1Foreign Key Constraints
14.5.1Foreign Key Constraints
13.6MySQL Index Definition
14.6MySQL Index Definition
- 14SQLite Database + 15SQLite Database - - - - + + + + - - +
14.1SQLite Type Mapping
14.2SQLite Database Class
14.3SQLite Connection and Connection Factory
14.4SQLite Exceptions
15.1SQLite Type Mapping
15.2SQLite Database Class
15.3SQLite Connection and Connection Factory
15.4SQLite Exceptions
14.5SQLite Limitations + 15.5SQLite Limitations - - - - - + + + + +
14.5.1Query Result Caching
14.5.2Automatic Assignment of Object Ids
14.5.3Foreign Key Constraints
14.5.4Constraint Violations
14.5.5Sharing of Queries
15.5.1Query Result Caching
15.5.2Automatic Assignment of Object Ids
15.5.3Foreign Key Constraints
15.5.4Constraint Violations
15.5.5Sharing of Queries
14.6SQLite Index Definition
15.6SQLite Index Definition
- 15PostgreSQL Database + 16PostgreSQL Database - - - - + + + + - - +
15.1PostgreSQL Type Mapping
15.2PostgreSQL Database Class
15.3PostgreSQL Connection and Connection Factory
15.4PostgreSQL Exceptions
16.1PostgreSQL Type Mapping
16.2PostgreSQL Database Class
16.3PostgreSQL Connection and Connection Factory
16.4PostgreSQL Exceptions
15.5PostgreSQL Limitations + 16.5PostgreSQL Limitations - - - - - - + + + + + +
15.5.1Query Result Caching
15.5.2Foreign Key Constraints
15.5.3Unique Constraint Violations
15.5.4Date-Time Format
15.5.5Timezones
15.5.6NUMERIC Type Support
16.5.1Query Result Caching
16.5.2Foreign Key Constraints
16.5.3Unique Constraint Violations
16.5.4Date-Time Format
16.5.5Timezones
16.5.6NUMERIC Type Support
15.6PostgreSQL Index Definition
16.6PostgreSQL Index Definition
- 16Oracle Database + 17Oracle Database - - - - + + + + - - +
16.1Oracle Type Mapping
16.2Oracle Database Class
16.3Oracle Connection and Connection Factory
16.4Oracle Exceptions
17.1Oracle Type Mapping
17.2Oracle Database Class
17.3Oracle Connection and Connection Factory
17.4Oracle Exceptions
16.5Oracle Limitations + 17.5Oracle Limitations - - - - - - - - + + + + + + + +
16.5.1Identifier Truncation
16.5.2Query Result Caching
16.5.3Foreign Key Constraints
16.5.4Unique Constraint Violations
16.5.5Large FLOAT and NUMBER Types
16.5.6Timezones
16.5.7LONG Types
16.5.8LOB Types and By-Value Accessors/Modifiers
17.5.1Identifier Truncation
17.5.2Query Result Caching
17.5.3Foreign Key Constraints
17.5.4Unique Constraint Violations
17.5.5Large FLOAT and NUMBER Types
17.5.6Timezones
17.5.7LONG Types
17.5.8LOB Types and By-Value Accessors/Modifiers
16.6Oracle Index Definition
17.6Oracle Index Definition
- 17Microsoft SQL Server Database + 18Microsoft SQL Server Database - - - - + + + + - - +
17.1SQL Server Type Mapping
17.2SQL Server Database Class
17.3SQL Server Connection and Connection Factory
17.4SQL Server Exceptions
18.1SQL Server Type Mapping
18.2SQL Server Database Class
18.3SQL Server Connection and Connection Factory
18.4SQL Server Exceptions
17.5SQL Server Limitations + 18.5SQL Server Limitations - - - - - - - + + + + + + +
17.5.1Query Result Caching
17.5.2Foreign Key Constraints
17.5.3Unique Constraint Violations
17.5.4Multi-threaded Windows Applications
17.5.5Affected Row Count and DDL Statements
17.5.6Long Data and Automatically Assigned Object Ids
17.5.7Long Data and By-Value Accessors/Modifiers
18.5.1Query Result Caching
18.5.2Foreign Key Constraints
18.5.3Unique Constraint Violations
18.5.4Multi-threaded Windows Applications
18.5.5Affected Row Count and DDL Statements
18.5.6Long Data and Automatically Assigned Object Ids
18.5.7Long Data and By-Value Accessors/Modifiers
17.6SQL Server Index Definition
18.6SQL Server Index Definition
@@ -701,35 +717,35 @@ for consistency. - 18Profiles Introduction + 19Profiles Introduction - 19Boost Profile + 20Boost Profile - - - - + + + + - - @@ -738,29 +754,29 @@ for consistency. -
19.1Smart Pointers Library
19.2Unordered Containers Library
19.3Multi-Index Container Library
19.4Optional Library
20.1Smart Pointers Library
20.2Unordered Containers Library
20.3Multi-Index Container Library
20.4Optional Library
19.5Date Time Library + 20.5Date Time Library - - - - - + + + + +
19.5.1MySQL Database Type Mapping
19.5.2SQLite Database Type Mapping
19.5.3PostgreSQL Database Type Mapping
19.5.4Oracle Database Type Mapping
19.5.5SQL Server Database Type Mapping
20.5.1MySQL Database Type Mapping
20.5.2SQLite Database Type Mapping
20.5.3PostgreSQL Database Type Mapping
20.5.4Oracle Database Type Mapping
20.5.5SQL Server Database Type Mapping
19.6Uuid Library + 20.6Uuid Library - - - - - + + + + +
19.6.1MySQL Database Type Mapping
19.6.2SQLite Database Type Mapping
19.6.3PostgreSQL Database Type Mapping
19.6.4Oracle Database Type Mapping
19.6.5SQL Server Database Type Mapping
20.6.1MySQL Database Type Mapping
20.6.2SQLite Database Type Mapping
20.6.3PostgreSQL Database Type Mapping
20.6.4Oracle Database Type Mapping
20.6.5SQL Server Database Type Mapping
20Qt Profile + 21Qt Profile - - - + + - @@ -1529,7 +1545,7 @@ main (int argc, char* argv[]) database name, etc., from the command line. In your own applications you may prefer to use other mysql::database constructors which allow you to pass this information directly - (Section 13.2, "MySQL Database Class").

+ (Section 14.2, "MySQL Database Class").

Next, we create three person objects. Right now they are transient objects, which means that if we terminate the application @@ -2039,7 +2055,155 @@ max age: 33 } -

2.9 Summary

+

2.9 Working with Multiple Databases

+ +

Accessing multiple databases (that is, data stores) is simply a + matter of creating multiple odb::<db>::database + instances representing each database. For example:

+ +
+odb::mysql::database db1 ("john", "secret", "test_db1");
+odb::mysql::database db2 ("john", "secret", "test_db2");
+  
+ +

A more interesting question is how we access multiple database + systems (that is, database implementations) from the same application. + For example, our application may need to store some objects in a + remote MySQL database and others in a local SQLite file. Or, our + application may need to be able to store its objects in a database + system that is selected by the user at runtime.

+ +

ODB provides comprehensive multi-database support that ranges from + tight integration with specific database systems to being able to + write database-agnostic code and loading individual database systems + support dynamically. While all these aspects are covered in detail + in Chapter 13, "Multi-Database Support", in this + section we will get a taste of this functionality by extending our + "Hello World" example to be able to store its data either in MySQL + or PostgreSQL (other database systems supported by ODB can be added + in a similar manner).

+ +

The first step in adding multi-database support is to re-compile + our person.hxx header to generate database support + code for additional database systems:

+ +
+odb --multi-database dynamic -d common -d mysql -d pgsql \
+--generate-query --generate-schema person.hxx
+  
+ +

The --multi-database ODB compiler option turns on + multi-database support. For now it is not important what the + dynamic value that we passed to this option means, + but if you are curious, see Chapter 13. The result of this + command are three sets of generated files: person-odb.?xx + (common interface; corresponds to the common database), + person-odb-mysql.?xx (MySQL support code), and + person-odb-pgsql.?xx (PostgreSQL support code). There + are also two schema files: person-mysql.sql and + person-pgsql.sql.

+ +

The only part that we need to change in driver.cxx + is how we create the database instance. Specifically, this line:

+ +
+auto_ptr<database> db (new odb::mysql::database (argc, argv));
+  
+ +

Now our example is capable of storing its data either in MySQL or + PostgreSQL so we need to somehow allow the caller to specify which + database we must use. To keep things simple, we will make the first + command line argument specify the database system we must use while + the rest will contain the database-specific options which we will + pass to the odb::<db>::database constructor as + before. Let's put all this logic into a separate function which we + will call create_database(). Here is what the beginning + of our modified driver.cxx will look like (the remainder + is unchanged):

+ +
+// driver.cxx
+//
+
+#include <string>
+#include <memory>   // std::auto_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/mysql/database.hxx>
+#include <odb/pgsql/database.hxx>
+
+#include "person.hxx"
+#include "person-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+auto_ptr<database>
+create_database (int argc, char* argv[])
+{
+  auto_ptr<database> r;
+
+  if (argc < 2)
+  {
+    cerr << "error: database system name expected" << endl;
+    return r;
+  }
+
+  string db (argv[1]);
+
+  if (db == "mysql")
+    r.reset (new odb::mysql::database (argc, argv));
+  else if (db == "pgsql")
+    r.reset (new odb::pgsql::database (argc, argv));
+  else
+    cerr << "error: unknown database system " << db << endl;
+
+  return r;
+}
+
+int
+main (int argc, char* argv[])
+{
+  try
+  {
+    auto_ptr<database> db (create_database (argc, argv));
+
+    if (db.get () == 0)
+      return 1; // Diagnostics has already been issued.
+
+    ...
+  
+ +

And that's it. The only thing left is to build and run our + example:

+ +
+c++ -c driver.cxx
+c++ -c person-odb.cxx
+c++ -c person-odb-mysql.cxx
+c++ -c person-odb-pgsql.cxx
+c++ -o driver driver.o person-odb.o person-odb-mysql.o \
+person-odb-pgsql.o -lodb-mysql -lodb-pgsql -lodb
+  
+ +

Here is how we can access a MySQL database:

+ +
+mysql --user=odb_test --database=odb_test < person-mysql.sql
+./driver mysql --user odb_test --database odb_test
+  
+ +

Or a PostgreSQL database:

+ +
+psql --user=odb_test --dbname=odb_test -f person-pgsql.sql
+./driver pgsql --user odb_test --database odb_test
+  
+ +

2.10 Summary

This chapter presented a very simple application which, nevertheless, exercised all of the core database functions: persist(), @@ -4698,7 +4862,7 @@ namespace odb the prepared query name. This name is used as a key for prepared query caching (discussed later) and must be unique. For some databases, notably PostgreSQL, it is also used as a name of the underlying prepared - statement. The name "object_query" (e.g., + statement. The name "object_query" (for example, "person_query") is reserved for the once-off queries executed by the database::query() function. Note that the prepare_query() function makes only a shallow copy @@ -7223,7 +7387,7 @@ namespace odb consider using a more efficient implementation of the optional value concept such as the optional class template from Boost - (Section 19.4, "Optional Library").

+ (Section 20.4, "Optional Library").

Another common C++ representation of a value that can be NULL is a pointer. ODB will automatically @@ -13718,18 +13882,20 @@ class person DATABASE SYSTEMS

Part II covers topics specific to the database system - implementations and their support in ODB. In particular, it - describes the system-specific database classes - as well as the default mapping between basic C++ value types - and native database types. Part II consists of the following - chapters.

+ implementations and their support in ODB. The first chapter in + Part II discusses how to use multiple database systems in the + same application. The subsequent chapters describe the system-specific + database classes as well as the default mapping + between basic C++ value types and native database types. Part + II consists of the following chapters.

20.1Basic Types Library + 21.1Basic Types Library - - - - - + + + + +
20.1.1MySQL Database Type Mapping
20.1.2SQLite Database Type Mapping
20.1.3PostgreSQL Database Type Mapping
20.1.4Oracle Database Type Mapping
20.1.5SQL Server Database Type Mapping
21.1.1MySQL Database Type Mapping
21.1.2SQLite Database Type Mapping
21.1.3PostgreSQL Database Type Mapping
21.1.4Oracle Database Type Mapping
21.1.5SQL Server Database Type Mapping
20.2Smart Pointers Library
20.3Containers Library
21.2Smart Pointers Library
21.3Containers Library
20.4Date Time Library + 21.4Date Time Library - - - - - + + + + +
20.4.1MySQL Database Type Mapping
20.4.2SQLite Database Type Mapping
20.4.3PostgreSQL Database Type Mapping
20.4.4Oracle Database Type Mapping
20.4.5SQL Server Database Type Mapping
21.4.1MySQL Database Type Mapping
21.4.2SQLite Database Type Mapping
21.4.3PostgreSQL Database Type Mapping
21.4.4Oracle Database Type Mapping
21.4.5SQL Server Database Type Mapping
- - - - - + + + + + +
13MySQL Database
14SQLite Database
15PostgreSQL Database
16Oracle Database
17Microsoft SQL Server Database
13Multi-Database Support
14MySQL Database
15SQLite Database
16PostgreSQL Database
17Oracle Database
18Microsoft SQL Server Database
@@ -13737,7 +13903,719 @@ class person
-

13 MySQL Database

+

13 Multi-Database Support

+ +

Some applications may need to access multiple database systems, either + simultaneously or one at a time. For example, an application may + utilize an embedded database such as SQLite as a local cache and use + a client-server database such as PostgreSQL for more permanent + but slower to access remote storage. Or an application may need + to be able to store its data in any database selected at runtime + by the user. Yet another scenario is the data migration from one + database system to another. In this case, multi-database support + is only required for a short period. It is also plausible that an + application implements all three of these scenarios, that is, it + uses SQLite as a local cache, allows the user to select the remote + database system, and supports data migration from one remote database + system to another.

+ +

ODB provides two types of multi-database support: static + and dynamic. With static support we use the + database system-specific interfaces to perform database + operations. That is, instead of using odb::database, + odb::transaction, or odb::query, we + would use, for example, odb::sqlite::database, + odb::sqlite::transaction, or + odb::sqlite::query to access an SQLite database.

+ +

In contrast, with dynamic multi-database support we can + use the common interface to access any database without having to + know which one it is. At runtime, ODB will automatically dispatch + a call on the common interface to the specific database implementation + based on the actual database instance being + used. In fact, this mechanism is very similar to C++ virtual + functions.

+ +

Both static and dynamic multi-database support have a different set + of advantages and disadvantages which makes them more or less suitable + for different use cases. Static support has zero overhead compared + to single-database support and allows us to use database + system-specific features, extensions, etc. At the same time, the + code that we write will be tied to the specific database system. + As a result, this type of multi-database support is more + suitable for situations where different parts of an application + access different but specific database systems. For example, + using SQLite as a local cache most likely falls into this + category since we are using a specific database system (SQLite) + and the code that will check the cache will most likely (but + not necessarily) be separate from the code that interact with + the remote database. Another example where static multi-database + support might be more suitable is a once-off data migration from + one database system to another. In this case both the source and + target are specific database systems. In contrast, if data migration + from one database system to another is a general feature in an + application, then dynamic multi-database support might be more + suitable.

+ +

The main advantage of dynamic multi-database support is the + database system-independence of the code that we write. The same + application code will work with any database system supported by + ODB and the generated database support code can be packaged into + separate libraries and loaded dynamically by the application. The + disadvantages of dynamic support are slight overhead and certain + limitations in functionality compared to static support (see + Section 13.2, "Dynamic Multi-Database Support" + for details). As a result, dynamic multi-database support is most + suitable to situations where we need the same code to + work with a range of database systems. For example, if your + application must be able to store its data in any database + selected by the user, then dynamic support is probably the + best option.

+ +

Note also that it is possible to mix and match static and dynamic + support in the same application. In fact, dynamic support is built + on top of static support so it is possible to use the same database + system both "statically" and "dynamically". In particular, the ability + to "drop down" from dynamic to static support can be used to overcome + the functionality limitations mentioned above. Finally, + single-database support is just a special case of static + multi-database support with a single database system.

+ +

By default ODB assumes single-database support. To enable + multi-database support we use the --multi-database + (or -m) ODB compiler option. This option is also used to + specify the support type: static or dynamic. + For example:

+ +
+odb -m static ... person.hxx
+  
+ +

With multi-database support enabled, we can now generate the database + support code for several database systems. This can be accomplished + either with a single ODB compiler invocation by specifying multiple + --database (or -d) options or with multiple + ODB compiler invocations. Both approaches produce the same result, + for example:

+ +
+odb -m static -d common -d sqlite -d pgsql person.hxx
+  
+ +

Is equivalent to:

+ +
+odb -m static -d common person.hxx
+odb -m static -d sqlite person.hxx
+odb -m static -d pgsql person.hxx
+  
+ +

Notice that the first -d option has common + as its value. This is not a real database system. Rather, it instructs + the ODB compiler to generate code that is common to all the database + systems and, in case of dynamic support, is also the common + interfaces.

+ +

If you look at the result of the above commands, you will also notice + changes in the output file names. In the single-database mode the ODB + compiler produces a single set of the person-odb.?xx files + which contain both the common as well as the database specific + generated code (since there is only one database system in use, + there is no reason to split the two). In contrast, in the + multi-database mode, the person-odb.?xx set of files + contains the common code while the database system-specific code is + written to files in the form person-odb-<db>.?xx. + That is, person-odb-sqlite.?xx for SQLite, + person-odb-pgsql.?xx for PostgreSQL, etc.

+ +

If we need dynamic support for some databases and static for + others, then the common code must be generated + in the dynamic mode. For example, if we need static support + for SQLite and dynamic support for PostgreSQL and Oracle, then + the ODB compiler invocations could look like this:

+ +
+odb -m dynamic -d common person.hxx
+odb -m static -d sqlite person.hxx
+odb -m dynamic -d pgsql person.hxx
+odb -m dynamic -d oracle person.hxx
+  
+ +

With multi-database support enabled, it is possible to restrict ODB + pragmas to apply only to a specific database system (unrestricted + pragmas apply to all the databases). For example:

+ +
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db pgsql:type("VARCHAR(128)") sqlite:type("TEXT")
+  std::string name_;
+
+  unsigned short age_;
+
+  #pragma db pgsql index member(age_)
+};
+  
+ +

Above, the pragma for the name_ data member shows the + use of a database prefix (for example, pgsql:) that + only applies to the specifier that follows. The pragma that defines + an index on the age_ data member shows the use of a + database prefix that applies to the whole pragma. In this case the + database name must immediately follow the db keyword.

+ + +

Similar to pragmas, ODB compiler options that determine the kind + (for example, --schema-format), names (for example, + --odb-file-suffix), or content (for example, prologue + and epilogue options) of the output files can be prefixed with the + database name. For example:

+ +
+odb --odb-file-suffix common:-odb-common ...
+  
+ +

Dynamic multi-database support requires consistent mapping across + all the databases. That is, the same classes and data members + should be mapped to objects, simple/composite values, etc., for + all the databases. In contrast, static multi-database support + does not have this restriction. Specifically, with static support, + some data members can be transient for some database systems. + Similarly, the same class (for example, point) can + be mapped to a simple value in one database (for example, to the + POINT PostgreSQL type) and to a composite value + in another (for example, in SQLite, which does not have a + built-in point type).

+ +

The following sections discuss static and dynamic multi-database + support in more detail.

+ + +

13.1 Static Multi-Database Support

+ +

With static multi-database support, instead of including + person-odb.hxx, application source code has + to include person-odb-<db>.hxx header files + corresponding to the database systems that will be used.

+ +

The application code has to also use database system-specific + interfaces when performing database operations. As an example, + consider the following transaction in a single-database + application. It uses the common interfaces, that is, classes + from the odb namespace.

+ +
+#include "person-odb.hxx"
+
+odb::database& db = ...
+
+typedef odb::query<person> query;
+typedef odb::result<person> result;
+
+odb::transaction t (db.begin ());
+result r (db.query<person> (query::age < 30));
+...
+t.commit ();
+  
+ +

In an application that employs static multi-database support + the same transaction for SQLite would be rewritten like this:

+ +
+#include "person-odb-sqlite.hxx"
+
+odb::sqlite::database& db = ...
+
+typedef odb::sqlite::query<person> query;
+typedef odb::result<person> result;      // odb:: not odb::sqlite::
+
+odb::sqlite::transaction t (db.begin ());
+result r (db.query<person> (query::age < 30));
+...
+t.commit ();
+  
+ +

That is, the database, transaction, and + query classes now come from the odb::sqlite + namespace instead of odb. Other classes that have + database system-specific interfaces are connection, + statement, and tracer. Note that + all of them derive from the corresponding common versions. It + is also possible to use common transaction, + connection, and statement classes + with static support, if desired.

+ +

Notice that we didn't use the odb::sqlite namespace + for the result class template. This is because + result is database system-independent. All other + classes defined in namespace odb, except those + specifically mentioned above, are database system-independent. + In particular, result, prepared_query, + session, schema_catalog, and all the + exceptions are database system-independent.

+ +

Writing odb::sqlite:: before every name can quickly + become burdensome. As we have seen before, in single-database + applications that use the common interface we can add the + using namespace directive to avoid qualifying + each name. For example:

+ +
+#include "person-odb.hxx"
+
+odb::database& db = ...
+
+{
+  using namespace odb::core;
+
+  typedef query<person> person_query;
+  typedef result<person> person_result;
+
+  transaction t (db.begin ());
+  person_result r (db.query<person> (person_query::age < 30));
+  ...
+  t.commit ();
+}
+  
+ +

A similar mechanism is available in multi-database support. Each + database runtime defines the odb::<db>::core + namespace that contains all the database system-independent + names as well as the database system-specific ones for this + database. Here is how we can rewire the above transaction + using this approach:

+ +
+#include "person-odb-sqlite.hxx"
+
+odb::sqlite::database& db = ...
+
+{
+  using namespace odb::sqlite::core;
+
+  typedef query<person> person_query;
+  typedef result<person> person_result;
+
+  transaction t (db.begin ());
+  person_result r (db.query<person> (person_query::age < 30));
+  ...
+  t.commit ();
+}
+  
+ +

If the using namespace directive cannot be used, for + example, because the same code fragment accesses several databases, + then we can still make the namespace qualifications more concise + by assigning shorter aliases to database namespaces. For example:

+ +
+#include "person-odb-pgsql.hxx"
+#include "person-odb-sqlite.hxx"
+
+namespace pg = odb::pgsql;
+namespace sl = odb::sqlite;
+
+pg::database& pg_db = ...
+sl::database& sl_db = ...
+
+typedef pg::query<person> pg_query;
+typedef sl::query<person> sl_query;
+typedef odb::result<person> result;
+
+// First check the local cache.
+//
+odb::transaction t (sl_db.begin ()); // Note: using common transaction.
+result r (sl_db.query<person> (sl_query::age < 30));
+
+// If no hits, try the remote database.
+//
+if (r.empty ())
+{
+  t.commit ();              // End the SQLite transaction.
+  t.reset (pg_db.begin ()); // Start the PostgreSQL transaction.
+
+  r = pg_db.query<person> (pg_query::age < 30);
+}
+
+// Handle the result.
+//
+...
+
+t.commit ();
+  
+ +

With static multi-database support we can make one of the databases + the default database with the --default-database option. + The default database can be accessed via the common interface, just + like with single-database support. For example:

+ +
+odb -m static -d common -d pgsql -d sqlite --default-database pgsql ...
+  
+ +

The default database mechanism can be useful when one of the + databases is primary or when retrofitting multi-database support + into an existing single-database application. For example, if + we are adding SQLite as a local cache into an existing + application that uses PostgreSQL as its only database, then + by making PostgreSQL the default database we avoid having to + change all the existing code. Note that if dynamic multi-database + support is enabled, then the common (dynamic) interface is always + made the default database.

+ +

13.2 Dynamic Multi-Database Support

+ +

With dynamic multi-database support, application source code only + needs to include the person-odb.hxx header file, just + like with single-database support. In particular, we don't need + to include any of the person-odb-<db>.hxx files + unless we would also like to use certain database systems in the + static multi-database mode.

+ +

When performing database operations, the application code + uses the common interfaces from the odb namespace, + just like with single-database support. As an example, consider + a function that can be used to load an object either from a local + SQLite cache or a remote PostgreSQL database (in reality, this + function can be used with any database system support by ODB + provided we generated the database support code for this database + and linked it into our application):

+ +
+#include "person-odb.hxx"
+
+std::unique_ptr<person>
+load (odb::database& db, const std::string& name)
+{
+  odb::transaction t (db.begin ());
+  std::unique_ptr<person> p (db.find (name));
+  t.commit ();
+  return p;
+}
+
+odb::pgsql::database& pg_db = ...
+odb::sqlite::database& sl_db = ...
+
+// First try the local cache.
+//
+std::unique_ptr<person> p (load (sl_db, "John Doe"));
+
+// If not found, try the remote database.
+//
+if (p == 0)
+  p = load (pg_db, "John Doe");
+
+...
+  
+ +

As you can see, we can use dynamic multi-database support just like + single-database support except that now our code can work with + different database systems. Note, however, one difference: with + single-database support we could perform database operations using + either the common odb::database or a database system-specific + (for example, odb::sqlite::database) interface + with the same effect. In contrast, with dynamic multi-database support, + the use of the database system-specific interface results in the + switch to the static mode (for which, as was mentioned earlier, we would + need to include the corresponding person-odb-<db>.hxx + header file). As we will discuss shortly, switching from dynamic to + static mode can be used to overcome limitations imposed by dynamic + multi-database support.

+ +

Dynamic multi-database support has certain overheads and limitations + compared to static support. For database operations, the generated code + maintains function tables that are used to dispatch calls to the database + system-specific implementations. In single-database and static + multi-database support, the query type implements a thin + wrapper around the underlying database system's SELECT + statement. With dynamic multi-database support, because the + underlying database system is only known at query execution + (or preparation) time, the query type stores a + database system-independent representation of the query that + is then translated to the database system-specific form. Because + of this database system-independent representation, dynamic + support queries have a number of limitations. Specifically, dynamic + queries do not support parameter binding in native query fragments. + They also make copies of by-value parameterd (by-reference parameters + can be used to remove this overhead). Finally, parameters of array + types (for example, char[256]) can only be bound + by-reference.

+ +

As we mentioned earlier, switching from dynamic to static mode + can be an effective way to overcome these limitations. As an + example, consider a function that prints the list of people of + a certain age. The caller also specified the limit on the number + of entries to print. Some database systems, for example, PostgreSQL, + allow us to propagate this limit to the database server with the + LIMIT clause. To add this clause we would need to + construct a native query fragment and, as we discussed above, we + won't be able to bind a parameter (the limit) while in the dynamic + mode. The following implementation shows how we can overcome this + by switching to the static mode and using the PostgreSQL-specific + interface:

+ +
+#include "person-odb.hxx"
+#include "person-odb-pgsql.hxx" // Needed for static mode.
+
+void
+print (odb::database& db, unsigned short age, unsigned long limit)
+{
+  typedef odb::query<person> query;
+  typedef odb::result<person> result;
+
+  odb::transaction t (db.begin ());
+
+  query q (query::age == age);
+  result r;
+
+  if (db.id () == odb::id_pgsql)
+  {
+    // We are using PostgreSQL. Drop down to the static mode and
+    // add the LIMIT clause to the query.
+    //
+    namespace pg = odb::pgsql;
+    typedef pg::query<person> pg_query;
+
+    pg::database& pg_db (static_cast<pg::database&> (db));
+    pg_query pg_q (pg_query (q) + "LIMIT" + pg_query::_val (limit));
+    r = pg_db.query<person> (pg_q);
+  }
+  else
+    r = db.query<person> (q);
+
+  // Handle the result up to the limit elements.
+  //
+  ...
+
+  t.commit ();
+}
+
+odb::pgsql::database& pg_db = ...
+odb::sqlite::database& sl_db = ...
+
+print (sl_db, 30, 100);
+print (sl_db, 30, 100);
+  
+ +

A few things to note about this example. First, we use the + database::id() function to determine the actual database + system we use. This function has the following signature:

+ +
+namespace odb
+{
+  enum database_id
+  {
+    id_mysql,
+    id_sqlite,
+    id_pgsql,
+    id_oracle,
+    id_mssql,
+    id_common
+  };
+
+  class database
+  {
+  public:
+    ...
+
+    database_id
+    id () const;
+  }
+}
+  
+ +

Note that database::id() can never return the + id_common value.

+ +

The other thing to note is how we translate the dynamic query + to the database system-specific one (the pg_query (q) + expression). Every odb::<db>::query class provides + such a translation constructor.

+ +

13.2.2 Dynamic Loading of Database Support Code

+ +

With dynamic multi-database support, the generated database support + code automatically registers itself with the function tables that + we mentioned earlier. This makes it possible to package the generated + code for each database into a separate dynamic-link library (Windows + DLL) or dynamic shared object (Unix DSO; collectively referred to as + DLLs from now on) and load/unload them from the application + dynamically using APIs such as Win32 LoadLibrary() or + POSIX dlopen(). This allows the application address + space to contain code only for database systems that are actually + needed in any particular moment. Another advantage of this approach + is the ability to distribute individual database system support + separately.

+ +

This section provides an overview of how to package the generated + database support code into DLLs for both Windows and Unix using + GNU/Linux as an example. Note also that if static multi-database + support is used for a particular database system, then the dynamic + loading cannot be used for this database. It is, however, still + possible to package the generated code into a DLL but this DLL + will have to be linked to the executable at link-time rather + than at runtime. If dynamic loading is desirable in this situation, + then another alternative would be to package the functionality + that requires static support together with the database support + code into the DLL and import this functionality dynamically + using the GetProcAddress() (Win32) or dlsym() + (Unix) function.

+ +

The first step in packaging the generated code into DLLs is to + set up the symbol exporting. This step is required for + Windows DLLs but is optional for Unix DSOs. Most modern Unix + systems (such as GNU/Linux) provide control over symbol + visibility, which is a mechanism similar to Windows symbol + exporting. Notable advantages of using this mechanism to + explicitly specify which symbols are visible include + smaller Unix DSOs and faster load times. If, however, you are + not planning to control symbol visibility on Unix, then you can + skip directly to the second step below.

+ +

An important point to understand is that we only need to export + the common interface, that is, the classes defined in the + person-odb.hxx header. In particular, we don't need + to export the database system-specific classes defined in + the person-odb-<db>.hxx, unless we are also using + this database in the static mode (in which case, the procedure + described below will need to be repeated for that database as + well).

+ +

The ODB compiler provides two command line options, + --export-symbol and --extern-symbol, + which can be used to insert the export and extern + macros in all the necessary places in the generated header file. + You are probably familiar with the concept of export macro which + expands to an export directive if we are building the DLL and to + an import directive if we are building client code. The + extern macro is a supplementary mechanism which is necessary to + export explicit template instantiations used by the generated + code when query support is enabled. As we will see shortly, the + extern macro must expand into the extern C++ keyword + in certain situations and must be left undefined in others. To + manage all these macro definitions, it is customary to create the + so called export header. Based on a single macro that is normally + defined in the project file or on the command line and which + indicates whether we are building the DLL or client code, the + export header file sets the export and extern macros to their + appropriate values. Continuing with our person example, on Windows + the export header, which we will call person-export.hxx, + could look like this:

+ +
+// person-export.hxx
+//
+// Define PERSON_BUILD_DLL if we are building the DLL. Leave it
+// undefined in client code.
+//
+#ifndef PERSON_EXPORT_HXX
+#define PERSON_EXPORT_HXX
+
+#ifdef PERSON_BUILD_DLL
+#  define PERSON_EXPORT __declspec(dllexport)
+#else
+#  define PERSON_EXPORT __declspec(dllimport)
+#  define PERSON_EXTERN extern
+#endif
+
+#endif // PERSON_EXPORT_HXX
+  
+ +

The equivalent export header for GCC on GNU/Linux is shown below. + Note also that on GNU/Linux, by default, all symbols are visible + and we need to add the GCC -fvisibility=hidden option to + make them hidden by default.

+ +
+// person-export.hxx
+//
+#ifndef PERSON_EXPORT_HXX
+#define PERSON_EXPORT_HXX
+
+#define PERSON_EXPORT __attribute__ ((visibility ("default")))
+#define PERSON_EXTERN extern
+
+#endif // PERSON_EXPORT_HXX
+  
+ +

Next we need to export the person persistent class + using the export macro and re-compile our person.hxx file + with the --export-symbol and --extern-symbol + options. We will also need to include person-export.hxx + into the generated person-odb.hxx file. For that we use + the --hxx-prologue option. Here is how we can do + this with multiple invocations of the ODB compiler:

+ +
+odb -m dynamic -d common --hxx-prologue "#include \"person-export.hxx\"" \
+--export-symbol PERSON_EXPORT --extern-symbol PERSON_EXTERN person.hxx
+
+odb -m dynamic -d sqlite person.hxx
+odb -m dynamic -d pgsql person.hxx
+  
+ +

It is also possible to achieve the same with a single invocation. + Here we need to restrict some option values to apply only to the + common database:

+ +
+odb -m dynamic -d common -d sqlite -d pgsql \
+--hxx-prologue "common:#include \"person-export.hxx\"" \
+--export-symbol common:PERSON_EXPORT --extern-symbol common:PERSON_EXTERN \
+person.hxx
+  
+ +

The second step in packaging the generated code into DLLs is to + decide where to place the generated common interface code. One + option is to place it into a DLL of its own so that we will end + up with (replace *.dll with lib*.so for + Unix): person.dll plus person-sqlite.dll and + person-pgsql.dll, which both link to person.dll, + as well as person.exe, which links to person.dll + and dynamically loads person-sqlite.dll + and/or person-pgsql.dll. If this is the organization + that you prefer, then the next step is to build all the DLLs as you + normally would any other DLL, placing person-odb.cxx + and person.cxx into person.dll, + person-odb-sqlite.cxx into person-sqlite.dll, + etc. Note that in the pure dynamic multi-database support, + person-sqlite.dll and person-pgsql.dll + do not export any symbols.

+ +

We can improve on the above organization by getting rid of + person.dll, which is not really necessary unless + we have multiple executables sharing the same database support. + To achieve this, we will place person-odb.cxx into + person.exe and export its symbols from the executable + instead of a DLL. Exporting symbols from an executable is a seldom + used functionality, especially on Windows, however, it is well + supported on both Windows and most Unix platforms. Note also that + this approach won't work if we also use one of the databases in the + static mode.

+ +

On Windows all we have to do is place person-odb.cxx + into the executable and compile it as we would in a DLL (that is, + with the PERSON_BUILD_DLL macro defined). If Windows + linker detects that an executable exports any symbols, then it + will automatically create the corresponding import library + (person.lib in our case). We then use this import + library to build person-sqlite.dll and + person-pgsql.dll as before.

+ +

To export symbols from an executable on GNU/Linux all we need to + do is add the -rdynamic option when linking our + executable.

+ + + + +
+

14 MySQL Database

To generate support code for the MySQL database you will need to pass the "--database mysql" @@ -13746,7 +14624,7 @@ class person library (libodb-mysql). All MySQL-specific ODB classes are defined in the odb::mysql namespace.

-

13.1 MySQL Type Mapping

+

14.1 MySQL Type Mapping

The following table summarizes the default mapping between basic C++ value types and MySQL database types. This mapping can be @@ -13939,7 +14817,7 @@ class object Section 12.7, "Database Type Mapping Pragmas".

-

13.2 MySQL Database Class

+

14.2 MySQL Database Class

The MySQL database class has the following interface:

@@ -14099,7 +14977,7 @@ namespace odb

This constructor throws the odb::mysql::cli_exception exception if the MySQL option values are missing or invalid. - See section Section 13.4, "MySQL Exceptions" + See section Section 14.4, "MySQL Exceptions" for more information on this exception.

The static print_usage() function prints the list of options @@ -14118,10 +14996,10 @@ namespace odb

The connection() function returns a pointer to the MySQL database connection encapsulated by the odb::mysql::connection class. For more information - on mysql::connection, refer to Section - 13.3, "MySQL Connection and Connection Factory".

+ on mysql::connection, refer to Section + 14.3, "MySQL Connection and Connection Factory".

-

13.3 MySQL Connection and Connection Factory

+

14.3 MySQL Connection and Connection Factory

The mysql::connection class has the following interface:

@@ -14313,7 +15191,7 @@ main (int argc, char* argv[]) } -

13.4 MySQL Exceptions

+

14.4 MySQL Exceptions

The MySQL ODB runtime library defines the following MySQL-specific exceptions:

@@ -14365,12 +15243,12 @@ namespace odb what() function returns a human-readable description of an error.

-

13.5 MySQL Limitations

+

14.5 MySQL Limitations

The following sections describe MySQL-specific limitations imposed by the current MySQL and ODB runtime versions.

-

13.5.1 Foreign Key Constraints

+

14.5.1 Foreign Key Constraints

ODB relies on standard SQL behavior which requires that foreign key constraints checking is deferred until the transaction is @@ -14381,7 +15259,7 @@ namespace odb foreign key definitions commented out. They are retained only for documentation.

-

13.6 MySQL Index Definitions

+

14.6 MySQL Index Definitions

When the index pragma (Section 12.6, "Index Definition Pragmas") is used to define a MySQL index, @@ -14410,7 +15288,7 @@ class object


-

14 SQLite Database

+

15 SQLite Database

To generate support code for the SQLite database you will need to pass the "--database sqlite" @@ -14419,7 +15297,7 @@ class object library (libodb-sqlite). All SQLite-specific ODB classes are defined in the odb::sqlite namespace.

-

14.1 SQLite Type Mapping

+

15.1 SQLite Type Mapping

The following table summarizes the default mapping between basic C++ value types and SQLite database types. This mapping can be @@ -14597,7 +15475,7 @@ class object Section 12.7, "Database Type Mapping Pragmas".

-

14.2 SQLite Database Class

+

15.2 SQLite Database Class

The SQLite database class has the following interface:

@@ -14670,7 +15548,7 @@ namespace odb values, refer to the sqlite3_open_v2() function description in the SQLite C API documentation. The foreign_keys argument specifies whether foreign key constraints checking - should be enabled. See Section 14.5.3, + should be enabled. See Section 15.5.3, "Foreign Key Constraints" for more information on foreign keys. The vfs argument specifies the SQLite virtual file system module that should be used to access the @@ -14726,7 +15604,7 @@ auto_ptr<odb::database> db (

The third constructor throws the odb::sqlite::cli_exception exception if the SQLite option values are missing or invalid. - See Section 14.4, "SQLite Exceptions" + See Section 15.4, "SQLite Exceptions" for more information on this exception.

The static print_usage() function prints the list of options @@ -14755,10 +15633,10 @@ auto_ptr<odb::database> db (

The connection() function returns a pointer to the SQLite database connection encapsulated by the odb::sqlite::connection class. For more information - on sqlite::connection, refer to Section - 14.3, "SQLite Connection and Connection Factory".

+ on sqlite::connection, refer to Section + 15.3, "SQLite Connection and Connection Factory".

-

14.3 SQLite Connection and Connection Factory

+

15.3 SQLite Connection and Connection Factory

The sqlite::connection class has the following interface:

@@ -14803,7 +15681,7 @@ namespace odb functions allow us to start an immediate and an exclusive SQLite transaction on the connection, respectively. Their semantics are equivalent to the corresponding functions defined in the - sqlite::database class (Section 14.2, + sqlite::database class (Section 15.2, "SQLite Database Class"). The handle() accessor returns the SQLite handle corresponding to the connection.

@@ -15003,7 +15881,7 @@ main (int argc, char* argv[]) } -

14.4 SQLite Exceptions

+

15.4 SQLite Exceptions

The SQLite ODB runtime library defines the following SQLite-specific exceptions:

@@ -15056,12 +15934,12 @@ namespace odb of an error.

-

14.5 SQLite Limitations

+

15.5 SQLite Limitations

The following sections describe SQLite-specific limitations imposed by the current SQLite and ODB runtime versions.

-

14.5.1 Query Result Caching

+

15.5.1 Query Result Caching

SQLite ODB runtime implementation does not perform query result caching (Section 4.4, "Query Result") even when explicitly @@ -15076,7 +15954,7 @@ namespace odb thrown. Future versions of the SQLite ODB runtime library may add support for result caching.

-

14.5.2 Automatic Assignment of Object Ids

+

15.5.2 Automatic Assignment of Object Ids

Due to SQLite API limitations, every automatically assigned object id (Section 12.4.2, "auto") should have @@ -15102,7 +15980,7 @@ class person }; -

14.5.3 Foreign Key Constraints

+

15.5.3 Foreign Key Constraints

By default the SQLite ODB runtime enables foreign key constraints checking (PRAGMA foreign_keys=ON). You can disable foreign @@ -15166,7 +16044,7 @@ CREATE TABLE Employee ( -

14.5.4 Constraint Violations

+

15.5.4 Constraint Violations

Due to the granularity of the SQLite error codes, it is impossible to distinguish between the duplicate primary key and other constraint @@ -15175,7 +16053,7 @@ CREATE TABLE Employee ( object_already_persistent exception (Section 3.14, "ODB Exceptions").

-

14.5.5 Sharing of Queries

+

15.5.5 Sharing of Queries

As discussed in Section 4.3, "Executing a Query", a query instance that does not have any by-reference parameters is @@ -15184,7 +16062,7 @@ CREATE TABLE Employee ( functionality. Future versions of the library will remove this limitation.

-

14.6 SQLite Index Definitions

+

15.6 SQLite Index Definitions

When the index pragma (Section 12.6, "Index Definition Pragmas") is used to define an SQLite index, @@ -15213,7 +16091,7 @@ class object


-

15 PostgreSQL Database

+

16 PostgreSQL Database

To generate support code for the PostgreSQL database you will need to pass the "--database pgsql" @@ -15227,7 +16105,7 @@ class object of the messaging protocol version 3.0. For this reason, ODB supports only PostgreSQL version 7.4 and later.

-

15.1 PostgreSQL Type Mapping

+

16.1 PostgreSQL Type Mapping

The following table summarizes the default mapping between basic C++ value types and PostgreSQL database types. This mapping can be @@ -15405,7 +16283,7 @@ class object For more information, refer to Section 12.7, "Database Type Mapping Pragmas".

-

15.2 PostgreSQL Database Class

+

16.2 PostgreSQL Database Class

The PostgreSQL database class has the following interface:

@@ -15525,7 +16403,7 @@ namespace odb

This constructor throws the odb::pgsql::cli_exception exception if the PostgreSQL option values are missing or invalid. - See section Section 15.4, "PostgreSQL Exceptions" + See section Section 16.4, "PostgreSQL Exceptions" for more information on this exception.

The static print_usage() function prints the list of options @@ -15551,10 +16429,10 @@ namespace odb

The connection() function returns a pointer to the PostgreSQL database connection encapsulated by the odb::pgsql::connection class. For more information - on pgsql::connection, refer to Section - 15.3, "PostgreSQL Connection and Connection Factory".

+ on pgsql::connection, refer to Section + 16.3, "PostgreSQL Connection and Connection Factory".

-

15.3 PostgreSQL Connection and Connection Factory

+

16.3 PostgreSQL Connection and Connection Factory

The pgsql::connection class has the following interface:

@@ -15735,7 +16613,7 @@ main (int argc, char* argv[]) } -

15.4 PostgreSQL Exceptions

+

16.4 PostgreSQL Exceptions

The PostgreSQL ODB runtime library defines the following PostgreSQL-specific exceptions:

@@ -15784,12 +16662,12 @@ namespace odb what() function returns a human-readable description of an error.

-

15.5 PostgreSQL Limitations

+

16.5 PostgreSQL Limitations

The following sections describe PostgreSQL-specific limitations imposed by the current PostgreSQL and ODB runtime versions.

-

15.5.1 Query Result Caching

+

16.5.1 Query Result Caching

The PostgreSQL ODB runtime implementation will always return a cached query result (Section 4.4, "Query Result") @@ -15797,7 +16675,7 @@ namespace odb PostgreSQL client library (libpq) which does not support uncached (streaming) query results.

-

15.5.2 Foreign Key Constraints

+

16.5.2 Foreign Key Constraints

ODB relies on standard SQL behavior which requires that foreign key constraints checking is deferred until the @@ -15815,7 +16693,7 @@ CREATE TABLE Employee ( employer BIGINT REFERENCES Employer(id) INITIALLY DEFERRED); -

15.5.3 Unique Constraint Violations

+

16.5.3 Unique Constraint Violations

Due to the granularity of the PostgreSQL error codes, it is impossible to distinguish between the duplicate primary key and other unique @@ -15824,7 +16702,7 @@ CREATE TABLE Employee ( errors to the object_already_persistent exception (Section 3.14, "ODB Exceptions").

-

15.5.4 Date-Time Format

+

16.5.4 Date-Time Format

ODB expects the PostgreSQL server to use integers as a binary format for the date-time types, which is the default for most @@ -15839,14 +16717,14 @@ CREATE TABLE Employee ( SHOW integer_datetimes -

15.5.5 Timezones

+

16.5.5 Timezones

ODB does not currently natively support the PostgreSQL date-time types with timezone information. However, these types can be accessed by mapping them to one of the natively supported types, as discussed in Section 12.7, "Database Type Mapping Pragmas".

-

15.5.6 NUMERIC Type Support

+

16.5.6 NUMERIC Type Support

Support for the PostgreSQL NUMERIC type is limited to providing a binary buffer containing the binary representation @@ -15858,7 +16736,7 @@ SHOW integer_datetimes Type Mapping Pragmas".

-

15.6 PostgreSQL Index Definitions

+

16.6 PostgreSQL Index Definitions

When the index pragma (Section 12.6, "Index Definition Pragmas") is used to define a PostgreSQL index, @@ -15897,7 +16775,7 @@ class object


-

16 Oracle Database

+

17 Oracle Database

To generate support code for the Oracle database you will need to pass the "--database oracle" @@ -15906,7 +16784,7 @@ class object library (libodb-oracle). All Oracle-specific ODB classes are defined in the odb::oracle namespace.

-

16.1 Oracle Type Mapping

+

17.1 Oracle Type Mapping

The following table summarizes the default mapping between basic C++ value types and Oracle database types. This mapping can be @@ -16081,7 +16959,7 @@ class object Section 12.7, "Database Type Mapping Pragmas".

-

16.2 Oracle Database Class

+

17.2 Oracle Database Class

The Oracle database class encapsulates the OCI environment handle as well as the database connection string and user credentials @@ -16206,7 +17084,7 @@ namespace odb

This constructor throws the odb::oracle::cli_exception exception if the Oracle option values are missing or invalid. See section - Section 16.4, "Oracle Exceptions" for more + Section 17.4, "Oracle Exceptions" for more information on this exception.

The static print_usage() function prints the list of options @@ -16256,10 +17134,10 @@ namespace odb

The connection() function returns a pointer to the Oracle database connection encapsulated by the odb::oracle::connection class. For more information - on oracle::connection, refer to Section - 16.3, "Oracle Connection and Connection Factory".

+ on oracle::connection, refer to Section + 17.3, "Oracle Connection and Connection Factory".

-

16.3 Oracle Connection and Connection Factory

+

17.3 Oracle Connection and Connection Factory

The oracle::connection class has the following interface:

@@ -16461,7 +17339,7 @@ main (int argc, char* argv[]) } -

16.4 Oracle Exceptions

+

17.4 Oracle Exceptions

The Oracle ODB runtime library defines the following Oracle-specific exceptions:

@@ -16542,12 +17420,12 @@ namespace odb condition. The what() function returns a human-readable description of an error.

-

16.5 Oracle Limitations

+

17.5 Oracle Limitations

The following sections describe Oracle-specific limitations imposed by the current Oracle and ODB runtime versions.

-

16.5.1 Identifier Truncation

+

17.5.1 Identifier Truncation

Oracle limits the length of database identifiers (table, column, etc., names) to 30 characters. The ODB compiler automatically truncates @@ -16592,7 +17470,7 @@ class long_class_name }; -

16.5.2 Query Result Caching

+

17.5.2 Query Result Caching

Oracle ODB runtime implementation does not perform query result caching (Section 4.4, "Query Result") even when explicitly @@ -16607,7 +17485,7 @@ class long_class_name always thrown. Future versions of the Oracle ODB runtime library may add support for result caching.

-

16.5.3 Foreign Key Constraints

+

17.5.3 Foreign Key Constraints

ODB relies on standard SQL behavior which requires that foreign key constraints checking is deferred until the @@ -16626,7 +17504,7 @@ CREATE TABLE Employee ( DEFERRABLE INITIALLY DEFERRED); -

16.5.4 Unique Constraint Violations

+

17.5.4 Unique Constraint Violations

Due to the granularity of the Oracle error codes, it is impossible to distinguish between the duplicate primary key and other unique @@ -16635,7 +17513,7 @@ CREATE TABLE Employee ( errors to the object_already_persistent exception (Section 3.14, "ODB Exceptions").

-

16.5.5 Large FLOAT and +

17.5.5 Large FLOAT and NUMBER Types

The Oracle FLOAT type with a binary precision greater @@ -16661,21 +17539,21 @@ CREATE TABLE Employee ( without any range and scale) can be extracted into the C++ float and double types.

-

16.5.6 Timezones

+

17.5.6 Timezones

ODB does not currently support the Oracle date-time types with timezone information. However, these types can be accessed by mapping them to one of the natively supported types, as discussed in Section 12.7, "Database Type Mapping Pragmas".

-

16.5.7 LONG Types

+

17.5.7 LONG Types

ODB does not support the deprecated Oracle LONG and LONG RAW data types. However, these types can be accessed by mapping them to one of the natively supported types, as discussed in Section 12.7, "Database Type Mapping Pragmas".

-

16.5.8 LOB Types and By-Value Accessors/Modifiers

+

17.5.8 LOB Types and By-Value Accessors/Modifiers

As discussed in Section 12.4.5, "get/set/access", by-value @@ -16686,7 +17564,7 @@ CREATE TABLE Employee ( data members. As a result, by-reference accessors and modifiers should be used for these data types.

-

16.6 Oracle Index Definitions

+

17.6 Oracle Index Definitions

When the index pragma (Section 12.6, "Index Definition Pragmas") is used to define an Oracle index, @@ -16720,7 +17598,7 @@ class object


-

17 Microsoft SQL Server Database

+

18 Microsoft SQL Server Database

To generate support code for the SQL Server database you will need to pass the "--database mssql" @@ -16729,7 +17607,7 @@ class object library (libodb-mssql). All SQL Server-specific ODB classes are defined in the odb::mssql namespace.

-

17.1 SQL Server Type Mapping

+

18.1 SQL Server Type Mapping

The following table summarizes the default mapping between basic C++ value types and SQL Server database types. This mapping can be @@ -16938,7 +17816,7 @@ class object than once as part of a query result iteration (Section 4.4, "Query Result"). Any such attempt will result in the odb::mssql::long_data_reload exception - (Section 17.4, "SQL Server Exceptions"). For + (Section 18.4, "SQL Server Exceptions"). For example:

@@ -16996,7 +17874,7 @@ t.commit ();
      For more information, refer to Section 12.7, "Database
      Type Mapping Pragmas".

-

17.2 SQL Server Database Class

+

18.2 SQL Server Database Class

The SQL Server database class encapsulates the ODBC environment handle as well as the server instance address and @@ -17273,7 +18151,7 @@ odb::mssql::database dbA ("test",

This constructor throws the odb::mssql::cli_exception exception if the SQL Server option values are missing or invalid. See - section Section 17.4, "SQL Server Exceptions" for + section Section 18.4, "SQL Server Exceptions" for more information on this exception.

The static print_usage() function prints the list of options @@ -17301,10 +18179,10 @@ odb::mssql::database dbA ("test",

The connection() function returns a pointer to the SQL Server database connection encapsulated by the odb::mssql::connection class. For more information - on mssql::connection, refer to Section - 17.3, "SQL Server Connection and Connection Factory".

+ on mssql::connection, refer to Section + 18.3, "SQL Server Connection and Connection Factory".

-

17.3 SQL Server Connection and Connection Factory

+

18.3 SQL Server Connection and Connection Factory

The mssql::connection class has the following interface:

@@ -17499,7 +18377,7 @@ main (int argc, char* argv[]) }
-

17.4 SQL Server Exceptions

+

18.4 SQL Server Exceptions

The SQL Server ODB runtime library defines the following SQL Server-specific exceptions:

@@ -17580,14 +18458,14 @@ namespace odb

The odb::mssql::long_data_reload is thrown if an attempt is made to re-load an object or view with long data as part of a query result iteration. For more information, refer - to Section 17.1, "SQL Server Type Mapping".

+ to Section 18.1, "SQL Server Type Mapping".

-

17.5 SQL Server Limitations

+

18.5 SQL Server Limitations

The following sections describe SQL Server-specific limitations imposed by the current SQL Server and ODB runtime versions.

-

17.5.1 Query Result Caching

+

18.5.1 Query Result Caching

SQL Server ODB runtime implementation does not perform query result caching (Section 4.4, "Query Result") even when @@ -17602,7 +18480,7 @@ namespace odb 3.14, "ODB Exceptions") is always thrown. Future versions of the SQL Server ODB runtime library may add support for result caching.

-

17.5.2 Foreign Key Constraints

+

18.5.2 Foreign Key Constraints

ODB relies on standard SQL behavior which requires that foreign key constraints checking is deferred until the transaction is @@ -17611,7 +18489,7 @@ namespace odb the ODB compiler for SQL Server have foreign key definitions commented out. They are retained only for documentation.

-

17.5.3 Unique Constraint Violations

+

18.5.3 Unique Constraint Violations

Due to the granularity of the ODBC error codes, it is impossible to distinguish between the duplicate primary key and other unique @@ -17620,7 +18498,7 @@ namespace odb errors to the object_already_persistent exception (Section 3.14, "ODB Exceptions").

-

17.5.4 Multi-threaded Windows Applications

+

18.5.4 Multi-threaded Windows Applications

Multi-threaded Windows applications must use the _beginthread()/_beginthreadex() and @@ -17629,7 +18507,7 @@ namespace odb Win32 functions to start and terminate threads. This is a limitation of the ODBC implementation on Windows.

-

17.5.5 Affected Row Count and DDL Statements

+

18.5.5 Affected Row Count and DDL Statements

SQL Server always returns zero as the number of affected rows for DDL statements. In particular, this means that the @@ -17637,7 +18515,7 @@ namespace odb "Executing Native SQL Statements") function will always return zero for such statements.

-

17.5.6 Long Data and Automatically Assigned Object Ids

+

18.5.6 Long Data and Automatically Assigned Object Ids

SQL Server 2005 has a bug that causes it to fail on an INSERT statement with the OUTPUT clause (used to return @@ -17655,7 +18533,7 @@ namespace odb by passing the --mssql-server-version 9.0 ODB compiler option.

-

17.5.7 Long Data and By-Value Accessors/Modifiers

+

18.5.7 Long Data and By-Value Accessors/Modifiers

As discussed in Section 12.4.5, "get/set/access", by-value @@ -17665,7 +18543,7 @@ namespace odb by-reference accessors and modifiers should be used for these data types.

-

17.6 SQL Server Index Definitions

+

18.6 SQL Server Index Definitions

When the index pragma (Section 12.6, "Index Definition Pragmas") is used to define an SQL Server index, @@ -17702,9 +18580,9 @@ class object and libraries. It consists of the following chapters.

- - - + + +
18Profiles Introduction
19Boost Profile
20Qt Profile
19Profiles Introduction
20Boost Profile
21Qt Profile
@@ -17712,7 +18590,7 @@ class object
-

18 Profiles Introduction

+

19 Profiles Introduction

ODB profiles are a generic mechanism for integrating ODB with widely-used C++ frameworks and libraries. A profile provides glue @@ -17766,7 +18644,7 @@ odb --profile boost/date-time ...


-

19 Boost Profile

+

20 Boost Profile

The ODB profile implementation for Boost is provided by the libodb-boost library and consists of multiple sub-profiles @@ -17791,7 +18669,7 @@ odb --profile boost/date-time ... that can be thrown by the Boost sub-profiles are described in the following sections.

-

19.1 Smart Pointers Library

+

20.1 Smart Pointers Library

The smart-ptr sub-profile provides persistence support for a subset of smart pointers from the Boost @@ -17861,7 +18739,7 @@ class employee this behavior, add the --default-pointer option specifying the alternative pointer type after the --profile option.

-

19.2 Unordered Containers Library

+

20.2 Unordered Containers Library

The unordered sub-profile provides persistence support for the containers from the Boost unordered library. To enable @@ -17887,7 +18765,7 @@ class person }; -

19.3 Multi-Index Container Library

+

20.3 Multi-Index Container Library

The multi-index sub-profile provides persistence support for boost::multi_index_container from the Boost Multi-Index @@ -17972,7 +18850,7 @@ class person }; -

19.4 Optional Library

+

20.4 Optional Library

The optional sub-profile provides persistence support for the boost::optional container from the Boost @@ -18004,7 +18882,7 @@ class person this profile is used, the NULL values are automatically enabled for data members of the boost::optional type.

-

19.5 Date Time Library

+

20.5 Date Time Library

The date-time sub-profile provides persistence support for a subset of types from the Boost date_time library. It is @@ -18077,7 +18955,7 @@ namespace odb exceptions are thrown are database system dependent and are discussed in more detail in the following sub-sections.

-

19.5.1 MySQL Database Type Mapping

+

20.5.1 MySQL Database Type Mapping

The following table summarizes the default mapping between the currently supported Boost date_time types and the MySQL database @@ -18138,7 +19016,7 @@ class person the out_of_range exception. Refer to the MySQL documentation for more information on the MySQL data type ranges.

-

19.5.2 SQLite Database Type Mapping

+

20.5.2 SQLite Database Type Mapping

The following table summarizes the default mapping between the currently supported Boost date_time types and the SQLite database @@ -18216,7 +19094,7 @@ class person will result in the out_of_range exception.

-

19.5.3 PostgreSQL Database Type Mapping

+

20.5.3 PostgreSQL Database Type Mapping

The following table summarizes the default mapping between the currently supported Boost date_time types and the PostgreSQL database @@ -18267,7 +19145,7 @@ class person result in the special_value exception.

-

19.5.4 Oracle Database Type Mapping

+

20.5.4 Oracle Database Type Mapping

The following table summarizes the default mapping between the currently supported Boost date_time types and the Oracle database @@ -18329,7 +19207,7 @@ class person the special_value exception.

-

19.5.5 SQL Server Database Type Mapping

+

20.5.5 SQL Server Database Type Mapping

The following table summarizes the default mapping between the currently supported Boost date_time types and the SQL Server database @@ -18400,7 +19278,7 @@ class person posix_time::time_duration value out of this range will result in the value_out_of_range exception.

-

19.6 Uuid Library

+

20.6 Uuid Library

The uuid sub-profile provides persistence support for the uuid type from the Boost uuid library. To @@ -18432,7 +19310,7 @@ class object }; -

19.6.1 MySQL Database Type Mapping

+

20.6.1 MySQL Database Type Mapping

The following table summarizes the default mapping between the Boost uuid type and the MySQL database type.

@@ -18452,7 +19330,7 @@ class object
-

19.6.2 SQLite Database Type Mapping

+

20.6.2 SQLite Database Type Mapping

The following table summarizes the default mapping between the Boost uuid type and the SQLite database type.

@@ -18472,7 +19350,7 @@ class object -

19.6.3 PostgreSQL Database Type Mapping

+

20.6.3 PostgreSQL Database Type Mapping

The following table summarizes the default mapping between the Boost uuid type and the PostgreSQL database type.

@@ -18492,7 +19370,7 @@ class object -

19.6.4 Oracle Database Type Mapping

+

20.6.4 Oracle Database Type Mapping

The following table summarizes the default mapping between the Boost uuid type and the Oracle database type.

@@ -18512,7 +19390,7 @@ class object -

19.6.5 SQL Server Database Type Mapping

+

20.6.5 SQL Server Database Type Mapping

The following table summarizes the default mapping between the Boost uuid type and the SQL Server database type.

@@ -18537,7 +19415,7 @@ class object
-

20 Qt Profile

+

21 Qt Profile

The ODB profile implementation for Qt is provided by the libodb-qt library and consists of multiple sub-profiles @@ -18563,7 +19441,7 @@ class object that can be thrown by the Qt sub-profiles are described in the following sections.

-

20.1 Basic Types

+

21.1 Basic Types

The basic sub-profile provides persistence support for basic types defined by Qt. To enable only this profile, pass @@ -18609,7 +19487,7 @@ class object }; -

20.1.1 MySQL Database Type Mapping

+

21.1.1 MySQL Database Type Mapping

The following table summarizes the default mapping between the currently supported basic Qt types and the MySQL database types.

@@ -18672,7 +19550,7 @@ class Person -

20.1.2 SQLite Database Type Mapping

+

21.1.2 SQLite Database Type Mapping

The following table summarizes the default mapping between the currently supported basic Qt types and the SQLite database types.

@@ -18708,7 +19586,7 @@ class Person are stored as a NULL value if their isNull() member function returns true.

-

20.1.3 PostgreSQL Database Type Mapping

+

21.1.3 PostgreSQL Database Type Mapping

The following table summarizes the default mapping between the currently supported basic Qt types and the PostgreSQL database types.

@@ -18763,7 +19641,7 @@ class Person }; -

20.1.4 Oracle Database Type Mapping

+

21.1.4 Oracle Database Type Mapping

The following table summarizes the default mapping between the currently supported basic Qt types and the Oracle database types.

@@ -18822,7 +19700,7 @@ class Person }; -

20.1.5 SQL Server Database Type Mapping

+

21.1.5 SQL Server Database Type Mapping

The following table summarizes the default mapping between the currently supported basic Qt types and the SQL Server database types.

@@ -18889,7 +19767,7 @@ class Person }; -

20.2 Smart Pointers

+

21.2 Smart Pointers

The smart-ptr sub-profile provides persistence support the Qt smart pointers. To enable only this profile, pass @@ -18958,7 +19836,7 @@ class Employee this behavior, add the --default-pointer option specifying the alternative pointer type after the --profile option.

-

20.3 Containers Library

+

21.3 Containers Library

The container sub-profile provides persistence support for Qt containers. To enable only this profile, pass @@ -18983,7 +19861,7 @@ class Person }; -

20.4 Date Time Types

+

21.4 Date Time Types

The date-time sub-profile provides persistence support for the Qt date-time types. To enable only this profile, pass @@ -19036,7 +19914,7 @@ namespace odb system dependent and is discussed in more detail in the following sub-sections.

-

20.4.1 MySQL Database Type Mapping

+

21.4.1 MySQL Database Type Mapping

The following table summarizes the default mapping between the currently supported Qt date-time types and the MySQL database types.

@@ -19095,7 +19973,7 @@ class Person the MySQL documentation for more information on the MySQL data type ranges.

-

20.4.2 SQLite Database Type Mapping

+

21.4.2 SQLite Database Type Mapping

The following table summarizes the default mapping between the currently supported Qt date-time types and the SQLite database types.

@@ -19157,7 +20035,7 @@ class Person epoch) as an SQLite INTEGER will result in the out_of_range exception.

-

20.4.3 PostgreSQL Database Type Mapping

+

21.4.3 PostgreSQL Database Type Mapping

The following table summarizes the default mapping between the currently supported Qt date-time types and the PostgreSQL database types.

@@ -19193,7 +20071,7 @@ class Person QDateTime types are stored as a NULL value if their isNull() member function returns true.

-

20.4.4 Oracle Database Type Mapping

+

21.4.4 Oracle Database Type Mapping

The following table summarizes the default mapping between the currently supported Qt date-time types and the Oracle database types.

@@ -19247,7 +20125,7 @@ class person }; -

20.4.5 SQL Server Database Type Mapping

+

21.4.5 SQL Server Database Type Mapping

The following table summarizes the default mapping between the currently supported Qt date-time types and the SQL Server database types.

diff --git a/doc/odb-prologue.1 b/doc/odb-prologue.1 index 2e667d0..23d7d5c 100644 --- a/doc/odb-prologue.1 +++ b/doc/odb-prologue.1 @@ -38,7 +38,8 @@ For an input file in the form .B name.hxx (other file extensions can be used instead of .BR .hxx ), -the following C++ files are generated: +in the single-database mode (the default), the generated C++ files by +default have the following names: .B name-odb.hxx (header file), .B name-odb.ixx @@ -51,7 +52,9 @@ option is specified), and .B --generate-schema option is specified and the .B sql -schema format is requested, the +schema format is requested (see +.BR --schema-format ), +the .B name.sql database schema file is generated. If the .B separate @@ -59,6 +62,23 @@ schema format is requested, the database creation code is generated into the separate .B name-schema.cxx file. + + +In the multi-database mode (see the +.B --multi-database +option below), the generated files corresponding to the +.B common +database have the same names as in the single-database mode. For other +databases, the file names include the database name: +.BR name-odb-\fIdb\fB.hxx , +.BR name-odb-\fIdb\fB.ixx , +.BR name-odb-\fIdb\fB.cxx , +.BR name-\fIdb\fB.sql , +and +.B name-schema-\fIdb\fB.cxx +(where +.I db +is the database name). .\" .\" .\" diff --git a/doc/odb-prologue.xhtml b/doc/odb-prologue.xhtml index d610b2b..7acd11d 100644 --- a/doc/odb-prologue.xhtml +++ b/doc/odb-prologue.xhtml @@ -67,17 +67,30 @@

For an input file in the form name.hxx (other file extensions can be used instead of .hxx), - the following C++ files are generated: + in the single-database mode (the default), the generated C++ files + by default have the following names: name-odb.hxx (header file), name-odb.ixx (inline file, generated unless the --suppress-inline option is specified), and name-odb.cxx (source file). Additionally, if the --generate-schema option is - specified and the sql schema format is requested, - the name.sql database schema file is generated. If - the separate schema format is requested, the database - creation code is generated into the separate - name-schema.cxx file.

+ specified and the sql schema format is requested (see + --schema-format), the name.sql + database schema file is generated. If the separate + schema format is requested, the database creation code is generated + into the separate name-schema.cxx file.

+ +

In the multi-database mode (see the --multi-database + option below), the generated files corresponding to the + common database have the same names as in the + single-database mode. For other databases, the file names include + the database name: + name-odb-db.hxx, + name-odb-db.ixx, + name-odb-db.cxx, + name-db.sql, and + name-schema-db.cxx + (where db is the database name).

OPTIONS

diff --git a/odb/options.cli b/odb/options.cli index 6da45e8..d756494 100644 --- a/odb/options.cli +++ b/odb/options.cli @@ -49,14 +49,23 @@ class options { "", "Generate code for the database. Valid values are \cb{mssql}, - \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common}." + \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common} + (multi-database mode only)." }; ::multi_database --multi-database | -m = ::multi_database::disabled { "", "Enable multi-database support and specify its type. Valid values - for this option are \cb{static} and \cb{dynamic}." + for this option are \cb{static} and \cb{dynamic}. + + In the multi-database mode, options that determine the kind (for + example, \cb{--schema-format}), names (for example, + \cb{--odb-file-suffix}), or content (for example, prologue and + epilogue options) of the output files can be prefixed with the + database name followed by a colon, for example, \cb{mysql:value}. + This restricts the value of such an option to only apply to + generated files corresponding to this database." }; ::database --default-database @@ -276,22 +285,29 @@ class options database_map --odb-file-suffix { "", - "Use instead of the default \cb{-odb} to construct the names - of the generated C++ files." + "Use to construct the names of the generated C++ files. In + the single-database mode the default value for this option is \cb{-odb}. + In the multi-database mode it is \cb{-odb} for the files corresponding + to the \cb{common} database and \c{\b{-odb-}\i{db}} (where \ci{db} is + the database name) for other databases." }; database_map --sql-file-suffix { "", "Use to construct the name of the generated schema SQL file. - By default no suffix is used." + In the single-database mode by default no suffix is used. In the + multi-database mode the default value for this option is + \c{\b{-}\i{db}} (where \ci{db} is the database name)." }; database_map --schema-file-suffix { "", - "Use instead of the default \cb{-schema} to construct the name - of the generated schema C++ source file. See the \cb{--schema-format} + "Use to construct the name of the generated schema C++ source + file. In the single-database mode the default value for this option is + \cb{-schema}. In the multi-database mode it is \c{\b{-schema-}\i{db}} + (where \ci{db} is the database name). See the \cb{--schema-format} option for details." }; -- cgit v1.1