summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes19
-rw-r--r--.gitignore1
-rw-r--r--NEWS894
-rw-r--r--buildfile6
-rw-r--r--odb/.gitignore29
-rw-r--r--odb/GPLv3674
-rw-r--r--odb/INSTALL36
-rw-r--r--odb/LICENSE21
l---------odb/NEWS1
-rw-r--r--odb/README20
-rw-r--r--odb/build/.gitignore3
-rw-r--r--odb/build/bootstrap.build10
-rw-r--r--odb/build/export.build9
-rw-r--r--odb/build/root.build110
-rw-r--r--odb/buildfile9
-rw-r--r--odb/doc/.gitignore5
-rw-r--r--odb/doc/buildfile191
-rw-r--r--odb/doc/default.css323
-rw-r--r--odb/doc/manual.html2ps69
-rw-r--r--odb/doc/manual.xhtml27073
-rw-r--r--odb/doc/odb-arch.pngbin0 -> 16883 bytes
-rw-r--r--odb/doc/odb-arch.svg410
-rw-r--r--odb/doc/odb-epilogue.1153
-rw-r--r--odb/doc/odb-epilogue.xhtml126
-rw-r--r--odb/doc/odb-flow.pngbin0 -> 39092 bytes
-rw-r--r--odb/doc/odb-flow.svg822
-rw-r--r--odb/doc/odb-prologue.184
-rw-r--r--odb/doc/odb-prologue.xhtml88
-rw-r--r--odb/doc/pregenerated/odb.1799
-rw-r--r--odb/doc/pregenerated/odb.xhtml978
-rw-r--r--odb/manifest31
-rw-r--r--odb/odb/.gitignore2
-rw-r--r--odb/odb/buildfile161
-rw-r--r--odb/odb/common-query.cxx1413
-rw-r--r--odb/odb/common-query.hxx279
-rw-r--r--odb/odb/common.cxx584
-rw-r--r--odb/odb/common.hxx468
-rw-r--r--odb/odb/context.cxx3384
-rw-r--r--odb/odb/context.hxx1941
-rw-r--r--odb/odb/context.ixx26
-rw-r--r--odb/odb/cxx-lexer.cxx364
-rw-r--r--odb/odb/cxx-lexer.hxx131
-rw-r--r--odb/odb/cxx-token.hxx30
-rw-r--r--odb/odb/diagnostics.cxx136
-rw-r--r--odb/odb/diagnostics.hxx96
-rw-r--r--odb/odb/emitter.cxx50
-rw-r--r--odb/odb/emitter.hxx47
-rw-r--r--odb/odb/features.hxx25
-rw-r--r--odb/odb/gcc-fwd.hxx64
-rw-r--r--odb/odb/gcc.hxx215
-rw-r--r--odb/odb/generate.hxx31
-rw-r--r--odb/odb/generator.cxx1044
-rw-r--r--odb/odb/generator.hxx22
-rw-r--r--odb/odb/header.cxx900
-rw-r--r--odb/odb/include.cxx738
-rw-r--r--odb/odb/inline.cxx506
-rw-r--r--odb/odb/instance.cxx73
-rw-r--r--odb/odb/instance.hxx295
-rw-r--r--odb/odb/location.cxx13
-rw-r--r--odb/odb/location.hxx25
-rw-r--r--odb/odb/lookup.cxx374
-rw-r--r--odb/odb/lookup.hxx97
-rw-r--r--odb/odb/odb.cxx2178
-rw-r--r--odb/odb/option-functions.cxx132
-rw-r--r--odb/odb/option-functions.hxx12
-rw-r--r--odb/odb/option-parsers.hxx199
-rw-r--r--odb/odb/option-types.cxx352
-rw-r--r--odb/odb/option-types.hxx391
-rw-r--r--odb/odb/options.cli1086
-rw-r--r--odb/odb/parser.cxx2403
-rw-r--r--odb/odb/parser.hxx37
-rw-r--r--odb/odb/plugin.cxx458
-rw-r--r--odb/odb/pragma.cxx4442
-rw-r--r--odb/odb/pragma.hxx287
-rw-r--r--odb/odb/pregenerated/odb/options.cxx4034
-rw-r--r--odb/odb/pregenerated/odb/options.hxx2340
-rw-r--r--odb/odb/pregenerated/odb/options.ixx3471
-rw-r--r--odb/odb/processor.cxx3255
-rw-r--r--odb/odb/processor.hxx23
-rw-r--r--odb/odb/profile.cxx86
-rw-r--r--odb/odb/profile.hxx39
-rw-r--r--odb/odb/relational/changelog.cxx1239
-rw-r--r--odb/odb/relational/common-query.cxx169
-rw-r--r--odb/odb/relational/common-query.hxx63
-rw-r--r--odb/odb/relational/common.cxx27
-rw-r--r--odb/odb/relational/common.hxx273
-rw-r--r--odb/odb/relational/common.txx125
-rw-r--r--odb/odb/relational/context.cxx169
-rw-r--r--odb/odb/relational/context.hxx289
-rw-r--r--odb/odb/relational/context.ixx44
-rw-r--r--odb/odb/relational/generate.hxx81
-rw-r--r--odb/odb/relational/header.cxx1146
-rw-r--r--odb/odb/relational/header.hxx1466
-rw-r--r--odb/odb/relational/inline.cxx47
-rw-r--r--odb/odb/relational/inline.hxx693
-rw-r--r--odb/odb/relational/model.cxx121
-rw-r--r--odb/odb/relational/model.hxx868
-rw-r--r--odb/odb/relational/mssql/common.cxx603
-rw-r--r--odb/odb/relational/mssql/common.hxx293
-rw-r--r--odb/odb/relational/mssql/context.cxx766
-rw-r--r--odb/odb/relational/mssql/context.hxx194
-rw-r--r--odb/odb/relational/mssql/header.cxx312
-rw-r--r--odb/odb/relational/mssql/inline.cxx42
-rw-r--r--odb/odb/relational/mssql/model.cxx66
-rw-r--r--odb/odb/relational/mssql/schema.cxx651
-rw-r--r--odb/odb/relational/mssql/source.cxx1201
-rw-r--r--odb/odb/relational/mysql/common.cxx400
-rw-r--r--odb/odb/relational/mysql/common.hxx171
-rw-r--r--odb/odb/relational/mysql/context.cxx868
-rw-r--r--odb/odb/relational/mysql/context.hxx194
-rw-r--r--odb/odb/relational/mysql/header.cxx136
-rw-r--r--odb/odb/relational/mysql/inline.cxx42
-rw-r--r--odb/odb/relational/mysql/model.cxx161
-rw-r--r--odb/odb/relational/mysql/schema.cxx489
-rw-r--r--odb/odb/relational/mysql/source.cxx724
-rw-r--r--odb/odb/relational/oracle/common.cxx522
-rw-r--r--odb/odb/relational/oracle/common.hxx203
-rw-r--r--odb/odb/relational/oracle/context.cxx795
-rw-r--r--odb/odb/relational/oracle/context.hxx188
-rw-r--r--odb/odb/relational/oracle/header.cxx230
-rw-r--r--odb/odb/relational/oracle/inline.cxx42
-rw-r--r--odb/odb/relational/oracle/model.cxx64
-rw-r--r--odb/odb/relational/oracle/schema.cxx696
-rw-r--r--odb/odb/relational/oracle/source.cxx646
-rw-r--r--odb/odb/relational/pgsql/common.cxx351
-rw-r--r--odb/odb/relational/pgsql/common.hxx159
-rw-r--r--odb/odb/relational/pgsql/context.cxx786
-rw-r--r--odb/odb/relational/pgsql/context.hxx192
-rw-r--r--odb/odb/relational/pgsql/header.cxx285
-rw-r--r--odb/odb/relational/pgsql/inline.cxx42
-rw-r--r--odb/odb/relational/pgsql/model.cxx101
-rw-r--r--odb/odb/relational/pgsql/schema.cxx266
-rw-r--r--odb/odb/relational/pgsql/source.cxx1140
-rw-r--r--odb/odb/relational/processor.cxx1564
-rw-r--r--odb/odb/relational/processor.hxx15
-rw-r--r--odb/odb/relational/schema-source.cxx281
-rw-r--r--odb/odb/relational/schema-source.hxx126
-rw-r--r--odb/odb/relational/schema.cxx174
-rw-r--r--odb/odb/relational/schema.hxx1606
-rw-r--r--odb/odb/relational/source.cxx6343
-rw-r--r--odb/odb/relational/source.hxx7154
-rw-r--r--odb/odb/relational/sqlite/common.cxx217
-rw-r--r--odb/odb/relational/sqlite/common.hxx147
-rw-r--r--odb/odb/relational/sqlite/context.cxx490
-rw-r--r--odb/odb/relational/sqlite/context.hxx146
-rw-r--r--odb/odb/relational/sqlite/header.cxx63
-rw-r--r--odb/odb/relational/sqlite/inline.cxx42
-rw-r--r--odb/odb/relational/sqlite/model.cxx91
-rw-r--r--odb/odb/relational/sqlite/schema.cxx455
-rw-r--r--odb/odb/relational/sqlite/source.cxx471
-rw-r--r--odb/odb/relational/validator.cxx638
-rw-r--r--odb/odb/relational/validator.hxx24
-rw-r--r--odb/odb/semantics.hxx19
-rw-r--r--odb/odb/semantics/class-template.cxx54
-rw-r--r--odb/odb/semantics/class-template.hxx68
-rw-r--r--odb/odb/semantics/class.cxx175
-rw-r--r--odb/odb/semantics/class.hxx142
-rw-r--r--odb/odb/semantics/derived.cxx254
-rw-r--r--odb/odb/semantics/derived.hxx440
-rw-r--r--odb/odb/semantics/elements.cxx619
-rw-r--r--odb/odb/semantics/elements.hxx841
-rw-r--r--odb/odb/semantics/elements.ixx13
-rw-r--r--odb/odb/semantics/enum.cxx85
-rw-r--r--odb/odb/semantics/enum.hxx228
-rw-r--r--odb/odb/semantics/fundamental.cxx222
-rw-r--r--odb/odb/semantics/fundamental.hxx150
-rw-r--r--odb/odb/semantics/namespace.cxx107
-rw-r--r--odb/odb/semantics/namespace.hxx71
-rw-r--r--odb/odb/semantics/relational.hxx18
-rw-r--r--odb/odb/semantics/relational/changelog.cxx187
-rw-r--r--odb/odb/semantics/relational/changelog.hxx164
-rw-r--r--odb/odb/semantics/relational/changeset.cxx56
-rw-r--r--odb/odb/semantics/relational/changeset.hxx93
-rw-r--r--odb/odb/semantics/relational/column.cxx201
-rw-r--r--odb/odb/semantics/relational/column.hxx205
-rw-r--r--odb/odb/semantics/relational/deferrable.cxx55
-rw-r--r--odb/odb/semantics/relational/deferrable.hxx41
-rw-r--r--odb/odb/semantics/relational/elements.cxx179
-rw-r--r--odb/odb/semantics/relational/elements.hxx468
-rw-r--r--odb/odb/semantics/relational/elements.txx215
-rw-r--r--odb/odb/semantics/relational/foreign-key.cxx218
-rw-r--r--odb/odb/semantics/relational/foreign-key.hxx152
-rw-r--r--odb/odb/semantics/relational/index.cxx145
-rw-r--r--odb/odb/semantics/relational/index.hxx100
-rw-r--r--odb/odb/semantics/relational/key.cxx97
-rw-r--r--odb/odb/semantics/relational/key.hxx104
-rw-r--r--odb/odb/semantics/relational/model.cxx54
-rw-r--r--odb/odb/semantics/relational/model.hxx49
-rw-r--r--odb/odb/semantics/relational/name.cxx89
-rw-r--r--odb/odb/semantics/relational/name.hxx159
-rw-r--r--odb/odb/semantics/relational/primary-key.cxx80
-rw-r--r--odb/odb/semantics/relational/primary-key.hxx62
-rw-r--r--odb/odb/semantics/relational/table.cxx183
-rw-r--r--odb/odb/semantics/relational/table.hxx115
-rw-r--r--odb/odb/semantics/template.cxx88
-rw-r--r--odb/odb/semantics/template.hxx146
-rw-r--r--odb/odb/semantics/union-template.cxx54
-rw-r--r--odb/odb/semantics/union-template.hxx33
-rw-r--r--odb/odb/semantics/union.cxx36
-rw-r--r--odb/odb/semantics/union.hxx27
-rw-r--r--odb/odb/semantics/unit.cxx42
-rw-r--r--odb/odb/semantics/unit.hxx183
-rw-r--r--odb/odb/source.cxx151
-rw-r--r--odb/odb/sql-lexer.cxx250
-rw-r--r--odb/odb/sql-lexer.hxx129
-rw-r--r--odb/odb/sql-lexer.ixx84
-rw-r--r--odb/odb/sql-token.cxx44
-rw-r--r--odb/odb/sql-token.hxx89
-rw-r--r--odb/odb/sql-token.ixx44
-rw-r--r--odb/odb/traversal.hxx19
-rw-r--r--odb/odb/traversal/class-template.cxx62
-rw-r--r--odb/odb/traversal/class-template.hxx45
-rw-r--r--odb/odb/traversal/class.cxx34
-rw-r--r--odb/odb/traversal/class.hxx40
-rw-r--r--odb/odb/traversal/derived.cxx111
-rw-r--r--odb/odb/traversal/derived.hxx130
-rw-r--r--odb/odb/traversal/elements.cxx77
-rw-r--r--odb/odb/traversal/elements.hxx238
-rw-r--r--odb/odb/traversal/enum.cxx54
-rw-r--r--odb/odb/traversal/enum.hxx57
-rw-r--r--odb/odb/traversal/fundamental.hxx39
-rw-r--r--odb/odb/traversal/namespace.hxx15
-rw-r--r--odb/odb/traversal/relational.hxx18
-rw-r--r--odb/odb/traversal/relational/changelog.cxx63
-rw-r--r--odb/odb/traversal/relational/changelog.hxx53
-rw-r--r--odb/odb/traversal/relational/changeset.hxx18
-rw-r--r--odb/odb/traversal/relational/column.hxx21
-rw-r--r--odb/odb/traversal/relational/elements.hxx162
-rw-r--r--odb/odb/traversal/relational/foreign-key.hxx21
-rw-r--r--odb/odb/traversal/relational/index.hxx20
-rw-r--r--odb/odb/traversal/relational/key.cxx17
-rw-r--r--odb/odb/traversal/relational/key.hxx50
-rw-r--r--odb/odb/traversal/relational/model.hxx18
-rw-r--r--odb/odb/traversal/relational/primary-key.hxx18
-rw-r--r--odb/odb/traversal/relational/table.hxx21
-rw-r--r--odb/odb/traversal/template.cxx53
-rw-r--r--odb/odb/traversal/template.hxx56
-rw-r--r--odb/odb/traversal/union-template.cxx26
-rw-r--r--odb/odb/traversal/union-template.hxx29
-rw-r--r--odb/odb/traversal/union.hxx15
-rw-r--r--odb/odb/traversal/unit.hxx15
-rw-r--r--odb/odb/validator.cxx1912
-rw-r--r--odb/odb/validator.hxx23
-rw-r--r--odb/odb/version.hxx37
-rw-r--r--odb/tests/.gitignore2
-rw-r--r--odb/tests/build/.gitignore3
-rw-r--r--odb/tests/build/bootstrap.build8
-rw-r--r--odb/tests/build/root.build16
-rw-r--r--odb/tests/buildfile4
-rw-r--r--odb/tests/testscript21
-rw-r--r--packages.manifest2
-rw-r--r--repositories.manifest14
252 files changed, 124675 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1631641
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,19 @@
+# This is a good default: files that are auto-detected by git to be text are
+# converted to the platform-native line ending (LF on Unix, CRLF on Windows)
+# in the working tree and to LF in the repository.
+#
+* text=auto
+
+# Use `eol=crlf` for files that should have the CRLF line ending both in the
+# working tree (even on Unix) and in the repository.
+#
+#*.bat text eol=crlf
+
+# Use `eol=lf` for files that should have the LF line ending both in the
+# working tree (even on Windows) and in the repository.
+#
+#*.sh text eol=lf
+
+# Use `binary` to make sure certain files are never auto-detected as text.
+#
+#*.png binary
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..13d880b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.bdep/
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..1bd577e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,894 @@
+Version 2.5.0
+
+ * Support for mapping C++ types as other C++ types. For example:
+
+ #pragma db map type(bool) as(std::string) \
+ to((?) ? "true" : "false") from((?) == "true")
+
+ @@ Ref to the manual.
+
+ * Support for custom table definition options in addition to column
+ definition options. For details, refer to Section 14.1.16, "options" in
+ the ODB manual.
+
+ * Support for nested object ids. Now the 'id' pragma specifier can optionally
+ include the data member path to the id inside a composite value. For
+ example:
+
+ #pragma db id(first)
+ std::pair<int, int> p;
+
+ Note that one somewhat counter-intuitive aspect of this new feature is
+ that the whole member marked with id ('p' in the above example) and not
+ just the actual id member ('p.first' in the above example) is treated as
+ readonly.
+
+ Such nested id also cannot be automatically assigned ('auto' specifier).
+
+ * Support for defining views as instantiations of C++ class templates,
+ similar to objects and composite value types.
+
+ * Allow the use of object pointers as map keys. Also remove the restriction
+ for map keys and set values to be NOT NULL.
+
+ * New 'points_to' pragma allows the establishment of relationship without
+ using object pointers.
+
+ * The 'inverse' pragma now supports nested data members.
+
+ * Helper header (<odb/nested-container.hxx>) for nested container emulation.
+ It is still fairly experimental and only supports vector<vector<T>>. For
+ example:
+
+ #include <odb/nested-container.hxx>
+
+ #pragma db object
+ class package
+ {
+ using licenses_type = std::vector<std::string>
+ using license_alternatives_type = std::vector<licenses_type>
+
+ license_alternatives_type license_alternatives;
+
+ // Database mapping.
+ //
+ using _license_key = odb::nested_key<licenses_type>;
+ using _licenses_type = std::map<_license_key, std::string>;
+
+ #pragma db value(_license_key)
+ #pragma db member(_license_key::outer) column("alternative_index")
+ #pragma db member(_license_key::inner) column("index")
+
+ #pragma db member(license_alternatives) id_column("") value_column("")
+ #pragma db member(licenses) \
+ virtual(_licenses_type) \
+ after(license_alternatives) \
+ get(odb::nested_get (this.license_alternatives)) \
+ set(odb::nested_set (this.license_alternatives, std::move (?))) \
+ id_column("") key_column("") value_column("license")
+ };
+
+ * Database classes are now move-constructible. This means they can be
+ returned by value from a function in C++11.
+
+ * Support for bulk operations in PostgreSQL 14 using the new pipeline mode.
+ For details on bulk operations see Section 15.3, "Bulk Database Operations"
+ in the ODB manual. Note that while this functionality requires libpq
+ version 14 or later, it should be usable with PostgreSQL servers version
+ 7.4 or later. The development of this support was sponsored by Qube
+ Research & Technologies Limited.
+
+ * Support for SQLite ATTACH DATABASE. Attached databases are represented as
+ special odb::sqlite::database instances. @@ TODO: doc ref.
+
+ * Support for SQLite incremental BLOB/TEXT I/O (the sqlite3_blob_open()
+ functionality). For details, refer to Section 18.1.3, "Incremental
+ BLOB/TEXT I/O" in the ODB manual.
+
+ * In PostgreSQL a SELECT statement in a view that is prefixed with the
+ /*CALL*/ comment is recognized and handled as a stored procedure call.
+
+ * Due to warnings issued by some compiler, std::auto_ptr is no longer mapped
+ as an object pointer or wrapper in the C++11 mode. Use std::unique_ptr
+ instead.
+
+Version 2.4.0
+
+ * Support for object loading views. Object loading views allow loading of
+ one or more complete objects instead of, or in addition to, a subset of
+ data members and with a single SELECT statement execution. For details,
+ refer to Section 10.2, "Object Loading Views" in the ODB manual.
+
+ * Support for bulk operations in Oracle and SQL Server. Bulk operations
+ persist, update, or erase a range of objects using a single database
+ statement execution which often translates to a significantly better
+ performance. For details, refer to Section 15.3, "Bulk Database
+ Operations" in the ODB manual.
+
+ * New database class functions, query_one() and query_value(), provide
+ convenient shortcuts for situations where the query is known to return
+ at most one element (query_one) or exactly one element (query_value).
+ Corresponding execute_one() and execute_value() functions for prepared
+ queries are also provided. For details, refer to Sections 4.3, "Executing
+ a Query" and 4.5, "Prepared Queries" in the ODB manual.
+
+ * Support for defining persistent objects as instantiations of C++ class
+ templates, similar to composite value types. For details, refer to
+ Section 15.2, "Persistent Class Template Instantiations" in the ODB
+ manual.
+
+ * Support for object and table join types in views. Supported join type
+ are left, right, full, inner, and cross with left being the default.
+ For details, refer to Sections 10.1, "Object Views" and 10.3, "Table
+ Views" in the ODB manual.
+
+ * Support for result modifiers in view query conditions. Currently
+ supported result modifiers are 'distinct' (which is translated to
+ SELECT DISTINCT) and 'for_update' (which is translated to FOR UPDATE or
+ equivalent for database systems that support it). For details, refer to
+ Section 10.5, "View Query Conditions" in the ODB manual.
+
+ * Support for persisting std::deque containers.
+
+ * New pragma, on_delete, allows the specification of an on-delete semantics
+ (translated to the ON DELETE SQL clause) for an object pointer. For more
+ information, refer to Section 14.4.15, "on_delete" in the ODB manual.
+
+ * Besides odb::stderr_tracer there is now odb::stderr_full_tracer that
+ traces statement preparations and deallocations in addition to their
+ executions. This new implementation can be useful when you want to see
+ the text of a statement that contains a syntax error and therefore
+ will not actually be executed. For more information, refer to Section
+ 3.13, "Tracing SQL Statement Execution" in the ODB manual.
+
+ * ODB can now compile headers that use #pragma once instead of include
+ guards.
+
+ * User-supplied prologues and epilogues are now generated outside the
+ pre.hxx/post.hxx includes. This allows the use of precompiled headers
+ with the generated files.
+
+ * Support for calling MySQL stored procedures. For details and limitations
+ refer to Section 17.7, "MySQL Stored Procedures" in the ODB manual.
+
+ * Support for calling SQL Server stored procedures. For details and
+ limitations refer to Section 21.7, "SQL Server Stored Procedures" in
+ the ODB manual.
+
+ * New option, --oracle-warn-truncation, makes ODB warn about SQL names
+ that are longer than 30 characters and are therefore truncated. ODB
+ now also detects when such truncations lead to Oracle name conflicts
+ and issues diagnostics even without this option specified.
+
+ * For MySQL, PostgreSQL, and SQL Server, ODB now warns when an SQL name
+ exceeds the database limit (64, 63, and 128 characters, respectively).
+ SQLite has no limitation on name lengths. For Oracle, which has a limit
+ that is much more likely to be reached in normal circumstances (30
+ characters), a more comprehensive detection is implemented (see the item
+ above).
+
+ * New option, --statement-regex, can be used to process prepared statement
+ names that are used by PostgreSQL. This can be useful, for example, to
+ shorten names that exceed the PostgreSQL name limit.
+
+ * The --std option now accepts the 'c++14' value.
+
+Version 2.3.0
+
+ * Support for database schema evolution, including schema migration, data
+ migration, and soft model changes. For more information, refer to Chapter
+ 13, "Database Schema Evolution" in the ODB manual.
+
+ * Support for object sections. Sections are an optimization mechanism that
+ allows the partitioning of data members of a persistent class into groups
+ that can be loaded and/or updated separately. For more information, refer
+ to Chapter 9, "Sections" in the ODB manual as well as the 'section'
+ example in the odb-examples package.
+
+ * Support for automatic mapping of C++11 enum classes in addition to "old"
+ enums. For more information, refer to the ODB manual "Type Mapping"
+ sections for each database system.
+
+ * Support for defining composite value types inside persistent classes,
+ views, and other composite values. For more information, refer to Section
+ 7.2, "Composite Value Types" in the ODB manual.
+
+ * Support for pattern matching (SQL LIKE operator) in the C++-integrated
+ queries. For more information, refer to Section 4.1, "ODB Query Language"
+ in the ODB manual.
+
+ * The schema_catalog::create_schema() function now has a third argument
+ which indicates whether to drop the schema prior to creating the new one.
+ The default is true which is backwards-compatible. The schema_catalog
+ class now also provides the drop_schema() function which allows you to
+ drop the schema without creating the new one. Finally, the exists()
+ function now has the schema name argument which by default is an empty
+ string (the default schema name). For more information, refer to Section
+ 3.4, "Database" in the ODB manual.
+
+ * The transaction class now provides the default constructor that allows
+ the creation of finalized transactions which can then be re-initialized
+ with the reset() function. The finalized() accessor has also been added
+ which allows querying of the finalization state. For more information,
+ refer to Section 3.5, "Transactions" in the ODB manual.
+
+ * New option, --fkeys-deferrable-mode, specifies the alternative deferrable
+ mode for foreign keys. By default, the ODB compiler generates deferred
+ foreign keys for databases that support them (SQLite, PostgreSQL, and
+ Oracle) and comments the foreign keys out for databases that don't (MySQL
+ and SQL Server). This option can be used to override this behavior. Refer
+ to the ODB compiler command line interface documentation (man pages) for
+ details.
+
+ * Starting with MySQL version 5.6.4 it is possible to store fractional
+ seconds up to microsecond precision in TIME, DATETIME, and TIMESTAMP
+ columns. Both Boost and Qt profiles have been updated to support this
+ new functionality. Note, however, that to enable sub-second precision,
+ the corresponding type with the desired precision has to be specified
+ explicitly. For details, refer to the "MySQL Database Type Mapping"
+ sections in the Boost and Qt profile chapters.
+
+ * New SQLite-specific exception, odb::sqlite::forced_rollback, which is
+ thrown if SQLite forces a transaction to roll back. For more information,
+ refer to Section 16.5.6, "Forced Rollback" in the ODB manual.
+
+ * New options, --pgsql-server-version, can be used to specify the minimum
+ PostgreSQL server version with which the generated C++ code and schema
+ will be used. Right now this information is used to enable the use of
+ the IF NOT EXISTS clause in the CREATE TABLE statement for the schema
+ version table creation in PostgreSQL 9.1 and later. Refer to the ODB
+ compiler command line interface documentation (man pages) for details.
+
+ * The --output-name option has been renamed to --input-name, which is more
+ semantically correct.
+
+ * The generated database schema now explicitly specify NULL for nullable
+ columns.
+
+ * The generated separate C++ schema file (--schema-format separate) no
+ longer includes the generated header file (-odb.hxx). As a result, it is
+ now possible to use the --generate-schema-only and --at-once options to
+ generate a combined C++ schema file for several headers.
+
+Version 2.2.0
+
+ * Multi-database support. This mechanism 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::<db>::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 database systems if and when necessary. For
+ more information, refer to Chapter 14, "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
+ provide a way to perform potentially expensive query preparation tasks
+ only once and then execute the query multiple times. For more information,
+ refer to Section 4.5, "Prepared Queries" in the ODB manual as well as the
+ 'prepared' example in the odb-examples package.
+
+ * Mapping for char[N] and std::array<char, N> to database VARCHAR(N-1) (or
+ similar) as well as for char to database CHAR(1) (or similar). For SQL
+ Server and SQLite on Windows equivalent mappings for wchar_t are also
+ provided. Also the query support for arrays has been improved to allow
+ passing a value of the decayed type (pointer) as a query parameter.
+ For more information, refer to the ODB manual "Type Mapping" sections
+ for each database system.
+
+ * Support for change-tracking std::vector and QList container equivalents.
+ Change-tracking containers minimize the number of database operations
+ necessary to synchronize the container state with the database. For
+ more information, refer to Sections 5.4, "Change-Tracking Containers",
+ 5.4.1 "Change-Tracking vector", and 22.3.1, "Change-Tracking QList"
+ in the ODB manual.
+
+ * Support for automatically-derived SQL name transformations (table, column,
+ index, etc). At the higher level, it is possible to assign prefixes and
+ suffixes (--table-prefix, --{index,fkey,sequence}--suffix options) as
+ well as to convert to upper or lower case (--sql-name-case option). At
+ the lower level, it is possible to specify transformations as regular
+ expressions (--{table,column,index,fkey,sequence,sql-name}-regex options).
+ For more information, refer to the SQL NAME TRANSFORMATIONS section in
+ the ODB compiler command line interface documentation (man pages).
+
+ * New options, --export-symbol and --extern-symbol, allow DLL-exporting of
+ the generated database support code.
+
+ * Support for transaction post- commit/rollback callbacks. For more
+ information, refer to Section 13.1, "Transaction Callbacks" in the ODB
+ manual.
+
+ * Support for custom session implementations. For more information, refer
+ to Section 10.2, "Custom Sessions" in the ODB manual.
+
+ * Support for early connection release. Now the database connection is
+ released when commit()/rollback() is called rather than when the
+ transaction instance goes out of scope.
+
+ * New odb::schema_catalog function, exists(), can be used to check whether
+ a schema with the specified name exists in the catalog.
+
+ * Support for SQL Server ROWVERSION-based optimistic concurrency. For more
+ information, refer to Section 19.1.1, "ROWVERSION Support" in the ODB
+ manual.
+
+ * Support for specifying the SQL Server transaction isolation level. For
+ more information, refer to Section 19.2, "SQL Server Database Class" in
+ the ODB manual.
+
+ * Support for "smart" containers. A smart container is provided with
+ additional functions which allow it to insert, update, and delete
+ individual elements in the database. Change-tracking containers are
+ examples of smart containers that utilizes this new functionality.
+ Currently only ordered smart containers are supported. Note also that
+ with this addition the names of the database functions provided by the
+ ODB compiler (see libodb/odb/container-traits.hxx) have changed. This
+ will only affect you if you have added ODB persistence support for a
+ custom container.
+
+Version 2.1.0
+
+ * The ODB compiler is now capable of automatically discovering accessor and
+ modifier functions for inaccessible data members in persistent classes,
+ composite value types, and views. It will then use these accessors and
+ modifiers in the generated code instead of trying to access such data
+ members directly. The names of these functions are derived from the
+ data member names and, by default, the ODB compiler will look for names
+ in the form: get_foo/set_foo, getFoo/setFoo, getfoo/setfoo, and just
+ foo. You can also add custom name derivations with the --accessor-regex
+ and --modifier-regex ODB compiler options. For more information, refer
+ to Section 3.2, "Declaring Persistent Objects and Values" in the ODB
+ manual.
+
+ * New pragmas, get, set, and access, allow the specification of custom
+ accessor and modifier expressions for data members in persistent classes,
+ composite value types, and views. For more information, refer to Section
+ 12.4.5, "get/set/access" in the ODB manual as well as the 'access' example
+ in the odb-examples package.
+
+ * New pragma, virtual, allows the declaration of virtual data members. A
+ virtual data member is an imaginary data member that is only used for
+ the purpose of database persistence. This mechanism can be useful to
+ aggregate or dis-aggregate real data members, implement the pimpl idiom,
+ and to handle third-party types for which names of real data members may
+ not be known. For more information, refer to Section 12.4.13, "virtual"
+ in the ODB manual as well as the 'access' and 'pimpl' examples in the
+ odb-examples package.
+
+ * Support for defining database indexes. Both simple and composite indexes
+ can be defined with support for database-specific index types, methods,
+ and options. For more information, refer to Section 12.6, "Index
+ Definition Pragmas" as well as Sections [13-17].16, "<Database> Index
+ Definition" in the ODB manual.
+
+ * Support for mapping extended 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.7, "Database Type Mapping
+ Pragmas" in the ODB manual.
+
+ * The Boost profile now provides persistence support for the Boost Multi-
+ Index container (boost::multi_index_container). For more information,
+ refer to Section 19.3, "Multi-Index Container Library" in the ODB manual.
+
+ * The Boost profile now provides persistence support for the Boost uuid type
+ (boost::uuids::uuid). For more information, refer to Section 19.6, "Uuid
+ Library" in the ODB manual as well as the 'boost' example in the odb-
+ examples package.
+
+ * The Qt profile now provides persistence support for the QUuid type. For
+ more information, refer to Section 20.1, "Basic Types" in the ODB manual
+ as well as the 'qt' example in the odb-examples package.
+
+ * SQLite improvements: Persistence support for std::wstring on Windows
+ (Section 14.1, "SQLite Type Mapping"). Ability to pass the database
+ name as std::wstring on Windows (Section 14.2, "SQLite Database Class").
+ Ability to specify the virtual filesystem (vfs) module in the database
+ constructors (Section 14.2, "SQLite Database Class").
+
+ * Support for mapping C++11 std::array<char, N> and std::array<unsigned
+ char, N> types to BLOB/BINARY database types. For more information,
+ refer to Sections [13-17].1, "<Database> Type Mapping" in the ODB manual.
+
+ * Support for mapping the char[16] array to PostgreSQL UUID and SQL Server
+ UNIQUEIDENTIFIER types. For more information, refer to Sections 15.1,
+ "PostgreSQL Type Mapping" and 17.1, "SQL Server Type Mapping" in the
+ ODB manual.
+
+ * New option, --output-name, specifies the alternative base name used to
+ construct the names of the generated files. Refer to the ODB compiler
+ command line interface documentation (man pages) for details.
+
+ * New option, --generate-schema-only, instructs the ODB compiler to
+ generate the database schema only. Refer to the ODB compiler command
+ line interface documentation (man pages) for details.
+
+ * New option, --at-once, triggers the generation of code for all the input
+ files as well as for all the files that they include at once. Refer to
+ the ODB compiler command line interface documentation (man pages) for
+ details.
+
+ * New options, --sql-interlude and --sql-interlude-file, allow the insertion
+ of custom SQL between the DROP and CREATE statements in the generated
+ database schema file.
+
+ * New options, --omit-drop and --omit-create, trigger the omission of DROP
+ and CREATE statements, respectively, from the generated database schema.
+
+ * New ODB manual Section, 6.3 "Circular Relationships", explains how to
+ handle persistent classes with circular dependencies that are defined
+ in separate headers.
+
+ * The id() pragma that was used to declare a persistent class without an
+ object id has been renamed to no_id.
+
+ * New pragma, definition, allows the specification of an alternative code
+ generation point for persistent classes, views, and composite value
+ types. This mechanism is primarily useful for converting third-party
+ types to ODB composite value types. For more information, refer to
+ Section 12.3.7, "Definition" in the ODB manual.
+
+ * The session constructor now accepts an optional 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
+ manual.
+
+ * Simplified Oracle automatically-assigned object id implementation that
+ does not rely on triggers.
+
+ * Support for mapping boost::posix_time::ptime and QDateTime to the DATE
+ Oracle type. For more information, refer to Sections 19.4.4 (Boost) and
+ 20.4.4 (Qt) in the ODB manual.
+
+ * Default SQLite mapping for float and double now allows NULL since SQLite
+ treats NaN FLOAT values as NULL. For more information, refer to Section
+ 14.1, "SQLite Type Mapping" in the ODB manual.
+
+Version 2.0.0
+
+ * Support for C++11. The newly supported C++11 standard library components
+ include:
+ - std::unique_ptr as object pointer or value wrapper
+ - odb::lazy_unique_ptr lazy counterpart
+ - std::shared_ptr/weak_ptr as object pointer or value wrapper
+ - odb::lazy_shared_ptr/lazy_weak_ptr lazy counterparts
+ - support for array, forward_list, and unordered containers
+ - connection factory can be passed to the database constructor as
+ std::unique_ptr instead of std::auto_ptr
+
+ The ODB compiler now recognizes the --std option. Valid values for this
+ option are 'c++98' (default) and 'c++11'. In the runtime libraries the
+ C++11 support is header-only which means that the same build of a runtime
+ library can be used in both the C++98 and C++11 modes. On UNIX, the tests
+ and examples can be compiled in the C++11 mode by passing the necessary
+ options to turn the C++ compiler into this mode (e.g., -std=c++0x GCC
+ option). On Windows, the tests and examples are always built in the C++11
+ mode with VC++ 10 and later. The new 'c++11' example in the odb-examples
+ package shows ODB support for some of the C++11 features.
+
+ * Support for polymorphism. Now a persistent class hierarchy can be
+ declared polymorphic which makes it possible to persist, load, update,
+ erase, and query objects of derived classes using their base class
+ interfaces. For more information, refer to Chapter 8, "Inheritance" in
+ the ODB manual as well as the 'inheritance/polymorphism' example in the
+ odb-examples package.
+
+ * Support for composite object ids. Now a composite value type can be used
+ to declare an object id member. For more information, refer to Section
+ 7.2.1, "Composite Object Ids" in the ODB manual as well as the 'composite'
+ example in the odb-examples package.
+
+ * Support for the NULL value semantics for composite values. For more
+ information, refer to Section 7.3, "Pointers and NULL Value Semantics"
+ in the ODB manual.
+
+ * New schema format (--schema-format), 'separate', allows the generation
+ of the schema creation code into a separate C++ source file (called
+ '<name>-schema.cxx' by default). This value is primarily useful if you
+ want to place the schema creation functionality into a separate program
+ or library.
+
+ * New namespace-level pragmas: table, pointer. The table pragma specifies
+ the table prefix that is added to table names for all the persistent
+ classes inside a namespace. The pointer pragma specifies the default
+ pointer type to be used for persistent classes and views inside a
+ namespace. For more information, refer to Section 12.5.1, "pointer" and
+ Section 12.5.2, "table" in the ODB manual.
+
+ * Session support is now optional and is disabled by default. This is a
+ backwards-incompatible change. Session support can be enabled on the
+ per class basis or at the namespace level using the new session pragma.
+ It can also be enabled by default for all the persistent classes using
+ the --generate-session ODB compiler option. Thus, to get the old behavior
+ where all the objects were session-enabled, simply add --generate-session
+ to your ODB compiler command line. For more information, refer to Chapter
+ 10, "Session" in the ODB manual.
+
+ * The semantics of the database operations callbacks has changed with
+ respect to object const-ness. This is a backwards-incompatible change.
+ Now the callback function for the *_persist, *_update, and *_erase events
+ is always called on the constant object reference while for the *_load
+ events -- always on the unrestricted reference. For more information,
+ refer to Section 12.1.7, "callback" in the ODB manual.
+
+ * New function, transaction::reset(), allows the reuse of the same
+ transaction instance to complete several database transactions. For more
+ information, refer to Section 3.4, "Transactions" in the ODB manual.
+
+ * New exception, odb::session_required, is thrown when ODB detects that
+ correctly loading a bidirectional object relationship requires a session
+ but one is not used. For more information, refer to Section 6.2,
+ "Bidirectional Relationships" in the ODB manual.
+
+Version 1.8.0
+
+ * Support for the Microsoft SQL Server database. The provided connection
+ factories include 'new' (a new connection is created every time one is
+ requested) and 'pool' (a pool of connections is maintained). The Boost
+ and Qt profiles have been updated to support this database. For more
+ information, refer to Chapter 17, "Microsoft SQL Server Database" in
+ the ODB manual.
+
+ * Support for defining composite value types as C++ class template
+ instantiations. For more information, refer to Section 7.2, "Composite
+ Value Types" in the ODB manual as well as the 'composite' example in the
+ odb-examples package.
+
+ * Support for database schemas ("database namespaces"). A schema can be
+ specified for a persistent class, for a C++ namespace (the schema then
+ applies to all the persistent classes within this namespace), and for a
+ file with the --schema ODB compiler option. For more information, refer
+ to Section 12.1.8, "schema" in the ODB manual.
+
+ * The --default-schema option has been renamed to --schema-name.
+
+ * The default Oracle mapping for std::string has changed from VARCHAR2(4000)
+ to VARCHAR2(512).
+
+Version 1.7.0
+
+ * Support for the Oracle database. The provided connection factories
+ include 'new' (a new connection is created every time one is requested)
+ and 'pool' (a pool of connections is maintained). The Boost and Qt
+ profiles have been updated to support this database. For more information,
+ refer to Chapter 16, "Oracle Database" in the ODB manual.
+
+ * Support for optimistic concurrency. For more information refer to Chapter
+ 11, "Optimistic Concurrency" in the ODB manual as well as the 'optimistic'
+ example in the odb-examples package.
+
+ * Support for read-only objects, composite value types, and data members.
+ The new readonly pragma can be used to declare one of these entities as
+ read-only. Constant data members are automatically treated as read-only.
+ For more information, refer to Section 12.1.4 "readonly (object)",
+ Section 12.3.6 "readonly (composite value)", and Section 12.4.10
+ "readonly (data member)" in the ODB manual.
+
+ * Support for persistent classes without object identifiers. Such classes
+ have to be explicitly declared as not having an object id and they have
+ limited functionality. For more information, refer to Section 12.1.5
+ "id" in the ODB manual.
+
+ * Support for SQL statement execution tracing. For more information, refer
+ to Section 3.12 "Tracing SQL Statement Execution" in the ODB manual.
+
+ * Support for mapping char[N], unsigned char[N], and std::vector<unsigned
+ char> to the BLOB (or equivalent) types. For more information, refer to
+ Chapters 13 (for MySQL), 14 (for SQLite), 15 (for PostgreSQL), and 16
+ (for Oracle) in the ODB manual.
+
+ * Query result iterator now provides the id() function which allows one
+ to get the object id without loading the object. For more information,
+ refer to Section 4.4 "Query Result" in the ODB manual.
+
+ * Support for microsecond precision in Boost and Qt date-time types
+ mapping to PostgreSQL date-time data types. Additionally, Qt QDateTime
+ values stored in a PostgreSQL database can now be earlier than the UNIX
+ epoch.
+
+Version 1.6.0
+
+ * New concept, view, is a C++ class that embodies a light-weight, read-
+ only projection of one or more persistent objects or database tables
+ or the result of a native SQL query execution. Some of the common
+ applications of views include loading a subset of data members from
+ objects or columns from database tables, executing and handling
+ results of arbitrary SQL queries, including aggregate queries, as
+ well as joining multiple objects and/or database tables using object
+ relationships or custom join conditions. For more information refer
+ to Chapter 9, "Views" in the ODB manual as well as the 'view' example
+ in the odb-examples package.
+
+ * New function, database::erase_query(), allows the deletion of the
+ database state of multiple objects matching certain criteria. It uses
+ the same query expression as the database::query() function. For more
+ information, refer to Section 3.10, "Deleting Persistent Objects" in
+ the ODB manual.
+
+ * Support for value wrappers. An ODB value wrapper is a class template
+ that wraps a value type or a container. Common examples of wrappers
+ are smart pointers, holders, and "optional value" containers such as
+ boost::optional. A wrapper can be transparent or it can handle the
+ NULL semantics. To allow the easy conversion of value types that do
+ not support the NULL semantics into the ones that do, the odb::nullable
+ class template has been added. ODB now also includes built-in support for
+ std::auto_ptr and std::tr1::shared_ptr smart pointers as value wrappers
+ as well as for boost::shared_ptr and QSharedPointer via the Boost and Qt
+ profiles. Currently, the NULL semantics is only supported for simple
+ values but smart pointers can still be used with composite values and
+ containers. For more information, refer to Section 7.3, "NULL Value
+ Semantics" in the ODB manual.
+
+ * Support for the boost::optional container in the Boost profile. A data
+ member of the boost::optional type is mapped to a column that can have
+ a NULL value. For more information, refer to Section 15.3 "Optional
+ Library" in the ODB manual.
+
+ * Support for mapping std::vector<char> to the BLOB (or equivalent) types.
+ For more information, refer to Chapters 11 (for MySQL), 12 (for SQLite)
+ and 13 (for PostgreSQL) in the ODB manual.
+
+ * New option, --table-prefix, allows the specification of a prefix that
+ is added to table and index names. For more information, refer to the
+ ODB compiler command line interface documentation (man pages).
+
+ * New ODB runtime library interface, odb::connection, represents a
+ connection to the database. The primary use case for a connection is to
+ execute native statements outside of a transaction. For more information,
+ refer to Section 3.5, "Connections" in the ODB manual.
+
+ * Support for multiplexing several transactions on the same thread. For
+ more information, refer to Section 3.4, "Transactions" in the ODB
+ manual.
+
+ * All the concrete connection classes now have a second constructor which
+ allows the creation of a connection instance from an already established
+ underlying connection handle. The connection_pool_factory and, in case of
+ SQLite, single_connection_factory now have a virtual create() function
+ that can be overridden to implement custom connection establishment and
+ configuration.
+
+ * The query expression syntax for object pointers and composite values has
+ changed. Now, instead of using the scope resolution operator ('::'), the
+ member access via a pointer operator (->) is used for object pointers and
+ the member access operator (.) is used for composite values. Examples of
+ old and new syntax for pointers, old: query<employee>::employer::name,
+ new: query<employee>::employer->name. For composites values, old:
+ query<employee>::name::first, new: query<employee>::name.first.
+
+ * SQLite ODB runtime now enables foreign key constraints checking by
+ default. While this should not affect correct applications, due to
+ bugs in SQLite DDL foreign keys support, you may need to temporarily
+ disable foreign key constraints checking when re-creating the database
+ schema (the sign that you may need to do so is the "foreign key
+ constraint failed" exception thrown by the commit() function after the
+ call to schema_catalog::create_schema()). For more information, refer
+ to Section 12.5.3, "Foreign Key Constraints" in the ODB manual.
+
+ * Support for specifying the client character set for the MySQL database.
+ For more information, refer to Section 11.2, "MySQL Database Class" in
+ the ODB manual.
+
+ * Object cache maintained by a session no longer distinguishes between
+ const and non-const objects. Instead, const objects are treated as
+ non-const by casting away constness. For more information on this new
+ behavior, refer to Section 9.1, "Object Cache" in the ODB manual.
+
+Version 1.5.0
+
+ * Support for the PostgreSQL database. The provided connection factories
+ include 'new' (a new connection is created every time one is requested)
+ and 'pool' (a pool of connections is maintained). The Boost and Qt
+ profiles have been updated to support this database. For more information,
+ refer to Chapter 13, "PostgreSQL Database" in the ODB manual.
+
+ * New handling of the NULL semantics. Now, instead of being specified as
+ part of the SQL type with the type pragma, there are separate null and
+ not_null pragmas. The not_null pragma was used to control the NULL
+ semantics of object pointers. Now the two pragmas are used consistently
+ for object pointers and simple values (and, in the future, they will work
+ for composite values and containers). To control the NULL semantics of
+ the container's element values, the value_null and value_not_null pragmas
+ have been added, similar to the value_type, value_column, etc., pragmas.
+ For more information about the new mechanism, refer to Sections 10.2.3,
+ 10.2.8, 10.3.4, and 10.3.13 in the ODB manual.
+
+ This is a backwards-incompatible change. Existing use cases that will
+ require manual changes are listed below.
+
+ For pragmas that apply to simple value types and data members of
+ such types:
+
+ #pragma db type("TEXT NOT NULL") => #pragma db type("TEXT")
+ #pragma db type("TEXT NULL") => #pragma db type("TEXT") null
+ #pragma db type("TEXT") => #pragma db type("TEXT") null
+
+ For pragmas that apply to containers of pointers and data members of
+ such types:
+
+ #pragma db not_null => #pragma db value_not_null
+
+ * New pragma, default, allows the specification of the database default
+ value. For more information, refer to Section 10.3.5, "default" in the
+ ODB manual.
+
+ * New pragmas, options, id_options, index_options, key_options, and
+ value_options, allow the specification of additional column definition
+ options. For more information, refer to Section 10.3.6, "options" in
+ the ODB manual.
+
+ * Support for database operations callbacks. Now a persistent class can
+ register a callback function that will be called before and after every
+ database operation (such as persist, load, update, or erase) is performed
+ on an object of this class. A database operations callback can be used to
+ implement object-specific pre and post initializations, registrations,
+ and cleanups. For more information and an example, refer to Section
+ 10.1.4, "callback" in the ODB manual.
+
+ * New option, --include-regex, allows the modification of the #include
+ directive paths generated by the ODB compiler. This is primarily useful
+ when placing the generated code into subdirectories and the #include
+ directives have to be adjusted accordingly. The --include-regex-trace
+ option is useful for debugging the expressions specified with
+ --include-regex.
+
+Version 1.4.0
+
+ * New profile, qt, provides persistence support for the Qt framework. This
+ version covers the most commonly used basic types, date-time types, smart
+ pointers, and containers. The qt profile implementation is provided by the
+ libodb-qt library. For more information refer to Chapter 13, "Profiles
+ Introduction" and Chapter 15, "Qt Profile" in the ODB manual as well as
+ the 'qt' example in the odb-examples package.
+
+ * Support for non-polymorphic object inheritance, including the new abstract
+ pragma. For more information refer to Chapter 8, "Inheritance" in the ODB
+ manual as well as the 'inheritance' example in the odb-examples package.
+
+ * Automatic mapping of C++ enumerations to suitable database types. In
+ database systems that support enumeration types (such as MySQL), a C++
+ enum is mapped to such a type (for example, ENUM('red', 'green', 'blue')
+ in MySQL). Otherwise, it is mapped to a suitable integer type. Refer to
+ Part II, "Database Systems" in the ODB manual for more details on the
+ provided mappings.
+
+ * New pragma, id_type, allows the specification of the native database type
+ that should be used for data members designated as object identifiers. In
+ combination with the type pragma, id_type allows you to map a C++ type
+ differently depending on whether it is used in an ordinary member or an
+ object id.
+
+ * New options, --odb-prologue-file and --odb-epilogue-file, allow the
+ inclusion of file contents into the ODB compilation.
+
+ * Default mapping of the size_t and std::size_t types to a 64-bit integer
+ database type irrespective of the platform width. This can be overridden
+ with the type pragma.
+
+Version 1.3.0
+
+ * Support for the SQLite database. The provided connection factories include
+ 'new' (a new connection is created every time one is requested), 'single'
+ (single connection is shared among all the callers), and 'pool' (a pool
+ of connections is maintained). In multi-threaded applications the runtime
+ uses the SQLite shared cache and unlock notification features to aid
+ concurrency. For more information, refer to Chapter 11, "SQLite Database"
+ in the ODB manual.
+
+ * Support for database-specific profiles. Now the ODB compiler first looks
+ for the <profile>-<database>.options file and only if this file is not
+ found, does it fall back to <profile>.options.
+
+ * Support for the GCC 4.6 plugin interface changes.
+
+Version 1.2.0
+
+ * New profile, boost, provides persistence support for the Boost libraries.
+ This version covers the most commonly used types from the smart_ptr,
+ unordered, and date_time libraries. The boost profile implementation is
+ provided by the libodb-boost library. For more information refer to
+ Chapter 11, "Profiles Introduction" and Chapter 12, "Boost Profile" in
+ the ODB manual as well as the 'boost' example in the odb-examples package.
+
+ * Support for embedded database schemas. The new option, --schema-format,
+ allows the selection of the schema format. The valid values for this
+ option are 'sql' for a standalone SQL file and 'embedded' for a schema
+ embedded into the generated C++ code. The new odb::schema_catalog class
+ provides support for accessing embedded schemas from within the
+ application. For details refer to Section 3.3, "Database" in the ODB
+ manual as well as the 'schema/embedded' example in the odb-examples
+ package.
+
+ * New exceptions: odb::recoverable, odb::connection_lost, and odb::timeout.
+ The odb::recoverable exception is a common base class for all recoverable
+ ODB exceptions. The other two exceptions plus odb::deadlock now inherit
+ from this base. Refer to Section 3.5, "Error Handling and Recovery" for
+ details.
+
+ * Support for connection validation (ping) in MySQL connection_pool_factory.
+ This transparently deals with the MySQL server closing connections after
+ a certain period of inactivity.
+
+ * New namespace, odb::core, contains using-declarations for the core ODB
+ constructs, such as the database, transaction, etc. The primary use of
+ this namespace is in the using-directives:
+
+ using namespace odb::core;
+
+ The above form should be preferred over the old variant:
+
+ using namespace odb;
+
+ The new approach brings all the essential ODB names into the current
+ namespace without any of the auxiliary objects such as traits, etc., which
+ minimizes the likelihood of conflicts with other libraries. Note that you
+ should still use the odb namespace when qualifying individual names, for
+ example, odb::database.
+
+ * New option aliases: -q for --generate-query and -s for --generate-schema.
+
+ * Support for the default options file. Now, if configured, the ODB compiler
+ loads the default options file (by default ../etc/odb/default.options,
+ relative to the ODB compiler binary). This file can be used for
+ installation-wide customization, such as adding extra include search
+ paths.
+
+Version 1.1.0
+
+ * Support for storing containers in the database. For more information refer
+ to Chapter 5, "Containers" in the ODB manual as well as the 'container'
+ example in the odb-examples package.
+
+ * Support for unidirectional and bidirectional object relationships,
+ including lazy loading. For more information refer to Chapter 6,
+ "Relationships" in the ODB manual as well as the 'relationship' and
+ 'inverse' examples in the odb-examples package.
+
+ * Support for composite value types. For more information refer to Chapter
+ 7, "Composite Value Types" in the ODB manual as well as the 'composite'
+ example in the odb-examples package.
+
+ * Support for sessions. A session is an application's unit of work that
+ may encompass several database transactions. In this version of ODB a
+ session is just an object cache. For more information refer to Chapter
+ 8, "Session" in the ODB manual.
+
+ * Support for custom object pointers that allows you to use smart pointers
+ to return, pass, and cache persistent objects. See Section 3.2, "Object
+ Pointers" in the ODB manual for details.
+
+ * Support for native SQL statement execution. See Section 3.9, "Executing
+ Native SQL Statements" in the ODB manual for details.
+
+ * New option, --profile/-p, instructs the ODB compiler to use the specified
+ profile library. See the ODB compiler command line manual for details.
+
+ * Support for literal names (template-id, derived type declarator such as
+ pointers, etc) in data member declarations. Now, for example, you can use
+ std::vector<std::string> directly instead of having to create a typedef
+ alias for it.
+
+ * Support for inheritance from transient base types for object types and
+ composite value types.
+
+ * New example, 'schema/custom', shows how to map persistent C++ classes to
+ a custom database schema.
+
+ * New options, --odb-prologue, --odb-epilogue, allow inclusion of extra code
+ into the ODB compilation process. This can be useful for making additional
+ traits specializations or ODB pragmas known to the ODB compiler.
+
+ * Support for persistent classes without default constructors. For objects
+ of such classes only the load() and find() database functions that
+ populate an existing instance can be used. Similarly, only the load()
+ query result iterator function which populates an existing instance can
+ be used.
+
+Version 1.0.0
+
+ * Initial release.
diff --git a/buildfile b/buildfile
new file mode 100644
index 0000000..c3c8909
--- /dev/null
+++ b/buildfile
@@ -0,0 +1,6 @@
+# Glue buildfile that "pulls" all the packages in the project.
+#
+import pkgs = [dir_paths] $process.run_regex(\
+ cat $src_root/packages.manifest, '\s*location\s*:\s*(\S+)\s*', '\1')
+
+./: $pkgs
diff --git a/odb/.gitignore b/odb/.gitignore
new file mode 100644
index 0000000..dc37d77
--- /dev/null
+++ b/odb/.gitignore
@@ -0,0 +1,29 @@
+# Local default options files.
+#
+.build2/local/
+
+# Compiler/linker output.
+#
+*.d
+*.t
+*.i
+*.i.*
+*.ii
+*.ii.*
+*.o
+*.obj
+*.gcm
+*.pcm
+*.ifc
+*.so
+*.dylib
+*.dll
+*.a
+*.lib
+*.exp
+*.pdb
+*.ilk
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
diff --git a/odb/GPLv3 b/odb/GPLv3
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/odb/GPLv3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/odb/INSTALL b/odb/INSTALL
new file mode 100644
index 0000000..6bd473c
--- /dev/null
+++ b/odb/INSTALL
@@ -0,0 +1,36 @@
+Prerequisites
+=============
+
+ - GNU g++ >= 4.5.0 http://gcc.gnu.org/
+ - libcutl >= 1.9.0 http://www.codesynthesis.com/projects/libcutl/
+
+ODB requires the GCC compiler (g++) to be built with plugin support.
+If you are building GCC yourself, you can enable plugin support with
+the --enable-plugin configure option. If you are using a pre-packaged
+GCC (for example, as part of your distribution), then you can verify
+that GCC was built with plugin support by running g++ with the -v
+option and then making sure --enable-plugin is present in the output.
+
+Note also that for pre-packaged GCC, plugin headers are usually
+distributed in a separate package, normally called gcc-plugin-dev
+or similar. You will need to install this package in order to build
+ODB. For Debian/Ubuntu, this package is called gcc-X.Y-plugin-dev,
+for example:
+
+apt-get install gcc-4.7-plugin-dev
+
+For RedHat/Fedora, this package is called gcc-plugin-devel, for
+example:
+
+yum install gcc-plugin-devel
+
+
+Building
+========
+
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build odb
+
+But if you don't want to use the package manager, then you can also build it
+manually using the standard build2 build system.
diff --git a/odb/LICENSE b/odb/LICENSE
new file mode 100644
index 0000000..f36493e
--- /dev/null
+++ b/odb/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2009-2024 Code Synthesis Tools CC.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3 as
+published by the Free Software Foundation.
+
+For more information on ODB licensing as well as for answers to
+some of the common licensing questions, visit the ODB License
+page:
+
+http://www.codesynthesis.com/products/odb/license.xhtml
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see:
+
+http://www.codesynthesis.com/licenses/gpl-3.txt
diff --git a/odb/NEWS b/odb/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/odb/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/odb/README b/odb/README
new file mode 100644
index 0000000..90078e5
--- /dev/null
+++ b/odb/README
@@ -0,0 +1,20 @@
+ODB is an object-relational mapping (ORM) system for C++. It provides
+tools, APIs, and library support that allow you to persist C++ objects
+to a relational database (RDBMS) without having to deal with tables,
+columns, or SQL and without manually writing any of the mapping code.
+For more information see:
+
+http://www.codesynthesis.com/products/odb/
+
+This package contains the ODB compiler and the ODB system documentation.
+
+See the NEWS file for the user-visible changes from the previous release.
+
+See the LICENSE file for distribution conditions.
+
+See the INSTALL file for prerequisites and installation instructions.
+
+See the doc/ directory for documentation.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/odb/build/.gitignore b/odb/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/odb/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/odb/build/bootstrap.build b/odb/build/bootstrap.build
new file mode 100644
index 0000000..9e30b56
--- /dev/null
+++ b/odb/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v3; see accompanying LICENSE file
+
+project = odb
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/odb/build/export.build b/odb/build/export.build
new file mode 100644
index 0000000..6293a45
--- /dev/null
+++ b/odb/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v3; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/
+}
+
+export $out_root/odb/exe{odb}
diff --git a/odb/build/root.build b/odb/build/root.build
new file mode 100644
index 0000000..86a02a0
--- /dev/null
+++ b/odb/build/root.build
@@ -0,0 +1,110 @@
+# file : build/root.build
+# license : GNU GPL v3; see accompanying LICENSE file
+
+# This configuration variable can be used to specify the GCC plugin directory
+# instead of auto-discovering it with -print-file-name=plugin. Primarily
+# useful when dealing with cross-compilation.
+#
+config [dir_path, config.report.variable=plugin_dir] \
+ config.odb.plugin_dir ?= [null]
+
+# This configuration variable can be used to specify the GCC g++ executable
+# name that will be called by the ODB compiler instead of auto-deriving it
+# from config.cxx. Primarily useful when dealing with cross-compilation.
+#
+config [string, config.report.variable=gxx_name] \
+ config.odb.gxx_name ?= [null]
+
+config [bool] config.odb.develop ?= false
+
+develop = $config.odb.develop
+
+define cli: file
+cli{*}: extension = cli
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
+
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
+
+# Omit the rest during the skeleton load.
+#
+if ($build.mode != 'skeleton')
+{
+ if ($cxx.id != 'gcc')
+ fail 'ODB compiler can only be built with GCC'
+
+ # Determine the GCC plugin directory unless specified explicitly.
+ #
+ if ($config.odb.plugin_dir != [null])
+ plugin_dir = $config.odb.plugin_dir
+ else
+ {
+ # If plugin support is disabled, then -print-file-name will print the name
+ # we have passed (the real plugin directory will always be absolute).
+ #
+ plugin_dir = [dir_path] $process.run($cxx.path -print-file-name=plugin)
+
+ if ("$plugin_dir" == plugin)
+ fail "$recall($cxx.path) does not support plugins"
+ }
+
+ # It can also include '..' components (e.g., on Windows) so normalize it for
+ # good measure.
+ #
+ plugin_dir = $normalize($plugin_dir)
+
+ # Determine the g++ executable name unless specified explicitly.
+ #
+ if ($config.odb.gxx_name != [null])
+ gxx_name = $config.odb.gxx_name
+ else
+ {
+ # Unless cross-compiling, pass the C++ compiler's recall path as the g++
+ # name.
+ #
+ # Note that we used to compare complete target triplets but that prooved
+ # too strict. For example, we may be running on x86_64-apple-darwin17.7.0
+ # while the compiler is targeting x86_64-apple-darwin17.3.0.
+ #
+ if ($cxx.target.cpu == $build.host.cpu && \
+ $cxx.target.system == $build.host.system)
+ {
+ gxx_name = $recall($cxx.path)
+ }
+ else
+ fail "g++ executable name must be specified explicitly with \
+config.odb.gxx_name when cross-compiling"
+ }
+
+ # Extract the copyright notice from the LICENSE file.
+ #
+ # Note that cat is a builtin which means this is both portable and fast.
+ #
+ copyright = $process.run_regex(cat $src_root/LICENSE, \
+ 'Copyright \(c\) (.+)\.', \
+ '\1')
+}
+else
+{
+ # Set for report.
+ #
+ plugin_dir = [null]
+ gxx_name = [null]
+}
diff --git a/odb/buildfile b/odb/buildfile
new file mode 100644
index 0000000..78b5d08
--- /dev/null
+++ b/odb/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v3; see accompanying LICENSE file
+
+./: {*/ -build/ -m4/} doc{INSTALL NEWS README} legal{GPLv3 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/odb/doc/.gitignore b/odb/doc/.gitignore
new file mode 100644
index 0000000..9ee2af2
--- /dev/null
+++ b/odb/doc/.gitignore
@@ -0,0 +1,5 @@
+/odb.1
+/odb.xhtml
+
+*.ps
+*.pdf
diff --git a/odb/doc/buildfile b/odb/doc/buildfile
new file mode 100644
index 0000000..88f30fc
--- /dev/null
+++ b/odb/doc/buildfile
@@ -0,0 +1,191 @@
+# file : doc/buildfile
+# license : GNU GPL v3; see accompanying LICENSE file
+
+define css: doc
+css{*}: extension = css
+
+define xhtml: doc
+xhtml{*}: extension = xhtml
+
+define ps: doc
+ps{*}: extension = ps
+
+define pdf: doc
+pdf{*}: extension = pdf
+
+define html2ps: file
+html2ps{*}: extension = html2ps
+
+./: css{default} xhtml{manual} doc{*.png} file{*.svg}
+
+# Man pages.
+#
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+./: pregenerated/{man1 xhtml}{*}: include = (!$develop)
+
+# Distribute pregenerated versions only in the consumption build.
+#
+pregenerated/{man1 xhtml}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+./: {man1 xhtml}{odb}: include = $develop
+
+if $develop
+{
+ doc_version = [string] "$version.major.$version.minor.$version.patch"
+ if $version.pre_release
+ doc_version += "-$version.pre_release_string"
+
+ # Let's take the last four-digit number to cover 2000-2021,2022.
+ #
+ doc_year = $regex.replace($copyright, '.+[-, ]([0-9][0-9][0-9][0-9]) .+', '\1')
+
+ man_options = -v project="ODB" -v version="$doc_version" \
+ -v copyright="$copyright" --suppress-undocumented
+
+ import! [metadata] cli = cli%exe{cli}
+}
+
+# In the development build distribute regenerated versions, remapping their
+# locations to the paths of the pregenerated versions (which are only
+# distributed in the consumption build; see above). This way we make sure that
+# the distributed files are always up-to-date.
+#
+{man1 xhtml}{odb}: dist = ($develop ? pregenerated/ : false)
+
+# @@ TMP Note that the project, version, and date variables we are passing to
+# cli are currently unused since the respective values are hard-coded
+# in the odb-prologue.* files.
+#
+man1{odb}: ../odb/cli{options} file{odb-prologue.1 odb-epilogue.1} $cli
+%
+if $develop
+{{
+ # Use the copyright year to approximate the last authoring date.
+ #
+ $cli --generate-man $man_options \
+ -v date="January $doc_year" \
+ --man-prologue-file $path($<[1]) \
+ --man-epilogue-file $path($<[2]) \
+ --stdout $path($<[0]) >$path($>)
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ if! diff $src_base/pregenerated/odb.1 $path($>) >-
+ cp $path($>) $src_base/pregenerated/odb.1
+ end
+}}
+
+xhtml{odb}: ../odb/cli{options} file{odb-prologue.xhtml odb-epilogue.xhtml} $cli
+%
+if $develop
+{{
+ $cli --generate-html $man_options \
+ --html-prologue-file $path($<[1]) \
+ --html-epilogue-file $path($<[2]) \
+ --stdout $path($<[0]) >$path($>)
+
+ if! diff $src_base/pregenerated/odb.xhtml $path($>) >-
+ cp $path($>) $src_base/pregenerated/odb.xhtml
+ end
+}}
+
+#
+##
+
+# Manual.
+#
+# This case is slightly more involved because we make the generation of the
+# manual's ps/pdf optional and also don't keep the result in the repository.
+# Specifically:
+#
+# 1. In the consumption build we will install/redistribute ps/pdf if present.
+#
+# 2. In the development build we will generate ps/pdf if we are able to import
+# the needed tools, issuing a warning otherwise.
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions, if exist, in the consumption build.
+#
+./: pregenerated/{ps pdf}{*}: include = (!$develop)
+
+# Distribute pregenerated versions only in the consumption build.
+#
+pregenerated/{ps pdf}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+html2pdf = false
+
+if $develop
+{
+ # Import the html2ps and ps2pdf programs from the system, if available.
+ #
+ import? html2ps = html2ps%exe{html2ps}
+ import? ps2pdf = ps2pdf14%exe{ps2pdf14}
+
+ html2pdf = ($html2ps != [null] && $ps2pdf != [null])
+
+ if! $html2pdf
+ warn "html2ps and/or ps2pdf14 are not available, not generating .ps and .pdf documentation"
+}
+
+./: {ps pdf}{odb-manual}: include = $html2pdf
+
+# In the development build distribute regenerated versions, remapping their
+# locations to the paths of the pregenerated versions (which are only
+# distributed in the consumption build; see above). This way we make sure that
+# the distributed files are always up-to-date.
+#
+{ps pdf}{odb-manual}: dist = ($html2pdf ? pregenerated/ : false)
+
+# Note: the pregenerated file may not exist, thus --no-cleanup option is
+# required for the cp builtin call. Strictly speaking we don't really need to
+# copy them since they are not stored in the repository, but let's do that for
+# consistency with the distributed source tree.
+#
+# @@ TMP Note that manual.{xhtml,html2ps} still have copyright years, ODB
+# version, document revision/date, etc hard-coded.
+#
+ps{odb-manual}: {xhtml html2ps}{manual} $html2ps
+%
+if $html2pdf
+{{
+ options =
+
+ diag html2ps ($<[0])
+ $html2ps $options -f $path($<[1]) -o $path($>) $path($<[0])
+
+ cp --no-cleanup $path($>) $src_base/pregenerated/odb-manual.ps
+}}
+
+pdf{odb-manual}: ps{odb-manual} $ps2pdf
+%
+if $html2pdf
+{{
+ options = -dOptimize=true -dEmbedAllFonts=true
+
+ diag ps2pdf ($<[0])
+ $ps2pdf $options $path($<[0]) $path($>)
+
+ cp --no-cleanup $path($>) $src_base/pregenerated/odb-manual.pdf
+}}
+
+#
+##
diff --git a/odb/doc/default.css b/odb/doc/default.css
new file mode 100644
index 0000000..889f46b
--- /dev/null
+++ b/odb/doc/default.css
@@ -0,0 +1,323 @@
+html {
+ margin : 0;
+ padding : 0;
+ background : white;
+}
+
+body {
+ font-family : "Lucida Grande", Verdana, "Bitstream Vera Sans", sans-serif;
+ font-weight : normal;
+ font-size : 13px;
+ line-height : 19px;
+
+ color : black;
+
+ margin : 0 2em 0 2em;
+ padding : 0;
+}
+
+
+body {
+ min-width: 40em;
+}
+
+#container {
+ max-width : 46em;
+ margin : 0 auto;
+ padding : 0 1em 0 1em;
+}
+
+
+
+/*
+ * Footer
+ *
+ */
+#footer {
+ color : #3a84a7;
+
+ padding : 1em 0 0.5em 0;
+
+ font-size : 10px;
+ line-height : 15px;
+
+ text-align: center;
+}
+
+#footer a:link, #footer a:visited {
+
+ color:#1d6699;
+ text-decoration: underline;
+}
+
+#footer a {
+ margin-left: 0.7em;
+ margin-right: 0.7em;
+}
+
+#footer p {
+ padding: 0;
+ margin: 0.3em 0 0 0;
+}
+
+/* Distribution terms. */
+#footer #terms {
+ text-align: justify;
+
+ font-size : 110%;
+ font-family : monospace;
+
+ padding : 1em 0 0.5em 0;
+}
+
+
+/*
+ * Content
+ *
+ */
+
+#content {
+ padding : 0em 0.1em 0 1.3em;
+ margin : 1.4em 0 0 0;
+}
+
+#content p,
+#content ol,
+#content ul,
+#content dl {
+ text-align: justify;
+}
+
+#content h1 {
+ margin-left: -0.89em;
+}
+
+a:link {
+ color:#0536d2;
+}
+
+
+/*
+ * Headings
+ *
+ */
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight : 500;
+}
+
+h1 { font-size : 155%; }
+h2 { font-size : 130%; }
+h3 { font-size : 125%; }
+h4 { font-size : 110%; }
+h5 { font-size : 106%; }
+h6 { font-size : 100%; }
+
+h1 { margin : 1.8em 0 0.8em 0;}
+h2 { margin-top : 1.4em;}
+h3 { margin-top : 1em;}
+
+p.indent {
+ margin-left : 1.5em;
+}
+
+
+/*
+ * Fix for IE 5.5 table font problem
+ *
+ */
+
+table {
+ font-size : 13px;
+}
+
+
+/*
+ * table of content
+ *
+ */
+
+ul.toc li {
+ padding : .4em 0em 0em 0em;
+}
+
+
+/* Toc links don't need to show when they are visited. */
+.toc a:visited {
+ color:#0536d2;
+}
+
+
+/*
+ * lists
+ *
+ */
+
+
+/* list of links */
+ul.menu {
+ list-style-type : none;
+}
+
+ul.menu li {
+ padding-top : 0.3em;
+ padding-bottom : 0.3em;
+}
+
+
+
+/* @@ I should probably use child selector here */
+/* list with multiline list-elements */
+ul.multiline li, ol.multiline li, dl.multiline dd {
+ padding-top : 0.16em;
+ padding-bottom : 0.16em;
+
+ font-size : 11px;
+ line-height : 15px;
+}
+
+
+
+/* C++ code snippet */
+pre.cxx {
+ margin-top : 0em;
+ margin-bottom : 2em;
+
+ margin-left : 1em;
+}
+
+/* SQL code snippet */
+pre.sql {
+ margin-top : 0em;
+ margin-bottom : 2em;
+
+ margin-left : 1em;
+}
+
+/* make code snippet */
+pre.make {
+ margin-top : 0em;
+ margin-bottom : 2em;
+
+ margin-left : 1em;
+}
+
+/* terminal output */
+pre.term {
+ margin-top : 0em;
+ margin-bottom : 2em;
+
+ margin-left : 1em;
+}
+
+
+/* Images */
+div.center {
+ text-align: center;
+}
+
+/* Document info. */
+#docinfo {
+ margin-top: 4em;
+ border-top: 1px dashed #000000;
+ font-size: 70%;
+}
+
+
+/* Footnote */
+
+#footnote {
+ margin-top : 2.5em;
+}
+
+#footnote hr, hr.footnote {
+ margin-left: 0;
+ margin-bottom: 0.6em;
+ width: 8em;
+ border-top: 1px solid #000000;
+ border-right: none;
+ border-bottom: none;
+ border-left: none;
+
+}
+
+#footnote ol {
+ margin-left: 0;
+ padding-left: 1.45em;
+}
+
+#footnote li {
+ text-align : left;
+ font-size : 11px;
+ line-height : 15px;
+
+ padding : .4em 0 .4em 0;
+}
+
+
+/* Normal table with borders, etc. */
+
+table.std {
+ margin: 2em 0 2em 0;
+
+ border-collapse : collapse;
+ border : 1px solid;
+ border-color : #000000;
+
+ font-size : 11px;
+ line-height : 14px;
+}
+
+table.std th, table.std td {
+ border : 1px solid;
+ padding : 0.6em 0.8em 0.6em 0.8em;
+}
+
+table.std th {
+ background : #cde8f6;
+}
+
+table.std td {
+ text-align: left;
+}
+
+
+/*
+ * "item | description" table.
+ *
+ */
+
+table.description {
+ border-style : none;
+ border-collapse : separate;
+ border-spacing : 0;
+
+ font-size : 13px;
+
+ margin : 0.6em 0 0.6em 0;
+ padding : 0 0 0 0;
+}
+
+table.description tr {
+ padding : 0 0 0 0;
+ margin : 0 0 0 0;
+}
+
+table.description * td, table.description * th {
+ border-style : none;
+ margin : 0 0 0 0;
+ vertical-align : top;
+}
+
+table.description * th {
+ font-weight : normal;
+ padding : 0.4em 1em 0.4em 0;
+ text-align : left;
+ white-space : nowrap;
+ background : none;
+}
+
+table.description * td {
+ padding : 0.4em 0 0.4em 1em;
+ text-align : justify;
+}
diff --git a/odb/doc/manual.html2ps b/odb/doc/manual.html2ps
new file mode 100644
index 0000000..6acd29d
--- /dev/null
+++ b/odb/doc/manual.html2ps
@@ -0,0 +1,69 @@
+@html2ps {
+ option {
+ toc: hb;
+ colour: 1;
+ hyphenate: 1;
+ titlepage: 1;
+ }
+
+ datefmt: "%B %Y";
+
+ titlepage {
+ content: "
+<div align=center>
+ <h1><big>C++ Object Persistence with ODB</big></h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+</div>
+ <p>Copyright &#169; 2009-2020 Code Synthesis Tools CC.</p>
+
+ <p>Permission is granted to copy, distribute and/or modify this
+ document under the terms of the
+ <a href='http://www.codesynthesis.com/licenses/fdl-1.3.txt'>GNU Free
+ Documentation License, version 1.3</a>; with no Invariant Sections,
+ no Front-Cover Texts and no Back-Cover Texts.
+ </p>
+
+ <p>Revision $[revision], $D</p>
+
+ <p>This revision of the manual describes ODB $[version] and is available
+ in the following formats:
+ <a href='http://www.codesynthesis.com/products/odb/doc/manual.xhtml'>XHTML</a>,
+ <a href='http://www.codesynthesis.com/products/odb/doc/odb-manual.pdf'>PDF</a>, and
+ <a href='http://www.codesynthesis.com/products/odb/doc/odb-manual.ps'>PostScript</a>.</p>";
+ }
+
+ toc {
+ indent: 2em;
+ }
+
+ header {
+ odd-right: $H;
+ even-left: $H;
+ }
+
+ footer {
+ odd-left: Revision $[revision], $D;
+ odd-center: $T;
+ odd-right: $N;
+
+ even-left: $N;
+ even-center: $T;
+ even-right: Revision $[revision], $D;
+ }
+}
+
+body {
+ font-size: 12pt;
+ text-align: justify;
+}
+
+pre {
+ font-size: 10pt;
+}
diff --git a/odb/doc/manual.xhtml b/odb/doc/manual.xhtml
new file mode 100644
index 0000000..a308758
--- /dev/null
+++ b/odb/doc/manual.xhtml
@@ -0,0 +1,27073 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+
+<head>
+ <title>C++ Object Persistence with ODB</title>
+
+ <meta name="copyright" content="&#169; 2009-2020 Code Synthesis Tools CC"/>
+ <meta name="keywords" content="odb,c++,object,persistence,ORM,relational,database,RDBMS,ODBMS,OODBMS"/>
+ <meta name="description" content="C++ Object Persistence with ODB"/>
+ <meta name="revision" content="2.4"/>
+ <meta name="version" content="2.4.0"/>
+
+<!--
+
+If you make changes to this document, follow these stylistic rules
+for consistency.
+
+ - Don't use 'object' for instances of non-persistent classes. Use
+ 'instance' instead.
+
+ - Each overloaded function should still be referred to as function.
+ When saying that a function is overloaded, use term 'version',
+ for example The persist() function has two overloaded versions.
+ Don't use version to refer to individual functions, use function
+ instead. The same holds for constructors.
+
+ - Use 'object id' and 'object's identifier'. But not other combinations
+ of the two.
+
+@@ Check that parts TOCs are up to date.
+
+-->
+
+ <link rel="stylesheet" type="text/css" href="default.css" />
+
+<style type="text/css">
+ pre {
+ padding : 0 0 0 0em;
+ margin : 0em 0em 0em 0;
+
+ font-size : 102%
+ }
+
+ body {
+ min-width: 48em;
+ }
+
+ h1 {
+ font-weight: bold;
+ font-size: 200%;
+ line-height: 1.2em;
+ }
+
+ h2 {
+ font-weight : bold;
+ font-size : 150%;
+
+ padding-top : 0.8em;
+ }
+
+ h3 {
+ font-size : 140%;
+ padding-top : 0.8em;
+ }
+
+ /* Force page break for both PDF and HTML (when printing). */
+ hr.page-break {
+ height: 0;
+ width: 0;
+ border: 0;
+ visibility: hidden;
+
+ page-break-after: always;
+ }
+
+ /* Adjust indentation for three levels. */
+ #container {
+ max-width: 48em;
+ }
+
+ #content {
+ padding: 0 0.1em 0 4em;
+ /*background-color: red;*/
+ }
+
+ #content h1 {
+ margin-left: -2.06em;
+ }
+
+ #content h2 {
+ margin-left: -1.33em;
+ }
+
+ /* Title page */
+
+ #titlepage {
+ padding: 2em 0 1em 0;
+ border-bottom: 1px solid black;
+ }
+
+ #titlepage .title {
+ font-weight: bold;
+ font-size: 200%;
+ text-align: center;
+ padding: 1em 0 2em 0;
+ }
+
+ #titlepage p {
+ padding-bottom: 1em;
+ }
+
+ #titlepage #revision {
+ padding-bottom: 0em;
+ }
+
+ /* Lists */
+ ul.list li, ol.list li {
+ padding-top : 0.3em;
+ padding-bottom : 0.3em;
+ }
+
+ div.img {
+ text-align: center;
+ padding: 2em 0 2em 0;
+ }
+
+ /* */
+ dl dt {
+ padding : 0.8em 0 0 0;
+ }
+
+ /* TOC */
+ table.toc {
+ border-style : none;
+ border-collapse : separate;
+ border-spacing : 0;
+
+ margin : 0.2em 0 0.2em 0;
+ padding : 0 0 0 0;
+ }
+
+ table.toc tr {
+ padding : 0 0 0 0;
+ margin : 0 0 0 0;
+ }
+
+ table.toc * td, table.toc * th {
+ border-style : none;
+ margin : 0 0 0 0;
+ vertical-align : top;
+ }
+
+ table.toc * th {
+ font-weight : normal;
+ padding : 0em 0.1em 0em 0;
+ text-align : left;
+ white-space : nowrap;
+ }
+
+ table.toc * table.toc th {
+ padding-left : 1em;
+ }
+
+ table.toc * td {
+ padding : 0em 0 0em 0.7em;
+ text-align : left;
+ }
+
+ /* operators table */
+ #operators {
+ margin: 2em 0 2em 0;
+
+ border-collapse : collapse;
+ border : 1px solid;
+ border-color : #000000;
+
+ font-size : 11px;
+ line-height : 14px;
+ }
+
+ #operators th, #operators td {
+ border: 1px solid;
+ padding : 0.9em 0.9em 0.7em 0.9em;
+ }
+
+ #operators th {
+ background : #cde8f6;
+ }
+
+ #operators td {
+ text-align: left;
+ }
+
+ /* scenarios table */
+ .scenarios {
+ margin: 2em 0 2em 0;
+
+ border-collapse : collapse;
+ border : 1px solid;
+ border-color : #000000;
+
+ font-size : 11px;
+ line-height : 14px;
+ }
+
+ .scenarios th, .scenarios td {
+ border: 1px solid;
+ padding : 0.9em 0.9em 0.7em 0.9em;
+ }
+
+ .scenarios th {
+ background : #cde8f6;
+ }
+
+ .scenarios td {
+ text-align: left;
+ }
+
+ /* specifiers table */
+ .specifiers {
+ margin: 2em 0 2em 0;
+
+ border-collapse : collapse;
+ border : 1px solid;
+ border-color : #000000;
+
+ font-size : 11px;
+ line-height : 14px;
+ }
+
+ .specifiers th, .specifiers td {
+ border: 1px solid;
+ padding : 0.9em 0.9em 0.7em 0.9em;
+ }
+
+ .specifiers th {
+ background : #cde8f6;
+ }
+
+ .specifiers td {
+ text-align: left;
+ }
+
+ /* mapping table */
+ #mapping {
+ margin: 2em 0 2em 0;
+
+ border-collapse : collapse;
+ border : 1px solid;
+ border-color : #000000;
+
+ font-size : 11px;
+ line-height : 14px;
+ }
+
+ #mapping th, #mapping td {
+ border: 1px solid;
+ padding : 0.9em 0.9em 0.7em 0.9em;
+ }
+
+ #mapping th {
+ background : #cde8f6;
+ }
+
+ #mapping td {
+ text-align: left;
+ }
+
+</style>
+
+
+</head>
+
+<body>
+<div id="container">
+ <div id="content">
+
+ <div class="noprint">
+
+ <div id="titlepage">
+ <div class="title">C++ Object Persistence with ODB</div>
+
+ <p>Copyright &#169; 2009-2020 Code Synthesis Tools CC.</p>
+
+ <p>Permission is granted to copy, distribute and/or modify this
+ document under the terms of the
+ <a href="http://www.codesynthesis.com/licenses/fdl-1.3.txt">GNU Free
+ Documentation License, version 1.3</a>; with no Invariant Sections,
+ no Front-Cover Texts and no Back-Cover Texts.</p>
+
+ <!-- REMEMBER TO CHANGE VERSIONS IN THE META TAGS ABOVE! -->
+ <p id="revision">Revision 2.4, February 2015</p>
+ <p>This revision of the manual describes ODB 2.4.0 and is available
+ in the following formats:
+ <a href="http://www.codesynthesis.com/products/odb/doc/manual.xhtml">XHTML</a>,
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb-manual.pdf">PDF</a>, and
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb-manual.ps">PostScript</a>.</p>
+ </div>
+
+ <hr class="page-break"/>
+ <h1>Table of Contents</h1>
+
+ <table class="toc">
+ <tr>
+ <th></th><td><a href="#0">Preface</a>
+ <table class="toc">
+ <tr><th></th><td><a href="#0.1">About This Document</a></td></tr>
+ <tr><th></th><td><a href="#0.2">More Information</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th colspan="2"><a href="#I">PART I OBJECT-RELATIONAL MAPPING</a></th>
+ </tr>
+
+ <tr>
+ <th>1</th><td><a href="#1">Introduction</a>
+ <table class="toc">
+ <tr><th>1.1</th><td><a href="#1.1">Architecture and Workflow</a></td></tr>
+ <tr><th>1.2</th><td><a href="#1.2">Benefits</a></td></tr>
+ <tr><th>1.3</th><td><a href="#1.3">Supported C++ Standards</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>2</th><td><a href="#2">Hello World Example</a>
+ <table class="toc">
+ <tr><th>2.1</th><td><a href="#2.1">Declaring Persistent Classes</a></td></tr>
+ <tr><th>2.2</th><td><a href="#2.2">Generating Database Support Code</a></td></tr>
+ <tr><th>2.3</th><td><a href="#2.3">Compiling and Running</a></td></tr>
+ <tr><th>2.4</th><td><a href="#2.4">Making Objects Persistent</a></td></tr>
+ <tr><th>2.5</th><td><a href="#2.5">Querying the Database for Objects</a></td></tr>
+ <tr><th>2.6</th><td><a href="#2.6">Updating Persistent Objects</a></td></tr>
+ <tr><th>2.7</th><td><a href="#2.7">Defining and Using Views</a></td></tr>
+ <tr><th>2.8</th><td><a href="#2.8">Deleting Persistent Objects</a></td></tr>
+ <tr><th>2.9</th><td><a href="#2.9">Changing Persistent Classes</a></td></tr>
+ <tr><th>2.10</th><td><a href="#2.10">Accessing Multiple Databases</a></td></tr>
+ <tr><th>2.11</th><td><a href="#2.11">Summary</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>3</th><td><a href="#3">Working with Persistent Objects</a>
+ <table class="toc">
+ <tr><th>3.1</th><td><a href="#3.1">Concepts and Terminology</a></td></tr>
+ <tr><th>3.2</th><td><a href="#3.2">Declaring Persistent Objects and Values</a></td></tr>
+ <tr><th>3.3</th><td><a href="#3.3">Object and View Pointers</a></td></tr>
+ <tr><th>3.4</th><td><a href="#3.4">Database</a></td></tr>
+ <tr><th>3.5</th><td><a href="#3.5">Transactions</a></td></tr>
+ <tr><th>3.6</th><td><a href="#3.6">Connections</a></td></tr>
+ <tr><th>3.7</th><td><a href="#3.7">Error Handling and Recovery</a></td></tr>
+ <tr><th>3.8</th><td><a href="#3.8">Making Objects Persistent</a></td></tr>
+ <tr><th>3.9</th><td><a href="#3.9">Loading Persistent Objects</a></td></tr>
+ <tr><th>3.10</th><td><a href="#3.10">Updating Persistent Objects</a></td></tr>
+ <tr><th>3.11</th><td><a href="#3.11">Deleting Persistent Objects</a></td></tr>
+ <tr><th>3.12</th><td><a href="#3.12">Executing Native SQL Statements</a></td></tr>
+ <tr><th>3.13</th><td><a href="#3.13">Tracing SQL Statement Execution</a></td></tr>
+ <tr><th>3.14</th><td><a href="#3.14">ODB Exceptions</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>4</th><td><a href="#4">Querying the Database</a>
+ <table class="toc">
+ <tr><th>4.1</th><td><a href="#4.1">ODB Query Language</a></td></tr>
+ <tr><th>4.2</th><td><a href="#4.2">Parameter Binding</a></td></tr>
+ <tr><th>4.3</th><td><a href="#4.3">Executing a Query</a></td></tr>
+ <tr><th>4.4</th><td><a href="#4.4">Query Result</a></td></tr>
+ <tr><th>4.5</th><td><a href="#4.5">Prepared Queries</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>5</th><td><a href="#5">Containers</a>
+ <table class="toc">
+ <tr><th>5.1</th><td><a href="#5.1">Ordered Containers</a></td></tr>
+ <tr><th>5.2</th><td><a href="#5.2">Set and Multiset Containers</a></td></tr>
+ <tr><th>5.3</th><td><a href="#5.3">Map and Multimap Containers</a></td></tr>
+ <tr>
+ <th>5.4</th><td><a href="#5.4">Change-Tracking Containers</a>
+ <table class="toc">
+ <tr><th>5.4.1</th><td><a href="#5.4.1">Change-Tracking <code>vector</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>5.5</th><td><a href="#5.5">Using Custom Containers</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>6</th><td><a href="#6">Relationships</a>
+ <table class="toc">
+ <tr>
+ <th>6.1</th><td><a href="#6.1">Unidirectional Relationships</a>
+ <table class="toc">
+ <tr><th>6.1.1</th><td><a href="#6.1.1">To-One Relationships</a></td></tr>
+ <tr><th>6.1.2</th><td><a href="#6.1.2">To-Many Relationships</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>6.2</th><td><a href="#6.2">Bidirectional Relationships</a>
+ <table class="toc">
+ <tr><th>6.2.1</th><td><a href="#6.2.1">One-to-One Relationships</a></td></tr>
+ <tr><th>6.2.2</th><td><a href="#6.2.2">One-to-Many Relationships</a></td></tr>
+ <tr><th>6.2.3</th><td><a href="#6.2.3">Many-to-Many Relationships</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>6.3</th><td><a href="#6.3">Circular Relationships</a></td></tr>
+ <tr><th>6.4</th><td><a href="#6.4">Lazy Pointers</a></td></tr>
+ <tr><th>6.5</th><td><a href="#6.5">Using Custom Smart Pointers</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>7</th><td><a href="#7">Value Types</a>
+ <table class="toc">
+ <tr><th>7.1</th><td><a href="#7.1">Simple Value Types</a></td></tr>
+ <tr>
+ <th>7.2</th><td><a href="#7.2">Composite Value Types</a>
+ <table class="toc">
+ <tr><th>7.2.1</th><td><a href="#7.2.1">Composite Object Ids</a></td></tr>
+ <tr><th>7.2.2</th><td><a href="#7.2.2">Composite Value Column and Table Names</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>7.3</th><td><a href="#7.3">Pointers and <code>NULL</code> Value Semantics</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>8</th><td><a href="#8">Inheritance</a>
+ <table class="toc">
+ <tr><th>8.1</th><td><a href="#8.1">Reuse Inheritance</a></td></tr>
+ <tr>
+ <th>8.2</th><td><a href="#8.2">Polymorphism Inheritance</a>
+ <table class="toc">
+ <tr><th>8.2.1</th><td><a href="#8.2.1">Performance and Limitations</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>8.3</th><td><a href="#8.3">Mixed Inheritance</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>9</th><td><a href="#9">Sections</a>
+ <table class="toc">
+ <tr><th>9.1</th><td><a href="#9.1">Sections and Inheritance</a></td></tr>
+ <tr><th>9.2</th><td><a href="#9.2">Sections and Optimistic Concurrency</a></td></tr>
+ <tr><th>9.3</th><td><a href="#9.3">Sections and Lazy Pointers</a></td></tr>
+ <tr><th>9.4</th><td><a href="#9.4">Sections and Change-Tracking Containers</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>10</th><td><a href="#10">Views</a>
+ <table class="toc">
+ <tr><th>10.1</th><td><a href="#10.1">Object Views</a></td></tr>
+ <tr><th>10.2</th><td><a href="#10.2">Object Loading Views</a></td></tr>
+ <tr><th>10.3</th><td><a href="#10.3">Table Views</a></td></tr>
+ <tr><th>10.4</th><td><a href="#10.4">Mixed Views</a></td></tr>
+ <tr><th>10.5</th><td><a href="#10.5">View Query Conditions</a></td></tr>
+ <tr><th>10.6</th><td><a href="#10.6">Native Views</a></td></tr>
+ <tr><th>10.7</th><td><a href="#10.7">Other View Features and Limitations</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>11</th><td><a href="#11">Session</a>
+ <table class="toc">
+ <tr><th>11.1</th><td><a href="#11.1">Object Cache</a></td></tr>
+ <tr><th>11.2</th><td><a href="#11.2">Custom Sessions</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>12</th><td><a href="#12">Optimistic Concurrency</a></td>
+ </tr>
+
+ <tr>
+ <th>13</th><td><a href="#13">Database Schema Evolution</a>
+ <table class="toc">
+ <tr><th>13.1</th><td><a href="#13.1">Object Model Version and Changelog</a></td></tr>
+ <tr><th>13.2</th><td><a href="#13.2">Schema Migration</a></td></tr>
+ <tr>
+ <th>13.3</th><td><a href="#13.3">Data Migration</a>
+ <table class="toc">
+ <tr><th>13.3.1</th><td><a href="#13.3.1">Immediate Data Migration</a></td></tr>
+ <tr><th>13.3.2</th><td><a href="#13.3.2">Gradual Data Migration</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>13.4</th><td><a href="#13.4">Soft Object Model Changes</a>
+ <table class="toc">
+ <tr><th>13.4.1</th><td><a href="#13.4.1">Reuse Inheritance Changes</a></td></tr>
+ <tr><th>13.4.2</th><td><a href="#13.4.2">Polymorphism Inheritance Changes</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>14</th><td><a href="#14">ODB Pragma Language</a>
+ <table class="toc">
+ <tr>
+ <th>14.1</th><td><a href="#14.1">Object Type Pragmas</a>
+ <table class="toc">
+ <tr><th>14.1.1</th><td><a href="#14.1.1"><code>table</code></a></td></tr>
+ <tr><th>14.1.2</th><td><a href="#14.1.2"><code>pointer</code></a></td></tr>
+ <tr><th>14.1.3</th><td><a href="#14.1.3"><code>abstract</code></a></td></tr>
+ <tr><th>14.1.4</th><td><a href="#14.1.4"><code>readonly</code></a></td></tr>
+ <tr><th>14.1.5</th><td><a href="#14.1.5"><code>optimistic</code></a></td></tr>
+ <tr><th>14.1.6</th><td><a href="#14.1.6"><code>no_id</code></a></td></tr>
+ <tr><th>14.1.7</th><td><a href="#14.1.7"><code>callback</code></a></td></tr>
+ <tr><th>14.1.8</th><td><a href="#14.1.8"><code>schema</code></a></td></tr>
+ <tr><th>14.1.9</th><td><a href="#14.1.9"><code>polymorphic</code></a></td></tr>
+ <tr><th>14.1.10</th><td><a href="#14.1.10"><code>session</code></a></td></tr>
+ <tr><th>14.1.11</th><td><a href="#14.1.11"><code>definition</code></a></td></tr>
+ <tr><th>14.1.12</th><td><a href="#14.1.12"><code>transient</code></a></td></tr>
+ <tr><th>14.1.13</th><td><a href="#14.1.13"><code>sectionable</code></a></td></tr>
+ <tr><th>14.1.14</th><td><a href="#14.1.14"><code>deleted</code></a></td></tr>
+ <tr><th>14.1.15</th><td><a href="#14.1.15"><code>bulk</code></a></td></tr>
+ <tr><th>14.1.16</th><td><a href="#14.1.16"><code>options</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>14.2</th><td><a href="#14.2">View Type Pragmas</a>
+ <table class="toc">
+ <tr><th>14.2.1</th><td><a href="#14.2.1"><code>object</code></a></td></tr>
+ <tr><th>14.2.2</th><td><a href="#14.2.2"><code>table</code></a></td></tr>
+ <tr><th>14.2.3</th><td><a href="#14.2.3"><code>query</code></a></td></tr>
+ <tr><th>14.2.4</th><td><a href="#14.2.4"><code>pointer</code></a></td></tr>
+ <tr><th>14.2.5</th><td><a href="#14.2.5"><code>callback</code></a></td></tr>
+ <tr><th>14.2.6</th><td><a href="#14.2.6"><code>definition</code></a></td></tr>
+ <tr><th>14.2.7</th><td><a href="#14.2.7"><code>transient</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>14.3</th><td><a href="#14.3">Value Type Pragmas</a>
+ <table class="toc">
+ <tr><th>14.3.1</th><td><a href="#14.3.1"><code>type</code></a></td></tr>
+ <tr><th>14.3.2</th><td><a href="#14.3.2"><code>id_type</code></a></td></tr>
+ <tr><th>14.3.3</th><td><a href="#14.3.3"><code>null</code>/<code>not_null</code></a></td></tr>
+ <tr><th>14.3.4</th><td><a href="#14.3.4"><code>default</code></a></td></tr>
+ <tr><th>14.3.5</th><td><a href="#14.3.5"><code>options</code></a></td></tr>
+ <tr><th>14.3.6</th><td><a href="#14.3.6"><code>readonly</code></a></td></tr>
+ <tr><th>14.3.7</th><td><a href="#14.3.7"><code>definition</code></a></td></tr>
+ <tr><th>14.3.8</th><td><a href="#14.3.8"><code>transient</code></a></td></tr>
+ <tr><th>14.3.9</th><td><a href="#14.3.9"><code>unordered</code></a></td></tr>
+ <tr><th>14.3.10</th><td><a href="#14.3.10"><code>index_type</code></a></td></tr>
+ <tr><th>14.3.11</th><td><a href="#14.3.11"><code>key_type</code></a></td></tr>
+ <tr><th>14.3.12</th><td><a href="#14.3.12"><code>value_type</code></a></td></tr>
+ <tr><th>14.3.13</th><td><a href="#14.3.13"><code>value_null</code>/<code>value_not_null</code></a></td></tr>
+ <tr><th>14.3.14</th><td><a href="#14.3.14"><code>id_options</code></a></td></tr>
+ <tr><th>14.3.15</th><td><a href="#14.3.15"><code>index_options</code></a></td></tr>
+ <tr><th>14.3.16</th><td><a href="#14.3.16"><code>key_options</code></a></td></tr>
+ <tr><th>14.3.17</th><td><a href="#14.3.17"><code>value_options</code></a></td></tr>
+ <tr><th>14.3.18</th><td><a href="#14.3.18"><code>id_column</code></a></td></tr>
+ <tr><th>14.3.19</th><td><a href="#14.3.19"><code>index_column</code></a></td></tr>
+ <tr><th>14.3.20</th><td><a href="#14.3.20"><code>key_column</code></a></td></tr>
+ <tr><th>14.3.21</th><td><a href="#14.3.21"><code>value_column</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>14.4</th><td><a href="#14.4">Data Member Pragmas</a>
+ <table class="toc">
+ <tr><th>14.4.1</th><td><a href="#14.4.1"><code>id</code></a></td></tr>
+ <tr><th>14.4.2</th><td><a href="#14.4.2"><code>auto</code></a></td></tr>
+ <tr><th>14.4.3</th><td><a href="#14.4.3"><code>type</code></a></td></tr>
+ <tr><th>14.4.4</th><td><a href="#14.4.4"><code>id_type</code></a></td></tr>
+ <tr><th>14.4.5</th><td><a href="#14.4.5"><code>get</code>/<code>set</code>/<code>access</code></a></td></tr>
+ <tr><th>14.4.6</th><td><a href="#14.4.6"><code>null</code>/<code>not_null</code></a></td></tr>
+ <tr><th>14.4.7</th><td><a href="#14.4.7"><code>default</code></a></td></tr>
+ <tr><th>14.4.8</th><td><a href="#14.4.8"><code>options</code></a></td></tr>
+ <tr><th>14.4.9</th><td><a href="#14.4.9"><code>column</code> (object, composite value)</a></td></tr>
+ <tr><th>14.4.10</th><td><a href="#14.4.10"><code>column</code> (view)</a></td></tr>
+ <tr><th>14.4.11</th><td><a href="#14.4.11"><code>transient</code></a></td></tr>
+ <tr><th>14.4.12</th><td><a href="#14.4.12"><code>readonly</code></a></td></tr>
+ <tr><th>14.4.13</th><td><a href="#14.4.13"><code>virtual</code></a></td></tr>
+ <tr><th>14.4.14</th><td><a href="#14.4.14"><code>inverse</code></a></td></tr>
+ <tr><th>14.4.15</th><td><a href="#14.4.15"><code>on_delete</code></a></td></tr>
+ <tr><th>14.4.16</th><td><a href="#14.4.16"><code>version</code></a></td></tr>
+ <tr><th>14.4.17</th><td><a href="#14.4.17"><code>index</code></a></td></tr>
+ <tr><th>14.4.18</th><td><a href="#14.4.18"><code>unique</code></a></td></tr>
+ <tr><th>14.4.19</th><td><a href="#14.4.19"><code>unordered</code></a></td></tr>
+ <tr><th>14.4.20</th><td><a href="#14.4.20"><code>table</code></a></td></tr>
+ <tr><th>14.4.21</th><td><a href="#14.4.21"><code>load</code>/<code>update</code></a></td></tr>
+ <tr><th>14.4.22</th><td><a href="#14.4.22"><code>section</code></a></td></tr>
+ <tr><th>14.4.23</th><td><a href="#14.4.23"><code>added</code></a></td></tr>
+ <tr><th>14.4.24</th><td><a href="#14.4.24"><code>deleted</code></a></td></tr>
+ <tr><th>14.4.25</th><td><a href="#14.4.25"><code>index_type</code></a></td></tr>
+ <tr><th>14.4.26</th><td><a href="#14.4.26"><code>key_type</code></a></td></tr>
+ <tr><th>14.4.27</th><td><a href="#14.4.27"><code>value_type</code></a></td></tr>
+ <tr><th>14.4.28</th><td><a href="#14.4.28"><code>value_null</code>/<code>value_not_null</code></a></td></tr>
+ <tr><th>14.4.29</th><td><a href="#14.4.29"><code>id_options</code></a></td></tr>
+ <tr><th>14.4.30</th><td><a href="#14.4.30"><code>index_options</code></a></td></tr>
+ <tr><th>14.4.31</th><td><a href="#14.4.31"><code>key_options</code></a></td></tr>
+ <tr><th>14.4.32</th><td><a href="#14.4.32"><code>value_options</code></a></td></tr>
+ <tr><th>14.4.33</th><td><a href="#14.4.33"><code>id_column</code></a></td></tr>
+ <tr><th>14.4.34</th><td><a href="#14.4.34"><code>index_column</code></a></td></tr>
+ <tr><th>14.4.35</th><td><a href="#14.4.35"><code>key_column</code></a></td></tr>
+ <tr><th>14.4.36</th><td><a href="#14.4.36"><code>value_column</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>14.5</th><td><a href="#14.5">Namespace Pragmas</a>
+ <table class="toc">
+ <tr><th>14.5.1</th><td><a href="#14.5.1"><code>pointer</code></a></td></tr>
+ <tr><th>14.5.2</th><td><a href="#14.5.2"><code>table</code></a></td></tr>
+ <tr><th>14.5.3</th><td><a href="#14.5.3"><code>schema</code></a></td></tr>
+ <tr><th>14.5.4</th><td><a href="#14.5.4"><code>session</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>14.6</th><td><a href="#14.6">Object Model Pragmas</a>
+ <table class="toc">
+ <tr><th>14.6.1</th><td><a href="#14.6.1"><code>version</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>14.7</th><td><a href="#14.7">Index Definition Pragmas</a></td>
+ </tr>
+ <tr>
+ <th>14.8</th><td><a href="#14.8">Database Type Mapping Pragmas</a></td>
+ </tr>
+ <tr>
+ <th>14.9</th><td><a href="#14.9">C++ Compiler Warnings</a>
+ <table class="toc">
+ <tr><th>14.9.1</th><td><a href="#14.9.1">GNU C++</a></td></tr>
+ <tr><th>14.9.2</th><td><a href="#14.9.2">Visual C++</a></td></tr>
+ <tr><th>14.9.3</th><td><a href="#14.9.3">Sun C++</a></td></tr>
+ <tr><th>14.9.4</th><td><a href="#14.9.4">IBM XL C++</a></td></tr>
+ <tr><th>14.9.5</th><td><a href="#14.9.5">HP aC++</a></td></tr>
+ <tr><th>14.9.6</th><td><a href="#14.9.6">Clang</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>15</th><td><a href="#15">Advanced Techniques and Mechanisms</a>
+ <table class="toc">
+ <tr><th>15.1</th><td><a href="#15.1">Transaction Callbacks</a></td></tr>
+ <tr><th>15.2</th><td><a href="#15.2">Persistent Class Template Instantiations</a></td></tr>
+ <tr><th>15.3</th><td><a href="#15.3">Bulk Database Operations</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th colspan="2"><a href="#II">PART II DATABASE SYSTEMS</a></th>
+ </tr>
+
+ <tr>
+ <th>16</th><td><a href="#16">Multi-Database Support</a>
+ <table class="toc">
+ <tr><th>16.1</th><td><a href="#16.1">Static Multi-Database Support</a></td></tr>
+ <tr>
+ <th>16.2</th><td><a href="#16.2">Dynamic Multi-Database Support</a>
+ <table class="toc">
+ <tr><th>16.2.2</th><td><a href="#16.2.2">16.2.2 Dynamic Loading of Database Support Code</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>17</th><td><a href="#17">MySQL Database</a>
+ <table class="toc">
+ <tr>
+ <th>17.1</th><td><a href="#17.1">MySQL Type Mapping</a>
+ <table class="toc">
+ <tr><th>17.1.1</th><td><a href="#17.1.1">String Type Mapping</a></td></tr>
+ <tr><th>17.1.2</th><td><a href="#17.1.2">Binary Type Mapping</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>17.2</th><td><a href="#17.2">MySQL Database Class</a></td></tr>
+ <tr><th>17.3</th><td><a href="#17.3">MySQL Connection and Connection Factory</a></td></tr>
+ <tr><th>17.4</th><td><a href="#17.4">MySQL Exceptions</a></td></tr>
+ <tr>
+ <th>17.5</th><td><a href="#17.5">MySQL Limitations</a>
+ <table class="toc">
+ <tr><th>17.5.1</th><td><a href="#17.5.1">Foreign Key Constraints</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>17.6</th><td><a href="#17.6">MySQL Index Definition</a></td></tr>
+ <tr><th>17.7</th><td><a href="#17.7">MySQL Stored Procedures</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>18</th><td><a href="#18">SQLite Database</a>
+ <table class="toc">
+ <tr>
+ <th>18.1</th><td><a href="#18.1">SQLite Type Mapping</a>
+ <table class="toc">
+ <tr><th>18.1.1</th><td><a href="#18.1.1">String Type Mapping</a></td></tr>
+ <tr><th>18.1.2</th><td><a href="#18.1.2">Binary Type Mapping</a></td></tr>
+ <tr><th>18.1.3</th><td><a href="#18.1.3">Incremental <code>BLOB</code>/<code>TEXT</code> I/O</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>18.2</th><td><a href="#18.2">SQLite Database Class</a></td></tr>
+ <tr><th>18.3</th><td><a href="#18.3">SQLite Connection and Connection Factory</a></td></tr>
+ <tr><th>18.4</th><td><a href="#18.4">SQLite Exceptions</a></td></tr>
+ <tr>
+ <th>18.5</th><td><a href="#18.5">SQLite Limitations</a>
+ <table class="toc">
+ <tr><th>18.5.1</th><td><a href="#18.5.1">Query Result Caching</a></td></tr>
+ <tr><th>18.5.2</th><td><a href="#18.5.2">Automatic Assignment of Object Ids</a></td></tr>
+ <tr><th>18.5.3</th><td><a href="#18.5.3">Foreign Key Constraints</a></td></tr>
+ <tr><th>18.5.4</th><td><a href="#18.5.4">Constraint Violations</a></td></tr>
+ <tr><th>18.5.5</th><td><a href="#18.5.5">Sharing of Queries</a></td></tr>
+ <tr><th>18.5.6</th><td><a href="#18.5.6">Forced Rollback</a></td></tr>
+ <tr><th>18.5.7</th><td><a href="#18.5.7">Database Schema Evolution</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>18.6</th><td><a href="#18.6">SQLite Index Definition</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>19</th><td><a href="#19">PostgreSQL Database</a>
+ <table class="toc">
+ <tr>
+ <th>19.1</th><td><a href="#19.1">PostgreSQL Type Mapping</a>
+ <table class="toc">
+ <tr><th>19.1.1</th><td><a href="#19.1.1">String Type Mapping</a></td></tr>
+ <tr><th>19.1.2</th><td><a href="#19.1.2">Binary Type and <code>UUID</code> Mapping</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>19.2</th><td><a href="#19.2">PostgreSQL Database Class</a></td></tr>
+ <tr><th>19.3</th><td><a href="#19.3">PostgreSQL Connection and Connection Factory</a></td></tr>
+ <tr><th>19.4</th><td><a href="#19.4">PostgreSQL Exceptions</a></td></tr>
+ <tr>
+ <th>19.5</th><td><a href="#19.5">PostgreSQL Limitations</a>
+ <table class="toc">
+ <tr><th>19.5.1</th><td><a href="#19.5.1">Query Result Caching</a></td></tr>
+ <tr><th>19.5.2</th><td><a href="#19.5.2">Foreign Key Constraints</a></td></tr>
+ <tr><th>19.5.3</th><td><a href="#19.5.3">Unique Constraint Violations</a></td></tr>
+ <tr><th>19.5.4</th><td><a href="#19.5.4">Date-Time Format</a></td></tr>
+ <tr><th>19.5.5</th><td><a href="#19.5.5">Timezones</a></td></tr>
+ <tr><th>19.5.6</th><td><a href="#19.5.6"><code>NUMERIC</code> Type Support</a></td></tr>
+ <tr><th>19.5.7</th><td><a href="#19.5.7">Bulk Operations Support</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>19.6</th><td><a href="#19.6">PostgreSQL Index Definition</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>20</th><td><a href="#20">Oracle Database</a>
+ <table class="toc">
+ <tr>
+ <th>20.1</th><td><a href="#20.1">Oracle Type Mapping</a>
+ <table class="toc">
+ <tr><th>20.1.1</th><td><a href="#20.1.1">String Type Mapping</a></td></tr>
+ <tr><th>20.1.2</th><td><a href="#20.1.2">Binary Type Mapping</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>20.2</th><td><a href="#20.2">Oracle Database Class</a></td></tr>
+ <tr><th>20.3</th><td><a href="#20.3">Oracle Connection and Connection Factory</a></td></tr>
+ <tr><th>20.4</th><td><a href="#20.4">Oracle Exceptions</a></td></tr>
+ <tr>
+ <th>20.5</th><td><a href="#20.5">Oracle Limitations</a>
+ <table class="toc">
+ <tr><th>20.5.1</th><td><a href="#20.5.1">Identifier Truncation</a></td></tr>
+ <tr><th>20.5.2</th><td><a href="#20.5.2">Query Result Caching</a></td></tr>
+ <tr><th>20.5.3</th><td><a href="#20.5.3">Foreign Key Constraints</a></td></tr>
+ <tr><th>20.5.4</th><td><a href="#20.5.4">Unique Constraint Violations</a></td></tr>
+ <tr><th>20.5.5</th><td><a href="#20.5.5">Large <code>FLOAT</code> and <code>NUMBER</code> Types</a></td></tr>
+ <tr><th>20.5.6</th><td><a href="#20.5.6">Timezones</a></td></tr>
+ <tr><th>20.5.7</th><td><a href="#20.5.7"><code>LONG</code> Types</a></td></tr>
+ <tr><th>20.5.8</th><td><a href="#20.5.8">LOB Types and By-Value Accessors/Modifiers</a></td></tr>
+ <tr><th>20.5.9</th><td><a href="#20.5.9">Database Schema Evolution</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>20.6</th><td><a href="#20.6">Oracle Index Definition</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>21</th><td><a href="#21">Microsoft SQL Server Database</a>
+ <table class="toc">
+ <tr>
+ <th>21.1</th><td><a href="#21.1">SQL Server Type Mapping</a>
+ <table class="toc">
+ <tr><th>21.1.1</th><td><a href="#21.1.1">String Type Mapping</a></td></tr>
+ <tr><th>21.1.2</th><td><a href="#21.1.2">Binary Type and <code>UNIQUEIDENTIFIER</code> Mapping</a></td></tr>
+ <tr><th>21.1.3</th><td><a href="#21.1.3"><code>ROWVERSION</code> Mapping</a></td></tr>
+ <tr><th>21.1.4</th><td><a href="#21.1.4">Long String and Binary Types</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>21.2</th><td><a href="#21.2">SQL Server Database Class</a></td></tr>
+ <tr><th>21.3</th><td><a href="#21.3">SQL Server Connection and Connection Factory</a></td></tr>
+ <tr><th>21.4</th><td><a href="#21.4">SQL Server Exceptions</a></td></tr>
+ <tr>
+ <th>21.5</th><td><a href="#21.5">SQL Server Limitations</a>
+ <table class="toc">
+ <tr><th>21.5.1</th><td><a href="#21.5.1">Query Result Caching</a></td></tr>
+ <tr><th>21.5.2</th><td><a href="#21.5.2">Foreign Key Constraints</a></td></tr>
+ <tr><th>21.5.3</th><td><a href="#21.5.3">Unique Constraint Violations</a></td></tr>
+ <tr><th>21.5.4</th><td><a href="#21.5.4">Multi-threaded Windows Applications</a></td></tr>
+ <tr><th>21.5.5</th><td><a href="#21.5.5">Affected Row Count and DDL Statements</a></td></tr>
+ <tr><th>21.5.6</th><td><a href="#21.5.6">Long Data and Auto Object Ids, <code>ROWVERSION</code></a></td></tr>
+ <tr><th>21.5.7</th><td><a href="#21.5.7">Long Data and By-Value Accessors/Modifiers</a></td></tr>
+ <tr><th>21.5.8</th><td><a href="#21.5.8">Bulk Update and <code>ROWVERSION</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>21.6</th><td><a href="#21.6">SQL Server Index Definition</a></td></tr>
+ <tr><th>21.7</th><td><a href="#21.7">SQL Server Stored Procedures</a></td></tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th colspan="2"><a href="#III">PART III PROFILES</a></th>
+ </tr>
+
+ <tr>
+ <th>22</th><td><a href="#22">Profiles Introduction</a></td>
+ </tr>
+
+ <tr>
+ <th>23</th><td><a href="#23">Boost Profile</a>
+ <table class="toc">
+ <tr><th>23.1</th><td><a href="#23.1">Smart Pointers Library</a></td></tr>
+ <tr><th>23.2</th><td><a href="#23.2">Unordered Containers Library</a></td></tr>
+ <tr><th>23.3</th><td><a href="#23.3">Multi-Index Container Library</a></td></tr>
+ <tr><th>23.4</th><td><a href="#23.4">Optional Library</a></td></tr>
+ <tr>
+ <th>23.5</th><td><a href="#23.5">Date Time Library</a>
+ <table class="toc">
+ <tr><th>23.5.1</th><td><a href="#23.5.1">MySQL Database Type Mapping</a></td></tr>
+ <tr><th>23.5.2</th><td><a href="#23.5.2">SQLite Database Type Mapping</a></td></tr>
+ <tr><th>23.5.3</th><td><a href="#23.5.3">PostgreSQL Database Type Mapping</a></td></tr>
+ <tr><th>23.5.4</th><td><a href="#23.5.4">Oracle Database Type Mapping</a></td></tr>
+ <tr><th>23.5.5</th><td><a href="#23.5.5">SQL Server Database Type Mapping</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>23.6</th><td><a href="#23.6">Uuid Library</a>
+ <table class="toc">
+ <tr><th>23.6.1</th><td><a href="#23.6.1">MySQL Database Type Mapping</a></td></tr>
+ <tr><th>23.6.2</th><td><a href="#23.6.2">SQLite Database Type Mapping</a></td></tr>
+ <tr><th>23.6.3</th><td><a href="#23.6.3">PostgreSQL Database Type Mapping</a></td></tr>
+ <tr><th>23.6.4</th><td><a href="#23.6.4">Oracle Database Type Mapping</a></td></tr>
+ <tr><th>23.6.5</th><td><a href="#23.6.5">SQL Server Database Type Mapping</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <th>24</th><td><a href="#24">Qt Profile</a>
+ <table class="toc">
+ <tr>
+ <th>24.1</th><td><a href="#24.1">Basic Types Library</a>
+ <table class="toc">
+ <tr><th>24.1.1</th><td><a href="#24.1.1">MySQL Database Type Mapping</a></td></tr>
+ <tr><th>24.1.2</th><td><a href="#24.1.2">SQLite Database Type Mapping</a></td></tr>
+ <tr><th>24.1.3</th><td><a href="#24.1.3">PostgreSQL Database Type Mapping</a></td></tr>
+ <tr><th>24.1.4</th><td><a href="#24.1.4">Oracle Database Type Mapping</a></td></tr>
+ <tr><th>24.1.5</th><td><a href="#24.1.5">SQL Server Database Type Mapping</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr><th>24.2</th><td><a href="#24.2">Smart Pointers Library</a></td></tr>
+ <tr>
+ <th>24.3</th><td><a href="#24.3">Containers Library</a>
+ <table class="toc">
+ <tr><th>24.3.1</th><td><a href="#24.3.1">Change-Tracking <code>QList</code></a></td></tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>24.4</th><td><a href="#24.4">Date Time Library</a>
+ <table class="toc">
+ <tr><th>24.4.1</th><td><a href="#24.4.1">MySQL Database Type Mapping</a></td></tr>
+ <tr><th>24.4.2</th><td><a href="#24.4.2">SQLite Database Type Mapping</a></td></tr>
+ <tr><th>24.4.3</th><td><a href="#24.4.3">PostgreSQL Database Type Mapping</a></td></tr>
+ <tr><th>24.4.4</th><td><a href="#24.4.4">Oracle Database Type Mapping</a></td></tr>
+ <tr><th>24.4.5</th><td><a href="#24.4.5">SQL Server Database Type Mapping</a></td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ </table>
+ </div>
+
+ <hr class="page-break"/>
+ <h1><a name="0">Preface</a></h1>
+
+ <p>As more critical aspects of our lives become dependant on software
+ systems, more and more applications are required to save the data
+ they work on in persistent and reliable storage. Database management
+ systems and, in particular, relational database management systems
+ (RDBMS) are commonly used for such storage. However, while the
+ application development techniques and programming languages have
+ evolved significantly over the past decades, the relational database
+ technology in this area stayed relatively unchanged. In particular,
+ this led to the now infamous mismatch between the object-oriented
+ model used by many modern applications and the relational model still
+ used by RDBMS.</p>
+
+ <p>While relational databases may be inconvenient to use from modern
+ programming languages, they are still the main choice for many
+ applications due to their maturity, reliability, as well as the
+ availability of tools and alternative implementations.</p>
+
+ <p>To allow application developers to utilize relational databases
+ from their object-oriented applications, a technique called
+ object-relational mapping (ORM) is often used. It involves a
+ conversion layer that maps between objects in the application's
+ memory and their relational representation in the database. While
+ the object-relational mapping code can be written manually,
+ automated ORM systems are available for most object-oriented
+ programming languages in use today.</p>
+
+ <p>ODB is an ORM system for the C++ programming language. It was
+ designed and implemented with the following main goals:</p>
+
+ <ul class="list">
+ <li>Provide a fully-automatic ORM system. In particular, the
+ application developer should not have to manually write any
+ mapping code, neither for persistent classes nor for their
+ data member. </li>
+
+ <li>Provide clean and easy to use object-oriented persistence
+ model and database APIs that support the development of realistic
+ applications for a wide variety of domains.</li>
+
+ <li>Provide a portable and thread-safe implementation. ODB should be
+ written in standard C++ and capable of persisting any standard
+ C++ classes.</li>
+
+ <li>Provide profiles that integrate ODB with type systems of
+ widely-used frameworks and libraries such as Qt and Boost.</li>
+
+ <li>Provide a high-performance and low overhead implementation. ODB
+ should make efficient use of database and application resources.</li>
+
+ </ul>
+
+
+ <h2><a name="0.1">About This Document</a></h2>
+
+ <p>The goal of this manual is to provide you with an understanding
+ of the object persistence model and APIs which are implemented by ODB.
+ As such, this document is intended for C++ application developers and
+ software architects who are looking for a C++ object persistence
+ solution. Prior experience with C++ is required to understand
+ this document. A basic understanding of relational database systems
+ is advantageous but not expected or required.</p>
+
+
+ <h2><a name="0.2">More Information</a></h2>
+
+ <p>Beyond this manual, you may also find the following sources of
+ information useful:</p>
+
+ <ul class="list">
+ <li><a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual.</a></li>
+
+ <li>The <code>INSTALL</code> files in the ODB source packages provide
+ build instructions for various platforms.</li>
+
+ <li>The <code>odb-examples</code> package contains a collection of
+ examples and a README file with an overview of each example.</li>
+
+ <li>The <a href="http://www.codesynthesis.com/mailman/listinfo/odb-users">odb-users</a>
+ mailing list is the place to ask technical questions about ODB.
+ Furthermore, the searchable
+ <a href="http://www.codesynthesis.com/pipermail/odb-users/">archives</a>
+ may already have answers to some of your questions.</li>
+
+ </ul>
+
+
+ <!-- PART -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="I">PART I&nbsp;&nbsp;
+ <span style="font-weight: normal;">OBJECT-RELATIONAL MAPPING</span></a></h1>
+
+ <p>Part I describes the essential database concepts, APIs, and tools that
+ together comprise the object-relational mapping for C++ as implemented
+ by ODB. It consists of the following chapters.</p>
+
+ <table class="toc">
+ <tr><th>1</th><td><a href="#1">Introduction</a></td></tr>
+ <tr><th>2</th><td><a href="#2">Hello World Example</a></td></tr>
+ <tr><th>3</th><td><a href="#3">Working with Persistent Objects</a></td></tr>
+ <tr><th>4</th><td><a href="#4">Querying the Database</a></td></tr>
+ <tr><th>5</th><td><a href="#5">Containers</a></td></tr>
+ <tr><th>6</th><td><a href="#6">Relationships</a></td></tr>
+ <tr><th>7</th><td><a href="#7">Value Types</a></td></tr>
+ <tr><th>8</th><td><a href="#8">Inheritance</a></td></tr>
+ <tr><th>10</th><td><a href="#10">Views</a></td></tr>
+ <tr><th>11</th><td><a href="#11">Session</a></td></tr>
+ <tr><th>12</th><td><a href="#12">Optimistic Concurrency</a></td></tr>
+ <tr><th>13</th><td><a href="#13">Database Schema Evolution</a></td></tr>
+ <tr><th>14</th><td><a href="#14">ODB Pragma Language</a></td></tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="1">1 Introduction</a></h1>
+
+ <p>ODB is an object-relational mapping (ORM) system for C++. It provides
+ tools, APIs, and library support that allow you to persist C++ objects
+ to a relational database (RDBMS) without having to deal with tables,
+ columns, or SQL and without manually writing any of the mapping code.</p>
+
+ <p>ODB is highly flexible and customizable. It can either completely
+ hide the relational nature of the underlying database or expose
+ some of the details as required. For example, you can automatically
+ map basic C++ types to suitable SQL types, generate the relational
+ database schema for your persistent classes, and use simple, safe,
+ and yet powerful object query language instead of SQL. Or you can
+ assign SQL types to individual data members, use the existing
+ database schema, run native SQL <code>SELECT</code> queries, and
+ call stored procedures. In fact, at an extreme, ODB can be used
+ as <em>just</em> a convenient way to handle results of native SQL
+ queries.</p>
+
+ <p>ODB is not a framework. It does not dictate how you should write
+ your application. Rather, it is designed to fit into your
+ style and architecture by only handling object persistence
+ and not interfering with any other functionality. There is
+ no common base type that all persistent classes should derive
+ from nor are there any restrictions on the data member types
+ in persistent classes. Existing classes can be made persistent
+ with a few or no modifications.</p>
+
+ <p>ODB has been designed for high performance and low memory
+ overhead. Prepared statements are used to send and receive
+ object state in binary format instead of text which reduces
+ the load on the application and the database server. Extensive
+ caching of connections, prepared statements, and buffers saves
+ time and resources on connection establishment, statement parsing,
+ and memory allocations. For each supported database system the
+ native C API is used instead of ODBC or higher-level wrapper
+ APIs to reduce overhead and provide the most efficient implementation
+ for each database operation. Finally, persistent classes have
+ zero memory overhead. There are no hidden "database" members
+ that each class must have nor are there per-object data structures
+ allocated by ODB.</p>
+
+ <p>In this chapter we present a high-level overview of ODB.
+ We will start with the ODB architecture and then outline the
+ workflow of building an application that uses ODB. We will
+ then continue by contrasting the drawbacks of the traditional
+ way of saving C++ objects to relational databases with the
+ benefits of using ODB for object persistence. We conclude the
+ chapter by discussing the C++ standards supported by ODB. The
+ next chapter takes a more hands-on approach and shows the
+ concrete steps necessary to implement object persistence in
+ a simple "Hello World" application.</p>
+
+ <h2><a name="1.1">1.1 Architecture and Workflow</a></h2>
+
+ <p>From the application developer's perspective, ODB
+ consists of three main components: the ODB compiler, the common
+ runtime library, called <code>libodb</code>, and the
+ database-specific runtime libraries, called
+ <code>libodb-&lt;database></code>, where &lt;database> is
+ the name of the database system this runtime
+ is for, for example, <code>libodb-mysql</code>. For instance,
+ if the application is going to use the MySQL database for
+ object persistence, then the three ODB components that this
+ application will use are the ODB compiler, <code>libodb</code>
+ and <code>libodb-mysql</code>.</p>
+
+ <p>The ODB compiler generates the database support code for
+ persistent classes in your application. The input to the ODB
+ compiler is one or more C++ header files defining C++ classes
+ that you want to make persistent. For each input header file
+ the ODB compiler generates a set of C++ source files implementing
+ conversion between persistent C++ classes defined in this
+ header and their database representation. The ODB compiler
+ can also generate a database schema file that creates tables
+ necessary to store the persistent classes.</p>
+
+ <p>The ODB compiler is a real C++ compiler except that it produces
+ C++ instead of assembly or machine code. In particular, it is not
+ an ad-hoc header pre-processor that is only capable of recognizing
+ a subset of C++. ODB is capable of parsing any standard C++ code.</p>
+
+ <p>The common runtime library defines database system-independent
+ interfaces that your application can use to manipulate persistent
+ objects. The database-specific runtime library provides implementations
+ of these interfaces for a concrete database as well as other
+ database-specific utilities that are used by the generated code.
+ Normally, the application does not use the database-specific
+ runtime library directly but rather works with it via the common
+ interfaces from <code>libodb</code>. The following diagram shows
+ the object persistence architecture of an application that uses
+ MySQL as the underlying database system:</p>
+
+ <!-- align=center is needed for html2ps -->
+ <div class="img" align="center"><img src="odb-arch.png"/></div>
+
+ <p>The ODB system also defines two special-purpose languages:
+ the ODB Pragma Language and ODB Query Language. The ODB Pragma
+ Language is used to communicate various properties of persistent
+ classes to the ODB compiler by means of special <code>#pragma</code>
+ directives embedded in the C++ header files. It controls aspects
+ of the object-relational mapping such as names of tables and columns
+ that are used for persistent classes and their members or mapping between
+ C++ types and database types.</p>
+
+ <p>The ODB Query Language is an object-oriented database query
+ language that can be used to search for objects matching
+ certain criteria. It is modeled after and is integrated into
+ C++ allowing you to write expressive and safe queries that look
+ and feel like ordinary C++.</p>
+
+ <p>The use of the ODB compiler to generate database support code
+ adds an additional step to your application build sequence. The
+ following diagram outlines the typical build workflow of an
+ application that uses ODB:</p>
+
+ <!-- align=center is needed for html2ps -->
+ <div class="img" align="center"><img src="odb-flow.png"/></div>
+
+ <h2><a name="1.2">1.2 Benefits</a></h2>
+
+ <p>The traditional way of saving C++ objects to relational databases
+ requires that you manually write code which converts between the database
+ and C++ representations of each persistent class. The actions that
+ such code usually performs include conversion between C++ values and
+ strings or database types, preparation and execution of SQL queries,
+ as well as handling the result sets. Writing this code manually has
+ the following drawbacks:</p>
+
+ <ul class="list">
+ <li><b>Difficult and time consuming.</b> Writing database conversion
+ code for any non-trivial application requires extensive
+ knowledge of the specific database system and its APIs.
+ It can also take a considerable amount of time to write
+ and maintain. Supporting multi-threaded applications can
+ complicate this task even further.</li>
+
+ <li><b>Suboptimal performance.</b> Optimal conversion often
+ requires writing large amounts of extra code, such as
+ parameter binding for prepared statements and caching
+ of connections, statements, and buffers. Writing code
+ like this in an ad-hoc manner is often too difficult
+ and time consuming.</li>
+
+ <li><b>Database vendor lock-in.</b> The conversion code is written for
+ a specific database which makes it hard to switch to another
+ database vendor.</li>
+
+ <li><b>Lack of type safety.</b> It is easy to misspell column names or
+ pass incompatible values in SQL queries. Such errors will
+ only be detected at runtime.</li>
+
+ <li><b>Complicates the application.</b> The database conversion code
+ often ends up interspersed throughout the application making it
+ hard to debug, change, and maintain.</li>
+ </ul>
+
+ <p>In contrast, using ODB for C++ object persistence has the
+ following benefits:</p>
+
+ <ul class="list">
+ <li><b>Ease of use.</b> ODB automatically generates database conversion
+ code from your C++ class declarations and allows you to manipulate
+ persistent objects using simple and thread-safe object-oriented
+ database APIs.</li>
+
+ <li><b>Concise code.</b> With ODB hiding the details of the underlying
+ database, the application logic is written using the natural object
+ vocabulary instead of tables, columns and SQL. The resulting code
+ is simpler and thus easier to read and understand.</li>
+
+ <li><b>Optimal performance.</b> ODB has been designed for high performance
+ and low memory overhead. All the available optimization techniques,
+ such as prepared statements and extensive connection, statement,
+ and buffer caching, are used to provide the most efficient
+ implementation for each database operation.</li>
+
+ <li><b>Database portability.</b> Because the database conversion code
+ is automatically generated, it is easy to switch from one database
+ vendor to another. In fact, it is possible to test your application
+ on several database systems before making a choice.</li>
+
+ <li><b>Safety.</b> The ODB object persistence and query APIs are
+ statically typed. You use C++ identifiers instead of strings
+ to refer to object members and the generated code makes sure
+ database and C++ types are compatible. All this helps catch
+ programming errors at compile-time rather than at runtime.</li>
+
+ <li><b>Maintainability.</b> Automatic code generation minimizes the
+ effort needed to adapt the application to changes in persistent
+ classes. The database support code is kept separately from the
+ class declarations and application logic. This makes the
+ application easier to debug and maintain.</li>
+ </ul>
+
+ <p>Overall, ODB provides an easy to use yet flexible and powerful
+ object-relational mapping (ORM) system for C++. Unlike other
+ ORM implementations for C++ that still require you to write
+ database conversion or member registration code for each
+ persistent class, ODB keeps persistent classes purely
+ declarative. The functional part, the database conversion
+ code, is automatically generated by the ODB compiler from
+ these declarations.</p>
+
+ <h2><a name="1.3">1.3 Supported C++ Standards</a></h2>
+
+ <p>ODB provides support for ISO/IEC C++ 1998/2003 (C++98/03),
+ ISO/IEC TR 19768 C++ Library Extensions (C++ TR1), and
+ ISO/IEC C++ 2011 (C++11). While the majority of the examples in
+ this manual use C++98/03, support for the new functionality and
+ library components introduced in TR1 and C++11 are discussed
+ throughout the document. The <code>c++11</code> example in the
+ <code>odb-examples</code> package also shows ODB support for
+ various C++11 features.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="2">2 Hello World Example</a></h1>
+
+ <p>In this chapter we will show how to create a simple C++
+ application that relies on ODB for object persistence using
+ the traditional "Hello World" example. In particular, we will
+ discuss how to declare persistent classes, generate database
+ support code, as well as compile and run our application. We
+ will also learn how to make objects persistent, load, update
+ and delete persistent objects, as well as query the database
+ for persistent objects that match certain criteria. The example
+ also shows how to define and use views, a mechanism that allows
+ us to create projections of persistent objects, database tables,
+ or to handle results of native SQL queries or stored procedure
+ calls.</p>
+
+ <p>The code presented in this chapter is based on the
+ <code>hello</code> example which can be found in the
+ <code>odb-examples</code> package of the ODB distribution.</p>
+
+ <h2><a name="2.1">2.1 Declaring Persistent Classes</a></h2>
+
+ <p>In our "Hello World" example we will depart slightly from
+ the norm and say hello to people instead of the world. People
+ in our application will be represented as objects of C++ class
+ <code>person</code> which is saved in <code>person.hxx</code>:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#include &lt;string>
+
+class person
+{
+public:
+ person (const std::string&amp; first,
+ const std::string&amp; last,
+ unsigned short age);
+
+ const std::string&amp; first () const;
+ const std::string&amp; last () const;
+
+ unsigned short age () const;
+ void age (unsigned short);
+
+private:
+ std::string first_;
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>In order not to miss anyone whom we need to greet, we would like
+ to save the <code>person</code> objects in a database. To achieve this
+ we declare the <code>person</code> class as persistent:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#include &lt;string>
+
+#include &lt;odb/core.hxx> // (1)
+
+#pragma db object // (2)
+class person
+{
+ ...
+
+private:
+ person () {} // (3)
+
+ friend class odb::access; // (4)
+
+ #pragma db id auto // (5)
+ unsigned long id_; // (5)
+
+ std::string first_;
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>To be able to save the <code>person</code> objects in the database
+ we had to make five changes, marked with (1) to (5), to the original
+ class definition. The first change is the inclusion of the ODB
+ header <code>&lt;odb/core.hxx></code>. This header provides a number
+ of core ODB declarations, such as <code>odb::access</code>, that
+ are used to define persistent classes.</p>
+
+ <p>The second change is the addition of <code>db&nbsp;object</code>
+ pragma just before the class definition. This pragma tells the
+ ODB compiler that the class that follows is persistent. Note
+ that making a class persistent does not mean that all objects
+ of this class will automatically be stored in the database.
+ You would still create ordinary or <em>transient</em> instances
+ of this class just as you would before. The difference is that
+ now you can make such transient instances persistent, as we will
+ see shortly.</p>
+
+ <p>The third change is the addition of the default constructor.
+ The ODB-generated database support code will use this constructor
+ when instantiating an object from the persistent state. Just as we have
+ done for the <code>person</code> class, you can make the default
+ constructor private or protected if you don't want to make it
+ available to the users of your class. Note also that with some
+ limitations it is possible to have a persistent class without
+ the default constructor.</p>
+
+ <p>With the fourth change we make the <code>odb::access</code> class a
+ friend of our <code>person</code> class. This is necessary to make
+ the default constructor and the data members accessible to the
+ database support code. If your class has a public default constructor and
+ either public data members or public accessors and modifiers for the
+ data members, then the <code>friend</code> declaration is unnecessary.</p>
+
+ <p>The final change adds a data member called <code>id_</code> which
+ is preceded by another pragma. In ODB every persistent object normally
+ has a unique, within its class, identifier. Or, in other words, no two
+ persistent instances of the same type have equal identifiers. While it
+ is possible to define a persistent class without an object id, the number
+ of database operations that can be performed on such a class is limited.
+ For our class we use an integer id. The <code>db&nbsp;id auto</code>
+ pragma that precedes the <code>id_</code> member tells the ODB compiler
+ that the following member is the object's identifier. The
+ <code>auto</code> specifier indicates that it is a database-assigned
+ id. A unique id will be automatically generated by the database and
+ assigned to the object when it is made persistent.</p>
+
+ <p>In this example we chose to add an identifier because none of
+ the existing members could serve the same purpose. However, if
+ a class already has a member with suitable properties, then it
+ is natural to use that member as an identifier. For example,
+ if our <code>person</code> class contained some form of personal
+ identification (SSN in the United States or ID/passport number
+ in other countries), then we could use that as an id. Or, if
+ we stored an email associated with each person, then we could
+ have used that if each person is presumed to have a unique
+ email address.</p>
+
+ <p>As another example, consider the following alternative version
+ of the <code>person</code> class. Here we use one of
+ the existing data members as id. Also the data members are kept
+ private and are instead accessed via public accessor and modifier
+ functions. Finally, the ODB pragmas are grouped together and are
+ placed after the class definition. They could have also been moved
+ into a separate header leaving the original class completely
+ unchanged (for more information on such a non-intrusive conversion
+ refer to <a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
+
+ <pre class="cxx">
+class person
+{
+public:
+ person ();
+
+ const std::string&amp; email () const;
+ void email (const std::string&amp;);
+
+ const std::string&amp; get_name () const;
+ std::string&amp; set_name ();
+
+ unsigned short getAge () const;
+ void setAge (unsigned short);
+
+private:
+ std::string email_;
+ std::string name_;
+ unsigned short age_;
+};
+
+#pragma db object(person)
+#pragma db member(person::email_) id
+ </pre>
+
+ <p>Now that we have the header file with the persistent class, let's
+ see how we can generate that database support code.</p>
+
+ <h2><a name="2.2">2.2 Generating Database Support Code</a></h2>
+
+ <p>The persistent class definition that we created in the previous
+ section was particularly light on any code that could actually
+ do the job and store the person's data to a database. There
+ was no serialization or deserialization code, not even data member
+ registration, that you would normally have to write by hand in
+ other ORM libraries for C++. This is because in ODB code
+ that translates between the database and C++ representations
+ of an object is automatically generated by the ODB compiler.</p>
+
+ <p>To compile the <code>person.hxx</code> header we created in the
+ previous section and generate the support code for the MySQL
+ database, we invoke the ODB compiler from a terminal (UNIX) or
+ a command prompt (Windows):</p>
+
+ <pre class="terminal">
+odb -d mysql --generate-query person.hxx
+ </pre>
+
+ <p>We will use MySQL as the database of choice in the remainder of
+ this chapter, though other supported database systems can be used
+ instead.</p>
+
+ <p>If you haven't installed the common ODB runtime library
+ (<code>libodb</code>) or installed it into a directory where
+ C++ compilers don't search for headers by default,
+ then you may get the following error:</p>
+
+ <pre class="terminal">
+person.hxx:10:24: fatal error: odb/core.hxx: No such file or directory
+ </pre>
+
+ <p>To resolve this you will need to specify the <code>libodb</code> headers
+ location with the <code>-I</code> preprocessor option, for example:</p>
+
+ <pre class="terminal">
+odb -I.../libodb -d mysql --generate-query person.hxx
+ </pre>
+
+ <p>Here <code>.../libodb</code> represents the path to the
+ <code>libodb</code> directory.</p>
+
+ <p>The above invocation of the ODB compiler produces three C++ files:
+ <code>person-odb.hxx</code>, <code>person-odb.ixx</code>,
+ <code>person-odb.cxx</code>. You normally don't use types
+ or functions contained in these files directly. Rather, all
+ you have to do is include <code>person-odb.hxx</code> in
+ C++ files where you are performing database operations
+ with classes from <code>person.hxx</code> as well as compile
+ <code>person-odb.cxx</code> and link the resulting object
+ file to your application.</p>
+
+ <p>You may be wondering what the <code>--generate-query</code>
+ option is for. It instructs the ODB compiler to generate
+ optional query support code that we will use later in our
+ "Hello World" example. Another option that we will find
+ useful is <code>--generate-schema</code>. This option
+ makes the ODB compiler generate a fourth file,
+ <code>person.sql</code>, which is the database schema
+ for the persistent classes defined in <code>person.hxx</code>:</p>
+
+ <pre class="terminal">
+odb -d mysql --generate-query --generate-schema person.hxx
+ </pre>
+
+ <p>The database schema file contains SQL statements that creates
+ tables necessary to store the persistent classes. We will learn
+ how to use it in the next section.</p>
+
+ <p>If you would like to see a list of all the available ODB compiler
+ options, refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a>.</p>
+
+ <p>Now that we have the persistent class and the database support
+ code, the only part that is left is the application code that
+ does something useful with all of this. But before we move on to
+ the fun part, let's first learn how to build and run an application
+ that uses ODB. This way when we have some application code
+ to try, there are no more delays before we can run it.</p>
+
+ <h2><a name="2.3">2.3 Compiling and Running</a></h2>
+
+ <p>Assuming that the <code>main()</code> function with the application
+ code is saved in <code>driver.cxx</code> and the database support
+ code and schema are generated as described in the previous section,
+ to build our application we will first need to compile all the C++
+ source files and then link them with two ODB runtime libraries.</p>
+
+ <p>On UNIX, the compilation part can be done with the following commands
+ (substitute <code>c++</code> with your C++ compiler name; for Microsoft
+ Visual Studio setup, see the <code>odb-examples</code> package):</p>
+
+ <pre class="terminal">
+c++ -c driver.cxx
+c++ -c person-odb.cxx
+ </pre>
+
+ <p>Similar to the ODB compilation, if you get an error stating that
+ a header in <code>odb/</code> or <code>odb/mysql</code> directory
+ is not found, you will need to use the <code>-I</code>
+ preprocessor option to specify the location of the common ODB runtime
+ library (<code>libodb</code>) and MySQL ODB runtime library
+ (<code>libodb-mysql</code>).</p>
+
+ <p>Once the compilation is done, we can link the application with
+ the following command:</p>
+
+ <pre class="terminal">
+c++ -o driver driver.o person-odb.o -lodb-mysql -lodb
+ </pre>
+
+ <p>Notice that we link our application with two ODB libraries:
+ <code>libodb</code> which is a common runtime library and
+ <code>libodb-mysql</code> which is a MySQL runtime library
+ (if you use another database, then the name of this library
+ will change accordingly). If you get an error saying that
+ one of these libraries could not be found, then you will need
+ to use the <code>-L</code> linker option to specify their locations.</p>
+
+ <p>Before we can run our application we need to create a database
+ schema using the generated <code>person.sql</code> file. For MySQL
+ we can use the <code>mysql</code> client program, for example:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+ </pre>
+
+ <p>The above command will log in to a local MySQL server as user
+ <code>odb_test</code> without a password and use the database
+ named <code>odb_test</code>. Beware that after executing this
+ command, all the data stored in the <code>odb_test</code> database
+ will be deleted.</p>
+
+ <p>Note also that using a standalone generated SQL file is not the
+ only way to create a database schema in ODB. We can also embed
+ the schema directly into our application or use custom schemas
+ that were not generated by the ODB compiler. Refer to
+ <a href="#3.4">Section 3.4, "Database"</a> for details.</p>
+
+ <p>Once the database schema is ready, we run our application
+ using the same login and database name:</p>
+
+ <pre class="terminal">
+./driver --user odb_test --database odb_test
+ </pre>
+
+
+ <h2><a name="2.4">2.4 Making Objects Persistent</a></h2>
+
+ <p>Now that we have the infrastructure work out of the way, it
+ is time to see our first code fragment that interacts with the
+ database. In this section we will learn how to make <code>person</code>
+ objects persistent:</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+#include &lt;memory> // std::auto_ptr
+#include &lt;iostream>
+
+#include &lt;odb/database.hxx>
+#include &lt;odb/transaction.hxx>
+
+#include &lt;odb/mysql/database.hxx>
+
+#include "person.hxx"
+#include "person-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
+
+ unsigned long john_id, jane_id, joe_id;
+
+ // Create a few persistent person objects.
+ //
+ {
+ person john ("John", "Doe", 33);
+ person jane ("Jane", "Doe", 32);
+ person joe ("Joe", "Dirt", 30);
+
+ transaction t (db->begin ());
+
+ // Make objects persistent and save their ids for later use.
+ //
+ john_id = db->persist (john);
+ jane_id = db->persist (jane);
+ joe_id = db->persist (joe);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; e.what () &lt;&lt; endl;
+ return 1;
+ }
+}
+ </pre>
+
+ <p>Let's examine this code piece by piece. At the beginning we include
+ a bunch of headers. After the standard C++ headers we include
+ <code>&lt;odb/database.hxx></code>
+ and <code>&lt;odb/transaction.hxx></code> which define database
+ system-independent <code>odb::database</code> and
+ <code>odb::transaction</code> interfaces. Then we include
+ <code>&lt;odb/mysql/database.hxx></code> which defines the
+ MySQL implementation of the <code>database</code> interface. Finally,
+ we include <code>person.hxx</code> and <code>person-odb.hxx</code>
+ which define our persistent <code>person</code> class.</p>
+
+ <p>Then we have two <code>using namespace</code> directives. The first
+ one brings in the names from the standard namespace and the second
+ brings in the ODB declarations which we will use later in the file.
+ Notice that in the second directive we use the <code>odb::core</code>
+ namespace instead of just <code>odb</code>. The former only brings
+ into the current namespace the essential ODB names, such as the
+ <code>database</code> and <code>transaction</code> classes, without
+ any of the auxiliary objects. This minimizes the likelihood of name
+ conflicts with other libraries. Note also that you should continue
+ using the <code>odb</code> namespace when qualifying individual names.
+ For example, you should write <code>odb::database</code>, not
+ <code>odb::core::database</code>.</p>
+
+ <p>Once we are in <code>main()</code>, the first thing we do is create
+ the MySQL database object. Notice that this is the last line in
+ <code>driver.cxx</code> that mentions MySQL explicitly; the rest
+ of the code works through the common interfaces and is database
+ system-independent. We use the <code>argc</code>/<code>argv</code>
+ <code>mysql::database</code> constructor which automatically
+ extract the database parameters, such as login name, password,
+ database name, etc., from the command line. In your own applications
+ you may prefer to use other <code>mysql::database</code>
+ constructors which allow you to pass this information directly
+ (<a href="#17.2">Section 17.2, "MySQL Database Class"</a>).</p>
+
+ <p>Next, we create three <code>person</code> objects. Right now they are
+ transient objects, which means that if we terminate the application
+ at this point, they will be gone without any evidence of them ever
+ existing. The next line starts a database transaction. We discuss
+ transactions in detail later in this manual. For now, all we need
+ to know is that all ODB database operations must be performed within
+ a transaction and that a transaction is an atomic unit of work; all
+ database operations performed within a transaction either succeed
+ (committed) together or are automatically undone (rolled back).</p>
+
+ <p>Once we are in a transaction, we call the <code>persist()</code>
+ database function on each of our <code>person</code> objects.
+ At this point the state of each object is saved in the database.
+ However, note that this state is not permanent until and unless
+ the transaction is committed. If, for example, our application
+ crashes at this point, there will still be no evidence of our
+ objects ever existing.</p>
+
+ <p>In our case, one more thing happens when we call <code>persist()</code>.
+ Remember that we decided to use database-assigned identifiers for our
+ <code>person</code> objects. The call to <code>persist()</code> is
+ where this assignment happens. Once this function returns, the
+ <code>id_</code> member contains this object's unique identifier.
+ As a convenience, the <code>persist()</code> function also returns
+ a copy of the object's identifier that it made persistent. We
+ save the returned identifier for each object in a local variable.
+ We will use these identifiers later in the chapter to perform other
+ database operations on our persistent objects.</p>
+
+ <p>After we have persisted our objects, it is time to commit the
+ transaction and make the changes permanent. Only after the
+ <code>commit()</code> function returns successfully, are we
+ guaranteed that the objects are made persistent. Continuing
+ with the crash example, if our application terminates after
+ the commit for whatever reason, the objects' state in the
+ database will remain intact. In fact, as we will discover
+ shortly, our application can be restarted and load the
+ original objects from the database. Note also that a
+ transaction must be committed explicitly with the
+ <code>commit()</code> call. If the <code>transaction</code>
+ object leaves scope without the transaction being
+ explicitly committed or rolled back, it will automatically be
+ rolled back. This behavior allows you not to worry about
+ exceptions being thrown within a transaction; if they
+ cross the transaction boundary, the transaction will
+ automatically be rolled back and all the changes made
+ to the database undone.</p>
+
+ <p>The final bit of code in our example is the <code>catch</code>
+ block that handles the database exceptions. We do this by catching
+ the base ODB exception (<a href="#3.14">Section 3.14, "ODB
+ Exceptions"</a>) and printing the diagnostics.</p>
+
+ <p>Let's now compile (<a href="#2.3">Section 2.3, "Compiling and
+ Running"</a>) and then run our first ODB application:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+./driver --user odb_test --database odb_test
+ </pre>
+
+ <p>Our first application doesn't print anything except for error
+ messages so we can't really tell whether it actually stored the
+ objects' state in the database. While we will make our application
+ more entertaining shortly, for now we can use the <code>mysql</code>
+ client to examine the database content. It will also give us a feel
+ for how the objects are stored:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test
+
+Welcome to the MySQL monitor.
+
+mysql> select * from person;
+
++----+-------+------+-----+
+| id | first | last | age |
++----+-------+------+-----+
+| 1 | John | Doe | 33 |
+| 2 | Jane | Doe | 32 |
+| 3 | Joe | Dirt | 30 |
++----+-------+------+-----+
+3 rows in set (0.00 sec)
+
+mysql> quit
+ </pre>
+
+ <p>Another way to get more insight into what's going on under the hood,
+ is to trace the SQL statements executed by ODB as a result of
+ each database operation. Here is how we can enable tracing just for
+ the duration of our transaction:</p>
+
+ <pre class="cxx">
+ // Create a few persistent person objects.
+ //
+ {
+ ...
+
+ transaction t (db->begin ());
+
+ t.tracer (stderr_tracer);
+
+ // Make objects persistent and save their ids for later use.
+ //
+ john_id = db->persist (john);
+ jane_id = db->persist (jane);
+ joe_id = db->persist (joe);
+
+ t.commit ();
+ }
+ </pre>
+
+ <p>With this modification our application now produces the following
+ output:</p>
+
+ <pre class="terminal">
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+ </pre>
+
+ <p>Note that we see question marks instead of the actual values
+ because ODB uses prepared statements and sends the data to the
+ database in binary form. For more information on tracing, refer
+ to <a href="#3.13">Section 3.13, "Tracing SQL Statement Execution"</a>.
+ In the next section we will see how to access persistent objects
+ from our application.</p>
+
+ <h2><a name="2.5">2.5 Querying the Database for Objects</a></h2>
+
+ <p>So far our application doesn't resemble a typical "Hello World"
+ example. It doesn't print anything except for error messages.
+ Let's change that and teach our application to say hello to
+ people from our database. To make it a bit more interesting,
+ let's say hello only to people over 30:</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+...
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ ...
+
+ // Create a few persistent person objects.
+ //
+ {
+ ...
+ }
+
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ // Say hello to those over 30.
+ //
+ {
+ transaction t (db->begin ());
+
+ result r (db->query&lt;person> (query::age > 30));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; "!" &lt;&lt; endl;
+ }
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; e.what () &lt;&lt; endl;
+ return 1;
+ }
+}
+ </pre>
+
+ <p>The first half of our application is the same as before and is
+ replaced with "..." in the above listing for brevity. Again, let's
+ examine the rest of it piece by piece.</p>
+
+ <p>The two <code>typedef</code>s create convenient aliases for two
+ template instantiations that will be used a lot in our application.
+ The first is the query type for the <code>person</code> objects
+ and the second is the result type for that query.</p>
+
+ <p>Then we begin a new transaction and call the <code>query()</code>
+ database function. We pass a query expression
+ (<code>query::age > 30</code>) which limits the returned objects
+ only to those with the age greater than 30. We also save the result
+ of the query in a local variable.</p>
+
+ <p>The next few lines perform a standard for-loop iteration
+ over the result sequence printing hello for every returned person.
+ Then we commit the transaction and that's it. Let's see what
+ this application will print:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+./driver --user odb_test --database odb_test
+
+Hello, John!
+Hello, Jane!
+ </pre>
+
+
+ <p>That looks about right, but how do we know that the query actually
+ used the database instead of just using some in-memory artifacts of
+ the earlier <code>persist()</code> calls? One way to test this
+ would be to comment out the first transaction in our application
+ and re-run it without re-creating the database schema. This way the
+ objects that were persisted during the previous run will be returned.
+ Alternatively, we can just re-run the same application without
+ re-creating the schema and notice that we now show duplicate
+ objects:</p>
+
+ <pre class="terminal">
+./driver --user odb_test --database odb_test
+
+Hello, John!
+Hello, Jane!
+Hello, John!
+Hello, Jane!
+ </pre>
+
+ <p>What happens here is that the previous run of our application
+ persisted a set of <code>person</code> objects and when we re-run
+ the application, we persist another set with the same names but
+ with different ids. When we later run the query, matches from
+ both sets are returned. We can change the line where we print
+ the "Hello" string as follows to illustrate this point:</p>
+
+ <pre class="cxx">
+cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; " (" &lt;&lt; i->id () &lt;&lt; ")!" &lt;&lt; endl;
+ </pre>
+
+ <p>If we now re-run this modified program, again without re-creating
+ the database schema, we will get the following output:</p>
+
+ <pre class="terminal">
+./driver --user odb_test --database odb_test
+
+Hello, John (1)!
+Hello, Jane (2)!
+Hello, John (4)!
+Hello, Jane (5)!
+Hello, John (7)!
+Hello, Jane (8)!
+ </pre>
+
+ <p>The identifiers 3, 6, and 9 that are missing from the above list belong
+ to the "Joe Dirt" objects which are not selected by this query.</p>
+
+ <h2><a name="2.6">2.6 Updating Persistent Objects</a></h2>
+
+ <p>While making objects persistent and then selecting some of them using
+ queries are two useful operations, most applications will also need
+ to change the object's state and then make these changes persistent.
+ Let's illustrate this by updating Joe's age who just had a birthday:</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+...
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ ...
+
+ unsigned long john_id, jane_id, joe_id;
+
+ // Create a few persistent person objects.
+ //
+ {
+ ...
+
+ // Save object ids for later use.
+ //
+ john_id = john.id ();
+ jane_id = jane.id ();
+ joe_id = joe.id ();
+ }
+
+ // Joe Dirt just had a birthday, so update his age.
+ //
+ {
+ transaction t (db->begin ());
+
+ auto_ptr&lt;person> joe (db->load&lt;person> (joe_id));
+ joe->age (joe->age () + 1);
+ db->update (*joe);
+
+ t.commit ();
+ }
+
+ // Say hello to those over 30.
+ //
+ {
+ ...
+ }
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; e.what () &lt;&lt; endl;
+ return 1;
+ }
+}
+ </pre>
+
+ <p>The beginning and the end of the new transaction are the same as
+ the previous two. Once within a transaction, we call the
+ <code>load()</code> database function to instantiate a
+ <code>person</code> object with Joe's persistent state. We
+ pass Joe's object identifier that we stored earlier when we
+ made this object persistent. While here we use
+ <code>std::auto_ptr</code> to manage the returned object, we
+ could have also used another smart pointer, for example
+ <code>std::unique_ptr</code> from C++11 or <code>shared_ptr</code>
+ from TR1, C++11, or Boost. For more information
+ on the object lifetime management and the smart pointers that we
+ can use for that, see <a href="#3.3">Section 3.3, "Object
+ and View Pointers"</a>.</p>
+
+ <p>With the instantiated object in hand we increment the age
+ and call the <code>update()</code> function to update
+ the object's state in the database. Once the transaction is
+ committed, the changes are made permanent.</p>
+
+ <p>If we now run this application, we will see Joe in the output
+ since he is now over 30:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+./driver --user odb_test --database odb_test
+
+Hello, John!
+Hello, Jane!
+Hello, Joe!
+ </pre>
+
+ <p>What if we didn't have an identifier for Joe? Maybe this object
+ was made persistent in another run of our application or by another
+ application altogether. Provided that we only have one Joe Dirt
+ in the database, we can use the query facility to come up with
+ an alternative implementation of the above transaction:</p>
+
+ <pre class="cxx">
+ // Joe Dirt just had a birthday, so update his age. An
+ // alternative implementation without using the object id.
+ //
+ {
+ transaction t (db->begin ());
+
+ // Here we know that there can be only one Joe Dirt in our
+ // database so we use the query_one() shortcut instead of
+ // manually iterating over the result returned by query().
+ //
+ auto_ptr&lt;person> joe (
+ db->query_one&lt;person> (query::first == "Joe" &amp;&amp;
+ query::last == "Dirt"));
+
+ if (joe.get () != 0)
+ {
+ joe->age (joe->age () + 1);
+ db->update (*joe);
+ }
+
+ t.commit ();
+ }
+ </pre>
+
+ <h2><a name="2.7">2.7 Defining and Using Views</a></h2>
+
+ <p>Suppose that we need to gather some basic statistics about the people
+ stored in our database. Things like the total head count, as well as
+ the minimum and maximum ages. One way to do it would be to query
+ the database for all the <code>person</code> objects and then
+ calculate this information as we iterate over the query result.
+ While this approach may work fine for our database with just three
+ people in it, it would be very inefficient if we had a large
+ number of objects.</p>
+
+ <p>While it may not be conceptually pure from the object-oriented
+ programming point of view, a relational database can perform
+ some computations much faster and much more economically than
+ if we performed the same operations ourselves in the application's
+ process.</p>
+
+ <p>To support such cases ODB provides the notion of views. An ODB view
+ is a C++ <code>class</code> that embodies a light-weight, read-only
+ projection of one or more persistent objects or database tables or
+ the result of a native SQL query execution or stored procedure
+ call.</p>
+
+ <p>Some of the common applications of views include loading a subset of
+ data members from objects or columns database tables, executing and
+ handling results of arbitrary SQL queries, including aggregate
+ queries, as well as joining multiple objects and/or database
+ tables using object relationships or custom join conditions.</p>
+
+ <p>While you can find a much more detailed description of views in
+ <a href="#10">Chapter 10, "Views"</a>, here is how we can define
+ the <code>person_stat</code> view that returns the basic statistics
+ about the <code>person</code> objects:</p>
+
+ <pre class="cxx">
+#pragma db view object(person)
+struct person_stat
+{
+ #pragma db column("count(" + person::id_ + ")")
+ std::size_t count;
+
+ #pragma db column("min(" + person::age_ + ")")
+ unsigned short min_age;
+
+ #pragma db column("max(" + person::age_ + ")")
+ unsigned short max_age;
+};
+ </pre>
+
+ <p>Normally, to get the result of a view we use the same
+ <code>query()</code> function as when querying the database for
+ an object. Here, however, we are executing an aggregate query
+ which always returns exactly one element. Therefore, instead
+ of getting the result instance and then iterating over it, we
+ can use the shortcut <code>query_value()</code> function. Here is
+ how we can load and print our statistics using the view we have
+ just created:</p>
+
+ <pre class="cxx">
+ // Print some statistics about all the people in our database.
+ //
+ {
+ transaction t (db->begin ());
+
+ // The result of this query always has exactly one element.
+ //
+ person_stat ps (db->query_value&lt;person_stat> ());
+
+ cout &lt;&lt; "count : " &lt;&lt; ps.count &lt;&lt; endl
+ &lt;&lt; "min age: " &lt;&lt; ps.min_age &lt;&lt; endl
+ &lt;&lt; "max age: " &lt;&lt; ps.max_age &lt;&lt; endl;
+
+ t.commit ();
+ }
+ </pre>
+
+ <p>If we now add the <code>person_stat</code> view to the
+ <code>person.hxx</code> header, the above transaction
+ to <code>driver.cxx</code>, as well as re-compile and
+ re-run our example, then we will see the following
+ additional lines in the output:</p>
+
+ <pre class="term">
+count : 3
+min age: 31
+max age: 33
+ </pre>
+
+ <h2><a name="2.8">2.8 Deleting Persistent Objects</a></h2>
+
+ <p>The last operation that we will discuss in this chapter is deleting
+ the persistent object from the database. The following code
+ fragment shows how we can delete an object given its identifier:</p>
+
+ <pre class="cxx">
+ // John Doe is no longer in our database.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase&lt;person> (john_id);
+ t.commit ();
+ }
+ </pre>
+
+ <p>To delete John from the database we start a transaction, call
+ the <code>erase()</code> database function with John's object
+ id, and commit the transaction. After the transaction is committed,
+ the erased object is no longer persistent.</p>
+
+ <p>If we don't have an object id handy, we can use queries to find
+ and delete the object:</p>
+
+ <pre class="cxx">
+ // John Doe is no longer in our database. An alternative
+ // implementation without using the object id.
+ //
+ {
+ transaction t (db->begin ());
+
+ // Here we know that there can be only one John Doe in our
+ // database so we use the query_one() shortcut again.
+ //
+ auto_ptr&lt;person> john (
+ db->query_one&lt;person> (query::first == "John" &amp;&amp;
+ query::last == "Doe"));
+
+ if (john.get () != 0)
+ db->erase (*john);
+
+ t.commit ();
+ }
+ </pre>
+
+ <h2><a name="2.9">2.9 Changing Persistent Classes</a></h2>
+
+ <p>When the definition of a transient C++ class is changed, for
+ example by adding or deleting a data member, we don't have to
+ worry about any existing instances of this class not matching
+ the new definition. After all, to make the class changes
+ effective we have to restart the application and none of the
+ transient instances will survive this.</p>
+
+ <p>Things are not as simple for persistent classes. Because they
+ are stored in the database and therefore survive application
+ restarts, we have a new problem: what happens to the state of
+ existing objects (which correspond to the old definition) once
+ we change our persistent class?</p>
+
+ <p>The problem of working with old objects, called <em>database
+ schema evolution</em>, is a complex issue and ODB provides
+ comprehensive support for handling it. While this support
+ is covered in detail in <a href="#13">Chapter 13,
+ "Database Schema Evolution"</a>, let us consider a simple
+ example that should give us a sense of the functionality
+ provided by ODB in this area.</p>
+
+ <p>Suppose that after using our <code>person</code> persistent
+ class for some time and creating a number of databases
+ containing its instances, we realized that for some people
+ we also need to store their middle name. If we go ahead and
+ just add the new data member, everything will work fine
+ with new databases. Existing databases, however, have a
+ table that does not correspond to the new class definition.
+ Specifically, the generated database support code now
+ expects there to be a column to store the middle name.
+ But such a column was never created in the old databases.</p>
+
+ <p>ODB can automatically generate SQL statements that will
+ migrate old databases to match the new class definitions.
+ But first, we need to enable schema evolution support by
+ defining a version for our object model:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#pragma db model version(1, 1)
+
+class person
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>The first number in the <code>version</code> pragma is the
+ base model version. This is the lowest version we will be
+ able to migrate from. The second number is the current model
+ version. Since we haven't made any changes yet to our
+ persistent class, both of these values are <code>1</code>.</p>
+
+ <p>Next we need to re-compile our <code>person.hxx</code> header
+ file with the ODB compiler, just as we did before:</p>
+
+ <pre class="terminal">
+odb -d mysql --generate-query --generate-schema person.hxx
+ </pre>
+
+ <p>If we now look at the list of files produced by the ODB compiler,
+ we will notice a new file: <code>person.xml</code>. This
+ is a changelog file where the ODB compiler keeps track of the
+ database changes corresponding to our class changes. Note that
+ this file is automatically maintained by the ODB compiler and
+ all we have to do is keep it around between re-compilations.</p>
+
+ <p>Now we are ready to add the middle name to our <code>person</code>
+ class. We also give it a default value (empty string) which
+ is what will be assigned to existing objects in old databases.
+ Notice that we have also incremented the current version:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#pragma db model version(1, 2)
+
+class person
+{
+ ...
+
+ std::string first_;
+
+ #pragma db default("")
+ std::string middle_;
+
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>If we now recompile the <code>person.hxx</code> header again, we will
+ see two extra generated files: <code>person-002-pre.sql</code>
+ and <code>person-002-post.sql</code>. These two files contain
+ schema migration statements from version <code>1</code> to
+ version <code>2</code>. Similar to schema creation, schema
+ migration statements can also be embedded into the generated
+ C++ code.</p>
+
+ <p><code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
+ are the pre and post schema migration files. To migrate
+ one of our old databases, we first execute the pre migration
+ file:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person-002-pre.sql
+ </pre>
+
+ <p>Between the pre and post schema migrations we can run data
+ migration code, if required. At this stage, we can both
+ access the old and store the new data. In our case we don't
+ need any data migration code since we assigned the default
+ value to the middle name for all the existing objects.</p>
+
+ <p>To finish the migration process we execute the post migration
+ statements:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person-002-post.sql
+ </pre>
+
+ <h2><a name="2.10">2.10 Working with Multiple Databases</a></h2>
+
+ <p>Accessing multiple databases (that is, data stores) is simply a
+ matter of creating multiple <code>odb::&lt;db>::database</code>
+ instances representing each database. For example:</p>
+
+ <pre class="cxx">
+odb::mysql::database db1 ("john", "secret", "test_db1");
+odb::mysql::database db2 ("john", "secret", "test_db2");
+ </pre>
+
+ <p>Some database systems also allow attaching multiple databases to
+ the same instance. 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.</p>
+
+ <p>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 <a href="#16">Chapter 16, "Multi-Database Support"</a>, 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).</p>
+
+ <p>The first step in adding multi-database support is to re-compile
+ our <code>person.hxx</code> header to generate database support
+ code for additional database systems:</p>
+
+ <pre class="terminal">
+odb --multi-database dynamic -d common -d mysql -d pgsql \
+--generate-query --generate-schema person.hxx
+ </pre>
+
+ <p>The <code>--multi-database</code> ODB compiler option turns on
+ multi-database support. For now it is not important what the
+ <code>dynamic</code> value that we passed to this option means, but
+ if you are curious, see <a href="#16">Chapter 16</a>. The result of this
+ command are three sets of generated files: <code>person-odb.?xx</code>
+ (common interface; corresponds to the <code>common</code> database),
+ <code>person-odb-mysql.?xx</code> (MySQL support code), and
+ <code>person-odb-pgsql.?xx</code> (PostgreSQL support code). There
+ are also two schema files: <code>person-mysql.sql</code> and
+ <code>person-pgsql.sql</code>.</p>
+
+ <p>The only part that we need to change in <code>driver.cxx</code>
+ is how we create the database instance. Specifically, this line:</p>
+
+ <pre class="cxx">
+auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
+ </pre>
+
+ <p>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 <code>odb::&lt;db>::database</code> constructor as
+ before. Let's put all this logic into a separate function which we
+ will call <code>create_database()</code>. Here is what the beginning
+ of our modified <code>driver.cxx</code> will look like (the remainder
+ is unchanged):</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+#include &lt;string>
+#include &lt;memory> // std::auto_ptr
+#include &lt;iostream>
+
+#include &lt;odb/database.hxx>
+#include &lt;odb/transaction.hxx>
+
+#include &lt;odb/mysql/database.hxx>
+#include &lt;odb/pgsql/database.hxx>
+
+#include "person.hxx"
+#include "person-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+auto_ptr&lt;database>
+create_database (int argc, char* argv[])
+{
+ auto_ptr&lt;database> r;
+
+ if (argc &lt; 2)
+ {
+ cerr &lt;&lt; "error: database system name expected" &lt;&lt; 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 &lt;&lt; "error: unknown database system " &lt;&lt; db &lt;&lt; endl;
+
+ return r;
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr&lt;database> db (create_database (argc, argv));
+
+ if (db.get () == 0)
+ return 1; // Diagnostics has already been issued.
+
+ ...
+ </pre>
+
+ <p>And that's it. The only thing left is to build and run our
+ example:</p>
+
+ <pre class="terminal">
+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
+ </pre>
+
+ <p>Here is how we can access a MySQL database:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person-mysql.sql
+./driver mysql --user odb_test --database odb_test
+ </pre>
+
+ <p>Or a PostgreSQL database:</p>
+
+ <pre class="terminal">
+psql --user=odb_test --dbname=odb_test -f person-pgsql.sql
+./driver pgsql --user odb_test --database odb_test
+ </pre>
+
+ <h2><a name="2.11">2.11 Summary</a></h2>
+
+ <p>This chapter presented a very simple application which, nevertheless,
+ exercised all of the core database functions: <code>persist()</code>,
+ <code>query()</code>, <code>load()</code>, <code>update()</code>,
+ and <code>erase()</code>. We also saw that writing an application
+ that uses ODB involves the following steps:</p>
+
+ <ol>
+ <li>Declare persistent classes in header files.</li>
+ <li>Compile these headers to generate database support code.</li>
+ <li>Link the application with the generated code and two ODB runtime
+ libraries.</li>
+ </ol>
+
+ <p>Do not be concerned if, at this point, much appears unclear. The intent
+ of this chapter is to give you only a general idea of how to persist C++
+ objects with ODB. We will cover all the details throughout the remainder
+ of this manual.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="3">3 Working with Persistent Objects</a></h1>
+
+ <p>The previous chapters gave us a high-level overview of ODB and
+ showed how to use it to store C++ objects in a database. In this
+ chapter we will examine the ODB object persistence model as
+ well as the core database APIs in greater detail. We will
+ start with basic concepts and terminology in <a href="#3.1">Section
+ 3.1</a> and <a href="#3.3">Section 3.3</a> and continue with the
+ discussion of the <code>odb::database</code> class in
+ <a href="#3.4">Section 3.4</a>, transactions in
+ <a href="#3.5">Section 3.5</a>, and connections in
+ <a href="#3.6">Section 3.6</a>. The remainder of this chapter
+ deals with the core database operations and concludes with
+ the discussion of ODB exceptions.</p>
+
+ <p>In this chapter we will continue to use and expand the
+ <code>person</code> persistent class that we have developed in the
+ previous chapter.</p>
+
+ <h2><a name="3.1">3.1 Concepts and Terminology</a></h2>
+
+ <p>The term <em>database</em> can refer to three distinct things:
+ a general notion of a place where an application stores its data,
+ a software implementation for managing this data (for example
+ MySQL), and, finally, some database software implementations
+ may manage several data stores which are usually distinguished
+ by name. This name is also commonly referred to as a database.</p>
+
+ <p>In this manual, when we use the word <em>database</em>, we
+ refer to the first meaning above, for example,
+ "The <code>update()</code> function saves the object's state to
+ the database." The term Database Management System (DBMS) is
+ often used to refer to the second meaning of the word database.
+ In this manual we will use the term <em>database system</em>
+ for short, for example, "Database system-independent
+ application code." Finally, to distinguish the third meaning
+ from the other two, we will use the term <em>database name</em>,
+ for example, "The second option specifies the database name
+ that the application should use to store its data."</p>
+
+ <p>In C++ there is only one notion of a type and an instance
+ of a type. For example, a fundamental type, such as <code>int</code>,
+ is, for the most part, treated the same as a user defined class
+ type. However, when it comes to persistence, we have to place
+ certain restrictions and requirements on certain C++ types that
+ can be stored in the database. As a result, we divide persistent
+ C++ types into two groups: <em>object types</em> and <em>value
+ types</em>. An instance of an object type is called an <em>object</em>
+ and an instance of a value type &mdash; a <em>value</em>.</p>
+
+ <p>An object is an independent entity. It can be stored, updated,
+ and deleted in the database independent of other objects.
+ Normally, an object has an identifier, called <em>object id</em>,
+ that is unique among all instances of an object type within a
+ database. In contrast, a value can only be stored in the database
+ as part of an object and doesn't have its own unique identifier.</p>
+
+ <p>An object consists of data members which are either values
+ (<a href="#7">Chapter 7, "Value Types"</a>), pointers
+ to other objects (<a href="#6">Chapter 6, "Relationships"</a>), or
+ containers of values or pointers to other objects (<a href="#5">Chapter
+ 5, "Containers")</a>. Pointers to other objects and containers can
+ be viewed as special kinds of values since they also can only
+ be stored in the database as part of an object. Static data members
+ are not stored in the database.</p>
+
+ <p>An object type is a C++ class. Because of this one-to-one
+ relationship, we will use terms <em>object type</em>
+ and <em>object class</em> interchangeably. In contrast,
+ a value type can be a fundamental C++ type, such as
+ <code>int</code> or a class type, such as <code>std::string</code>.
+ If a value consists of other values, then it is called a
+ <em>composite value</em> and its type &mdash; a
+ <em>composite value type</em> (<a href="#7.2">Section 7.2,
+ "Composite Value Types"</a>). Otherwise, the value is
+ called <em>simple value</em> and its type &mdash; a
+ <em>simple value type</em> (<a href="#7.1">Section 7.1,
+ "Simple Value Types"</a>). Note that the distinction between
+ simple and composite values is conceptual rather than
+ representational. For example, <code>std::string</code>
+ is a simple value type because conceptually string is a
+ single value even though the representation of the string
+ class may contain several data members each of which could be
+ considered a value. In fact, the same value type can be
+ viewed (and mapped) as both simple and composite by different
+ applications.</p>
+
+ <p>While not strictly necessary in a purely object-oriented application,
+ practical considerations often require us to only load a
+ subset of an object's data members or a combination of members
+ from several objects. We may also need to factor out some
+ computations to the relational database instead of performing
+ them in the application's process. To support such requirements
+ ODB distinguishes a third kind of C++ types, called <em>views</em>
+ (<a href="#10">Chapter 10, "Views"</a>). An ODB view is a C++
+ <code>class</code> that embodies a light-weight, read-only
+ projection of one or more persistent objects or database
+ tables or the result of a native SQL query execution.</p>
+
+ <p>Understanding how all these concepts map to the relational model
+ will hopefully make these distinctions clearer. In a relational
+ database an object type is mapped to a table and a value type is
+ mapped to one or more columns. A simple value type is mapped
+ to a single column while a composite value type is mapped to
+ several columns. An object is stored as a row in this
+ table and a value is stored as one or more cells in this row.
+ A simple value is stored in a single cell while a composite
+ value occupies several cells. A view is not a persistent
+ entity and it is not stored in the database. Rather, it is a
+ data structure that is used to capture a single row of an SQL
+ query result.</p>
+
+ <p>Going back to the distinction between simple and composite
+ values, consider a date type which has three integer
+ members: year, month, and day. In one application it can be
+ considered a composite value and each member will get its
+ own column in a relational database. In another application
+ it can be considered a simple value and stored in a single
+ column as a number of days from some predefined date.</p>
+
+ <p>Until now, we have been using the term <em>persistent class</em>
+ to refer to object classes. We will continue to do so even though
+ a value type can also be a class. The reason for this asymmetry
+ is the subordinate nature of value types when it comes to
+ database operations. Remember that values are never stored
+ directly but rather as part of an object that contains them.
+ As a result, when we say that we want to make a C++ class
+ persistent or persist an instance of a class in the database,
+ we invariably refer to an object class rather than a value
+ class.</p>
+
+ <p>Normally, you would use object types to model real-world entities,
+ things that have their own identity. For example, in the
+ previous chapter we created a <code>person</code> class to model
+ a person, which is a real-world entity. Name and age, which we
+ used as data members in our <code>person</code> class are clearly
+ values. It is hard to think of age 31 or name "Joe" as having their
+ own identities.</p>
+
+ <p>A good test to determine whether something is an object or
+ a value, is to consider if other objects might reference
+ it. A person is clearly an object because it can be referred
+ to by other objects such as a spouse, an employer, or a
+ bank. On the other hand, a person's age or name is not
+ something that other objects would normally refer to.</p>
+
+ <p>Also, when an object represents a real entity, it is easy to
+ choose a suitable object id. For example, for a
+ person there is an established notion of an identifier
+ (SSN, student id, passport number, etc). Another alternative
+ is to use a person's email address as an identifier.</p>
+
+ <p>Note, however, that these are only guidelines. There could
+ be good reasons to make something that would normally be
+ a value an object. Consider, for example, a database that
+ stores a vast number of people. Many of the <code>person</code>
+ objects in this database have the same names and surnames and
+ the overhead of storing them in every object may negatively
+ affect the performance. In this case, we could make the first name
+ and last name each an object and only store pointers to
+ these objects in the <code>person</code> class.</p>
+
+ <p>An instance of a persistent class can be in one of two states:
+ <em>transient</em> and <em>persistent</em>. A transient
+ instance only has a representation in the application's
+ memory and will cease to exist when the application terminates,
+ unless it is explicitly made persistent. In other words, a
+ transient instance of a persistent class behaves just like an
+ instance of any ordinary C++ class. A persistent instance
+ has a representation in both the application's memory and the
+ database. A persistent instance will remain even after the
+ application terminates unless and until it is explicitly
+ deleted from the database.</p>
+
+ <h2><a name="3.2">3.2 Declaring Persistent Objects and Values</a></h2>
+
+ <p>To make a C++ class a persistent object class we declare
+ it as such using the <code>db&nbsp;object</code> pragma, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>The other pragma that we often use is <code>db&nbsp;id</code>
+ which designates one of the data members as an object id, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+};
+ </pre>
+
+ <p>The object id can be of a simple or composite (<a href="#7.2.1">Section
+ 7.2.1, "Composite Object Ids"</a>) value type. This type should be
+ default-constructible, copy-constructible, and copy-assignable. It
+ is also possible to declare a persistent class without an object id,
+ however, such a class will have limited functionality
+ (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
+
+ <p>The above two pragmas are the minimum required to declare a
+ persistent class with an object id. Other pragmas can be used to
+ fine-tune the database-related properties of a class and its
+ members (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
+
+ <p>Normally, a persistent class should define the default constructor. The
+ generated database support code uses this constructor when
+ instantiating an object from the persistent state. If we add the
+ default constructor only for the database support code, then we
+ can make it private provided we also make the <code>odb::access</code>
+ class, defined in the <code>&lt;odb/core.hxx></code> header, a
+ friend of this object class. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+ person () {}
+};
+ </pre>
+
+ <p>It is also possible to have an object class without the default
+ constructor. However, in this case, the database operations will
+ only be able to load the persistent state into an existing instance
+ (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
+ <a href="#4.4">Section 4.4, "Query Result"</a>).</p>
+
+ <p>The ODB compiler also needs access to the non-transient
+ (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
+ data members of a persistent class. The ODB compiler can access
+ such data members directly if they are public. It can also do
+ so if they are private or protected and the <code>odb::access</code>
+ class is declared a friend of the object type. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+ person () {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string name_;
+};
+ </pre>
+
+ <p>If data members are not accessible directly, then the ODB
+ compiler will try to automatically find suitable accessor and
+ modifier functions. To accomplish this, the ODB compiler will
+ try to lookup common accessor and modifier names derived from
+ the data member name. Specifically, for the <code>name_</code>
+ data member in the above example, the ODB compiler will look
+ for accessor functions with names: <code>get_name()</code>,
+ <code>getName()</code>, <code>getname()</code>, and just
+ <code>name()</code> as well as for modifier functions with
+ names: <code>set_name()</code>, <code>setName()</code>,
+ <code>setname()</code>, and just <code>name()</code>. You can
+ also add support for custom name derivations with the
+ <code>--accessor-regex</code> and <code>--modifier-regex</code>
+ ODB compiler options. Refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a> for details on these options.
+ The following example illustrates automatic accessor and modifier
+ discovery:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ person () {}
+
+ ...
+
+ unsigned long id () const;
+ void id (unsigned long);
+
+ const std::string&amp; get_name () const;
+ std::string&amp; set_name ();
+
+private:
+ #pragma db id
+ unsigned long id_; // Uses id() for access.
+
+ std::string name_; // Uses get_name()/set_name() for access.
+};
+ </pre>
+
+ <p>Finally, if a data member is not directly accessible and the
+ ODB compiler was unable to discover suitable accessor and
+ modifier functions, then we can provide custom accessor
+ and modifier expressions using the <code>db&nbsp;get</code>
+ and <code>db&nbsp;set</code> pragmas. For more information
+ on custom accessor and modifier expressions refer to
+ <a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>.</p>
+
+ <p>Data members of a persistent class can also be split into
+ separately-loaded and/or separately-updated sections.
+ For more information on this functionality, refer to
+ <a href="#9">Chapter 9, "Sections"</a>.</p>
+
+ <p>You may be wondering whether we also have to declare value types
+ as persistent. We don't need to do anything special for simple value
+ types such as <code>int</code> or <code>std::string</code> since the
+ ODB compiler knows how to map them to suitable database types and
+ how to convert between the two. On the other hand, if a simple value
+ is unknown to the ODB compiler then we will need to provide the
+ mapping to the database type and, possibly, the code to
+ convert between the two. For more information on how to achieve
+ this refer to the <code>db&nbsp;type</code> pragma description
+ in <a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>.</p>
+
+ <p>Similar to object classes, composite value types have to be
+ explicitly declared as persistent using the <code>db&nbsp;value</code>
+ pragma, for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+ </pre>
+
+ <p>Note that a composite value cannot have a data member designated
+ as an object id since, as we have discussed earlier, values do
+ not have a notion of identity. A composite value type also doesn't
+ have to define the default constructor, unless it is used as an
+ element of a container. The ODB compiler uses the same mechanisms
+ to access data members in composite value types as in object types.
+ Composite value types are discussed in more detail in
+ <a href="#7.2">Section 7.2, "Composite Value Types"</a>.</p>
+
+ <h2><a name="3.3">3.3 Object and View Pointers</a></h2>
+
+ <p>As we have seen in the previous chapter, some database operations
+ create dynamically allocated instances of persistent classes and
+ return pointers to these instances. As we will see in later chapters,
+ pointers are also used to establish relationships between objects
+ (<a href="#6">Chapter 6, "Relationships"</a>) as well as to cache
+ persistent objects in a session (<a href="#11">Chapter 11,
+ "Session"</a>). While in most cases you won't need to deal with
+ pointers to views, it is possible to a obtain a dynamically allocated
+ instance of a view using the <code>result_iterator::load()</code>
+ function (<a href="#4.4">Section 4.4, "Query Results"</a>).</p>
+
+ <p>By default, all these mechanisms use raw pointers to return
+ objects and views as well as to pass and cache objects. This
+ is normally sufficient for applications
+ that have simple object lifetime requirements and do not use sessions
+ or object relationships. In particular, a dynamically allocated object
+ or view that is returned as a raw pointer from a database operation
+ can be assigned to a smart pointer of our choice, for example
+ <code>std::auto_ptr</code>, <code>std::unique_ptr</code> from C++11, or
+ <code>shared_ptr</code> from TR1, C++11, or Boost.</p>
+
+ <p>However, to avoid any possibility of a mistake, such as forgetting
+ to use a smart pointer for a returned object or view, as well as to
+ simplify the use of more advanced ODB functionality, such as sessions
+ and bidirectional object relationships, it is recommended that you use
+ smart pointers with the sharing semantics as object pointers.
+ The <code>shared_ptr</code> smart pointer from TR1, C++11, or Boost
+ is a good default choice. However, if sharing is not required and
+ sessions are not used, then <code>std::unique_ptr</code> or
+ <code>std::auto_ptr</code> can be used just as well.</p>
+
+ <p>ODB provides several mechanisms for changing the object or view pointer
+ type. To specify the pointer type on the per object or per view basis
+ we can use the <code>db&nbsp;pointer</code> pragma, for example:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::tr1::shared_ptr)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>We can also specify the default pointer for a group of objects or
+ views at the namespace level:</p>
+
+ <pre class="cxx">
+#pragma db namespace pointer(std::tr1::shared_ptr)
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Finally, we can use the <code>--default-pointer</code> option to specify
+ the default pointer for the whole file. Refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a> for details on this option's argument.
+ The typical usage is shown below:</p>
+
+ <pre class="terminal">
+--default-pointer std::tr1::shared_ptr
+ </pre>
+
+ <p>An alternative to this method with the same effect is to specify the
+ default pointer for the global namespace:</p>
+
+ <pre class="terminal">
+#pragma db namespace() pointer(std::tr1::shared_ptr)
+ </pre>
+
+ <p>Note that we can always override the default pointer specified
+ at the namespace level or with the command line option using
+ the <code>db&nbsp;pointer</code> object or view pragma. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::shared_ptr)
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object pointer(std::unique_ptr)
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Refer to <a href="#14.1.2">Section 14.1.2, "<code>pointer</code>
+ (object)"</a>, <a href="#14.2.4">Section 14.2.4, "<code>pointer</code>
+ (view)"</a>, and <a href="#14.5.1">Section 14.5.1, "<code>pointer</code>
+ (namespace)"</a> for more information on these mechanisms.</p>
+
+ <p>Built-in support that is provided by the ODB runtime library allows us
+ to use <code>shared_ptr</code> (TR1 or C++11),
+ <code>std::unique_ptr</code> (C++11), or <code>std::auto_ptr</code> as
+ pointer types. Plus, ODB profile libraries, that are available for
+ commonly used frameworks and libraries (such as Boost and Qt),
+ provide support for smart pointers found in these frameworks and
+ libraries (<a href="#III">Part III, "Profiles"</a>). It is also
+ easy to add support for our own smart pointers, as described in
+ <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>.</p>
+
+ <h2><a name="3.4">3.4 Database</a></h2>
+
+ <p>Before an application can make use of persistence services
+ offered by ODB, it has to create a database class instance. A
+ database instance is the representation of the place where
+ the application stores its persistent objects. We create
+ a database instance by instantiating one of the database
+ system-specific classes. For example, <code>odb::mysql::database</code>
+ would be such a class for the MySQL database system. We will
+ also normally pass a database name as an argument to the
+ class' constructor. The following code fragment
+ shows how we can create a database instance for the MySQL
+ database system:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+#include &lt;odb/mysql/database.hxx>
+
+auto_ptr&lt;odb::database> db (
+ new odb::mysql::database (
+ "test_user" // database login name
+ "test_password" // database password
+ "test_database" // database name
+ ));
+ </pre>
+
+ <p>The <code>odb::database</code> class is a common interface for
+ all the database system-specific classes provided by ODB. You
+ would normally work with the database
+ instance via this interface unless there is a specific
+ functionality that your application depends on and which is
+ only exposed by a particular system's <code>database</code>
+ class. You will need to include the <code>&lt;odb/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The <code>odb::database</code> interface defines functions for
+ starting transactions and manipulating persistent objects.
+ These are discussed in detail in the remainder of this chapter
+ as well as the next chapter which is dedicated to the topic of
+ querying the database for persistent objects. For details on the
+ system-specific <code>database</code> classes, refer to
+ <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>Before we can persist our objects, the corresponding database schema has
+ to be created in the database. The schema contains table definitions and
+ other relational database artifacts that are used to store the state of
+ persistent objects in the database.</p>
+
+ <p>There are several ways to create the database schema. The easiest is to
+ instruct the ODB compiler to generate the corresponding schema from the
+ persistent classes (<code>--generate-schema</code> option). The ODB
+ compiler can generate the schema as a standalone SQL file,
+ embedded into the generated C++ code, or as a separate C++ source file
+ (<code>--schema-format</code> option). If we are using the SQL file
+ to create the database schema, then this file should be executed,
+ normally only once, before the application is started.</p>
+
+ <p>Alternatively, if the schema is embedded directly into the generated
+ code or produced as a separate C++ source file, then we can use the
+ <code>odb::schema_catalog</code> class to create it in the database
+ from within our application, for example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/schema-catalog.hxx>
+
+odb::transaction t (db->begin ());
+odb::schema_catalog::create_schema (*db);
+t.commit ();
+ </pre>
+
+ <p>Refer to the next section for information on the
+ <code>odb::transaction</code> class. The complete version of the above
+ code fragment is available in the <code>schema/embedded</code> example in
+ the <code>odb-examples</code> package.</p>
+
+ <p>The <code>odb::schema_catalog</code> class has the following interface.
+ You will need to include the <code>&lt;odb/schema-catalog.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class schema_catalog
+ {
+ public:
+ static void
+ create_schema (database&amp;,
+ const std::string&amp; name = "",
+ bool drop = true);
+
+ static void
+ drop_schema (database&amp;, const std::string&amp; name = "");
+
+ static bool
+ exists (database_id, const std::string&amp; name = "");
+
+ static bool
+ exists (const database&amp;, const std::string&amp; name = "")
+ };
+}
+ </pre>
+
+ <p>The first argument to the <code>create_schema()</code> function
+ is the database instance that we would like to create the schema in.
+ The second argument is the schema name. By default, the ODB
+ compiler generates all embedded schemas with the default schema
+ name (empty string). However, if your application needs to
+ have several separate schemas, you can use the
+ <code>--schema-name</code> ODB compiler option to assign
+ custom schema names and then use these names as a second argument
+ to <code>create_schema()</code>. By default, <code>create_schema()</code>
+ will also delete all the database objects (tables, indexes, etc.) if
+ they exist prior to creating the new ones. You can change this
+ behavior by passing <code>false</code> as the third argument. The
+ <code>drop_schema()</code> function allows you to delete all the
+ database objects without creating the new ones.</p>
+
+ <p>If the schema is not found, the <code>create_schema()</code> and
+ <code>drop_schema()</code> functions throw the
+ <code>odb::unknown_schema</code> exception. You can use the
+ <code>exists()</code> function to check whether a schema for the
+ specified database and with the specified name exists in the
+ catalog. Note also that the <code>create_schema()</code> and
+ <code>drop_schema()</code> functions should be called within a
+ transaction.</p>
+
+ <p>ODB also provides support for database schema evolution. Similar
+ to schema creation, schema migration statements can be generated
+ either as standalone SQL files or embedded into the generated C++
+ code. For more information on schema evolution support, refer to
+ <a href="#13">Chapter 13, "Database Schema Evolution"</a>.</p>
+
+ <p>Finally, we can also use a custom database schema with ODB. This approach
+ can work similarly to the standalone SQL file described above except that
+ the database schema is hand-written or produced by another program. Or we
+ could execute custom SQL statements that create the schema directly from
+ our application. To map persistent classes to custom database schemas, ODB
+ provides a wide range of mapping customization pragmas, such
+ as <code>db&nbsp;table</code>, <code>db&nbsp;column</code>,
+ and <code>db&nbsp;type</code> (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>). For sample code that shows how to perform such mapping
+ for various C++ constructs, refer to the <code>schema/custom</code>
+ example in the <code>odb-examples</code> package.</p>
+
+ <h2><a name="3.5">3.5 Transactions</a></h2>
+
+ <p>A transaction is an atomic, consistent, isolated and durable
+ (ACID) unit of work. Database operations can only be
+ performed within a transaction and each thread of execution
+ in an application can have only one active transaction at a
+ time.</p>
+
+ <p>By atomicity we mean that when it comes to making changes to
+ the database state within a transaction,
+ either all the changes are applied or none at all. Consider,
+ for example, a transaction that transfers funds between two
+ objects representing bank accounts. If the debit function
+ on the first object succeeds but the credit function on
+ the second fails, the transaction is rolled back and the
+ database state of the first object remains unchanged.</p>
+
+ <p>By consistency we mean that a transaction must take all the
+ objects stored in the database from one consistent state
+ to another. For example, if a bank account object must
+ reference a person object as its owner and we forget to
+ set this reference before making the object persistent,
+ the transaction will be rolled back and the database
+ will remain unchanged.</p>
+
+ <p>By isolation we mean that the changes made to the database
+ state during a transaction are only visible inside this
+ transaction until and unless it is committed. Using the
+ above example with the bank transfer, the results of the
+ debit operation performed on the first object is not
+ visible to other transactions until the credit operation
+ is successfully completed and the transaction is committed.</p>
+
+ <p>By durability we mean that once the transaction is committed,
+ the changes that it made to the database state are permanent
+ and will survive failures such as an application crash. From
+ now on the only way to alter this state is to execute and commit
+ another transaction.</p>
+
+ <p>A transaction is started by calling either the
+ <code>database::begin()</code> or <code>connection::begin()</code>
+ function. The returned transaction handle is stored in
+ an instance of the <code>odb::transaction</code> class.
+ You will need to include the <code>&lt;odb/transaction.hxx></code>
+ header file to make this class available in your application.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/transaction.hxx>
+
+transaction t (db.begin ())
+
+// Perform database operations.
+
+t.commit ();
+ </pre>
+
+ <p>The <code>odb::transaction</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class transaction
+ {
+ public:
+ typedef odb::database database_type;
+ typedef odb::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ void
+ reset (transaction_impl*, bool make_current = true);
+
+ void
+ commit ();
+
+ void
+ rollback ();
+
+ database_type&amp;
+ database ();
+
+ connection_type&amp;
+ connection ();
+
+ bool
+ finilized () const;
+
+ public:
+ static bool
+ has_current ();
+
+ static transaction&amp;
+ current ();
+
+ static void
+ current (transaction&amp;);
+
+ static bool
+ reset_current ();
+
+ // Callback API.
+ //
+ public:
+ ...
+ };
+}
+ </pre>
+
+ <p>The <code>commit()</code> function commits a transaction and
+ <code>rollback()</code> rolls it back. Unless the transaction
+ has been <em>finalized</em>, that is, explicitly committed or rolled
+ back, the destructor of the <code>transaction</code> class will
+ automatically roll it back when the transaction instance goes
+ out of scope. If we try to commit or roll back a finalized
+ transaction, the <code>odb::transaction_already_finalized</code>
+ exception is thrown.</p>
+
+ <p>The <code>database()</code> accessor returns the database this
+ transaction is working on. Similarly, the <code>connection()</code>
+ accessor returns the database connection this transaction is on
+ (<a href="#3.6">Section 3.6, "Connections"</a>).</p>
+
+ <p>The static <code>current()</code> accessor returns the
+ currently active transaction for this thread. If there is no active
+ transaction, this function throws the <code>odb::not_in_transaction</code>
+ exception. We can check whether there is a transaction in effect in
+ this thread using the <code>has_current()</code> static function.</p>
+
+ <p>The <code>make_current</code> argument in the <code>transaction</code>
+ constructor as well as the static <code>current()</code> modifier and
+ <code>reset_current()</code> function give us additional
+ control over the nomination of the currently active transaction.
+ If we pass <code>false</code> as the <code>make_current</code>
+ argument, then the newly created transaction will not
+ automatically be made the active transaction for this
+ thread. Later, we can use the static <code>current()</code> modifier
+ to set this transaction as the active transaction.
+ The <code>reset_current()</code> static function clears the
+ currently active transaction. Together, these mechanisms
+ allow for more advanced use cases, such as multiplexing
+ two or more transactions on the same thread. For example:</p>
+
+ <pre class="cxx">
+transaction t1 (db1.begin ()); // Active transaction.
+transaction t2 (db2.begin (), false); // Not active.
+
+// Perform database operations on db1.
+
+transaction::current (t2); // Deactivate t1, activate t2.
+
+// Perform database operations on db2.
+
+transaction::current (t1); // Switch back to t1.
+
+// Perform some more database operations on db1.
+
+t1.commit ();
+
+transaction::current (t2); // Switch to t2.
+
+// Perform some more database operations on db2.
+
+t2.commit ();
+ </pre>
+
+ <p>The <code>reset()</code> modifier allows us to reuse the same
+ <code>transaction</code> instance to complete several database
+ transactions. Similar to the destructor, <code>reset()</code>
+ will roll the current transaction back if it hasn't been finalized.
+ The default <code>transaction</code> constructor creates a finalized
+ transaction which can later be initialized using <code>reset()</code>.
+ The <code>finilized()</code> accessor can be used to check whether the
+ transaction has been finalized. Here is how we can use this functionality
+ to commit the current transaction and start a new one every time a
+ certain number of database operations has been performed:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+for (size_t i (0); i &lt; n; ++i)
+{
+ // Perform a database operation, such as persist an object.
+
+ // Commit the current transaction and start a new one after
+ // every 100 operations.
+ //
+ if (i % 100 == 0)
+ {
+ t.commit ();
+ t.reset (db.begin ());
+ }
+}
+
+t.commit ();
+ </pre>
+
+ <p>For more information on the transaction callback support, refer
+ to <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>.</p>
+
+ <p>Note that in the above discussion of atomicity, consistency,
+ isolation, and durability, all of those guarantees only apply
+ to the object's state in the database as opposed to the object's
+ state in the application's memory. It is possible to roll
+ a transaction back but still have changes from this
+ transaction in the application's memory. An easy way to
+ avoid this potential inconsistency is to instantiate
+ persistent objects only within the transaction scope. Consider,
+ for example, these two implementations of the same transaction:</p>
+
+ <pre class="cxx">
+void
+update_age (database&amp; db, person&amp; p)
+{
+ transaction t (db.begin ());
+
+ p.age (p.age () + 1);
+ db.update (p);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>In the above implementation, if the <code>update()</code> call fails
+ and the transaction is rolled back, the state of the <code>person</code>
+ object in the database and the state of the same object in the
+ application's memory will differ. Now consider an
+ alternative implementation which only instantiates the
+ <code>person</code> object for the duration of the transaction:</p>
+
+ <pre class="cxx">
+void
+update_age (database&amp; db, unsigned long id)
+{
+ transaction t (db.begin ());
+
+ auto_ptr&lt;person> p (db.load&lt;person> (id));
+ p.age (p.age () + 1);
+ db.update (p);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>Of course, it may not always be possible to write the
+ application in this style. Oftentimes we need to access and
+ modify the application's state of persistent objects out of
+ transactions. In this case it may make sense to try to
+ roll back the changes made to the application state if
+ the transaction was rolled back and the database state
+ remains unchanged. One way to do this is to re-load
+ the object's state from the database, for example:</p>
+
+ <pre class="cxx">
+void
+update_age (database&amp; db, person&amp; p)
+{
+ try
+ {
+ transaction t (db.begin ());
+
+ p.age (p.age () + 1);
+ db.update (p);
+
+ t.commit ();
+ }
+ catch (...)
+ {
+ transaction t (db.begin ());
+ db.load (p.id (), p);
+ t.commit ();
+
+ throw;
+ }
+}
+ </pre>
+
+ <p>See also <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>
+ for an alternative approach.</p>
+
+ <h2><a name="3.6">3.6 Connections</a></h2>
+
+ <p>The <code>odb::connection</code> class represents a connection
+ to the database. Normally, you wouldn't work with connections
+ directly but rather let the ODB runtime obtain and release
+ connections as needed. However, certain use cases may require
+ obtaining a connection manually. For completeness, this section
+ describes the <code>connection</code> class and discusses some
+ of its use cases. You may want to skip this section if you are
+ reading through the manual for the first time.</p>
+
+ <p>Similar to <code>odb::database</code>, the <code>odb::connection</code>
+ class is a common interface for all the database system-specific
+ classes provided by ODB. For details on the system-specific
+ <code>connection</code> classes, refer to <a href="#II">Part II,
+ "Database Systems"</a>.</p>
+
+ <p>To make the <code>odb::connection</code> class available in your
+ application you will need to include the <code>&lt;odb/connection.hxx></code>
+ header file. The <code>odb::connection</code> class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class connection
+ {
+ public:
+ typedef odb::database database_type;
+
+ transaction
+ begin () = 0;
+
+ unsigned long long
+ execute (const char* statement);
+
+ unsigned long long
+ execute (const std::string&amp; statement);
+
+ unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ database_type&amp;
+ database ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+}
+ </pre>
+
+ <p>The <code>begin()</code> function is used to start a transaction
+ on the connection. The <code>execute()</code> functions allow
+ us to execute native database statements on the connection.
+ Their semantics are equivalent to the <code>database::execute()</code>
+ functions (<a href="#3.12">Section 3.12, "Executing Native SQL
+ Statements"</a>) except that they can be legally called outside
+ a transaction. Finally, the <code>database()</code> accessor
+ returns a reference to the <code>odb::database</code> instance
+ to which this connection corresponds.</p>
+
+ <p>To obtain a connection we call the <code>database::connection()</code>
+ function. The connection is returned as <code>odb::connection_ptr</code>,
+ which is an implementation-specific smart pointer with the shared
+ pointer semantics. This, in particular, means that the connection
+ pointer can be copied and returned from functions. Once the last
+ instance of <code>connection_ptr</code> pointing to the same
+ connection is destroyed, the connection is returned to the
+ <code>database</code> instance. The following code fragment
+ shows how we can obtain, use, and release a connection:</p>
+
+ <pre class="cxx">
+using namespace odb::core;
+
+database&amp; db = ...
+connection_ptr c (db.connection ());
+
+// Temporarily disable foreign key constraints.
+//
+c->execute ("SET FOREIGN_KEY_CHECKS = 0");
+
+// Start a transaction on this connection.
+//
+transaction t (c->begin ());
+...
+t.commit ();
+
+// Restore foreign key constraints.
+//
+c->execute ("SET FOREIGN_KEY_CHECKS = 1");
+
+// When 'c' goes out of scope, the connection is returned to 'db'.
+ </pre>
+
+ <p>Some of the use cases which may require direct manipulation of
+ connections include out-of-transaction statement execution,
+ such as the execution of connection configuration statements,
+ the implementation of a connection-per-thread policy, and making
+ sure that a set of transactions is executed on the same
+ connection.</p>
+
+ <h2><a name="3.7">3.7 Error Handling and Recovery</a></h2>
+
+ <p>ODB uses C++ exceptions to report database operation errors. Most
+ ODB exceptions signify <em>hard</em> errors or errors that cannot
+ be corrected without some intervention from the application.
+ For example, if we try to load an object with an unknown object
+ id, the <code>odb::object_not_persistent</code> exception is
+ thrown. Our application may be able to correct this error, for
+ instance, by obtaining a valid object id and trying again.
+ The hard errors and corresponding ODB exceptions that can be
+ thrown by each database function are described in the remainder
+ of this chapter with <a href="#3.14">Section 3.14, "ODB Exceptions"</a>
+ providing a quick reference for all the ODB exceptions.</p>
+
+ <p>The second group of ODB exceptions signify <em>soft</em> or
+ <em>recoverable</em> errors. Such errors are temporary
+ failures which normally can be corrected by simply re-executing
+ the transaction. ODB defines three such exceptions:
+ <code>odb::connection_lost</code>, <code>odb::timeout</code>,
+ and <code>odb::deadlock</code>. All recoverable ODB exceptions
+ are derived from the common <code>odb::recoverable</code> base
+ exception which can be used to handle all the recoverable
+ conditions with a single <code>catch</code> block.</p>
+
+ <p>The <code>odb::connection_lost</code> exception is thrown if
+ a connection to the database is lost in the middle of
+ a transaction. In this situation the transaction is aborted but
+ it can be re-tried without any changes. Similarly, the
+ <code>odb::timeout</code> exception is thrown if one of the
+ database operations or the whole transaction has timed out.
+ Again, in this case the transaction is aborted but can be
+ re-tried as is.</p>
+
+ <p>If two or more transactions access or modify more than one object
+ and are executed concurrently by different applications or by
+ different threads within the same application, then it is possible
+ that these transactions will try to access objects in an incompatible
+ order and deadlock. The canonical example of a deadlock are
+ two transactions in which the first has modified <code>object1</code>
+ and is waiting for the second transaction to commit its changes to
+ <code>object2</code> so that it can also update <code>object2</code>.
+ At the same time the second transaction has modified <code>object2</code>
+ and is waiting for the first transaction to commit its changes to
+ <code>object1</code> because it also needs to modify <code>object1</code>.
+ As a result, none of the two transactions can be completed.</p>
+
+ <p>The database system detects such situations and automatically
+ aborts the waiting operation in one of the deadlocked transactions.
+ In ODB this translates to the <code>odb::deadlock</code>
+ recoverable exception being thrown from one of the database functions.</p>
+
+ <p>The following code fragment shows how to handle the recoverable
+ exceptions by restarting the affected transaction:</p>
+
+ <pre class="cxx">
+const unsigned short max_retries = 5;
+
+for (unsigned short retry_count (0); ; retry_count++)
+{
+ try
+ {
+ transaction t (db.begin ());
+
+ ...
+
+ t.commit ();
+ break;
+ }
+ catch (const odb::recoverable&amp; e)
+ {
+ if (retry_count > max_retries)
+ throw retry_limit_exceeded (e.what ());
+ else
+ continue;
+ }
+}
+ </pre>
+
+ <h2><a name="3.8">3.8 Making Objects Persistent</a></h2>
+
+ <p>A newly created instance of a persistent class is transient.
+ We use the <code>database::persist()</code> function template
+ to make a transient instance persistent. This function has four
+ overloaded versions with the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (const T&amp; object);
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (const object_traits&lt;T>::const_pointer_type&amp; object);
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (T&amp; object);
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (const object_traits&lt;T>::pointer_type&amp; object);
+ </pre>
+
+ <p>Here and in the rest of the manual,
+ <code>object_traits&lt;T>::pointer_type</code> and
+ <code>object_traits&lt;T>::const_pointer_type</code> denote the
+ unrestricted and constant object pointer types (<a href="#3.3">Section
+ 3.3, "Object and View Pointers"</a>), respectively.
+ Similarly, <code>object_traits&lt;T>::id_type</code> denotes the object
+ id type. The <code>odb::object_traits</code> template is part of the
+ database support code generated by the ODB compiler.</p>
+
+ <p>The first <code>persist()</code> function expects a constant reference
+ to an instance being persisted. The second function expects a constant
+ object pointer. Both of these functions can only be used on objects with
+ application-assigned object ids (<a href="#14.4.2">Section 14.4.2,
+ "<code>auto</code>"</a>).</p>
+
+ <p>The second and third <code>persist()</code> functions are similar to the
+ first two except that they operate on unrestricted references and object
+ pointers. If the identifier of the object being persisted is assigned
+ by the database, these functions update the id member of the passed
+ instance with the assigned value. All four functions return the object
+ id of the newly persisted object.</p>
+
+ <p>If the database already contains an object of this type with this
+ identifier, the <code>persist()</code> functions throw the
+ <code>odb::object_already_persistent</code> exception. This should
+ never happen for database-assigned object ids as long as the
+ number of objects persisted does not exceed the value space of
+ the id type.</p>
+
+ <p>When calling the <code>persist()</code> functions, we don't need to
+ explicitly specify the template type since it will be automatically
+ deduced from the argument being passed. The following example shows
+ how we can call these functions:</p>
+
+ <pre class="cxx">
+person john ("John", "Doe", 33);
+shared_ptr&lt;person> jane (new person ("Jane", "Doe", 32));
+
+transaction t (db.begin ());
+
+db.persist (john);
+unsigned long jane_id (db.persist (jane));
+
+t.commit ();
+
+cerr &lt;&lt; "Jane's id: " &lt;&lt; jane_id &lt;&lt; endl;
+ </pre>
+
+ <p>Notice that in the above code fragment we have created instances
+ that we were planning to make persistent before starting the
+ transaction. Likewise, we printed Jane's id after we have committed
+ the transaction. As a general rule, you should avoid performing
+ operations within the transaction scope that can be performed
+ before the transaction starts or after it terminates. An active
+ transaction consumes both your application's resources, such as
+ a database connection, as well as the database server's
+ resources, such as object locks. By following the above rule you
+ make sure these resources are released and made available to other
+ threads in your application and to other applications as soon as
+ possible.</p>
+
+ <p>Some database systems support persisting multiple objects with a
+ single underlying statement execution which can result in significantly
+ improved performance. For such database systems ODB provides
+ bulk <code>persist()</code> functions. For details, refer to
+ <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
+
+ <h2><a name="3.9">3.9 Loading Persistent Objects</a></h2>
+
+ <p>Once an object is made persistent, and you know its object id, it
+ can be loaded by the application using the <code>database::load()</code>
+ function template. This function has two overloaded versions with
+ the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ load (const typename object_traits&lt;T>::id_type&amp; id);
+
+ template &lt;typename T>
+ void
+ load (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
+ </pre>
+
+ <p>Given an object id, the first function allocates a new instance
+ of the object class in the dynamic memory, loads its state from
+ the database, and returns the pointer to the new instance. The
+ second function loads the object's state into an existing instance.
+ Both functions throw <code>odb::object_not_persistent</code> if
+ there is no object of this type with this id in the database.</p>
+
+ <p>When we call the first <code>load()</code> function, we need to
+ explicitly specify the object type. We don't need to do this for
+ the second function because the object type will be automatically
+ deduced from the second argument, for example:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+auto_ptr&lt;person> jane (db.load&lt;person> (jane_id));
+
+db.load (jane_id, *jane);
+
+t.commit ();
+ </pre>
+
+ <p>In certain situations it may be necessary to reload the state
+ of an object from the database. While this is easy to achieve
+ using the second <code>load()</code> function, ODB provides
+ the <code>database::reload()</code> function template that
+ has a number of special properties. This function has two
+ overloaded versions with the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ reload (T&amp; object);
+
+ template &lt;typename T>
+ void
+ reload (const object_traits&lt;T>::pointer_type&amp; object);
+ </pre>
+
+ <p>The first <code>reload()</code> function expects an object
+ reference, while the second expects an object pointer. Both
+ functions expect the id member in the passed object to contain
+ a valid object identifier and, similar to <code>load()</code>,
+ both will throw <code>odb::object_not_persistent</code> if
+ there is no object of this type with this id in the database.</p>
+
+ <p>The first special property of <code>reload()</code>
+ compared to the <code>load()</code> function is that it
+ does not interact with the session's object cache
+ (<a href="#11.1">Section 11.1, "Object Cache"</a>). That is, if
+ the object being reloaded is already in the cache, then it will
+ remain there after <code>reload()</code> returns. Similarly, if the
+ object is not in the cache, then <code>reload()</code> won't
+ put it there either.</p>
+
+ <p>The second special property of the <code>reload()</code> function
+ only manifests itself when operating on an object with the optimistic
+ concurrency model. In this case, if the states of the object
+ in the application memory and in the database are the same, then
+ no reloading will occur. For more information on optimistic
+ concurrency, refer to <a href="#12">Chapter 12, "Optimistic
+ Concurrency"</a>.</p>
+
+ <p>If we don't know for sure whether an object with a given id
+ is persistent, we can use the <code>find()</code> function
+ instead of <code>load()</code>, for example:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ find (const typename object_traits&lt;T>::id_type&amp; id);
+
+ template &lt;typename T>
+ bool
+ find (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
+ </pre>
+
+ <p>If an object with this id is not found in the database, the first
+ <code>find()</code> function returns a <code>NULL</code> pointer
+ while the second function leaves the passed instance unmodified and
+ returns <code>false</code>.</p>
+
+ <p>If we don't know the object id, then we can use queries to
+ find the object (or objects) matching some criteria
+ (<a href="#4">Chapter 4, "Querying the Database"</a>). Note,
+ however, that loading an object's state using its
+ identifier can be significantly faster than executing a query.</p>
+
+
+ <h2><a name="3.10">3.10 Updating Persistent Objects</a></h2>
+
+ <p>If a persistent object has been modified, we can store the updated
+ state in the database using the <code>database::update()</code>
+ function template. This function has three overloaded versions with
+ the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ update (const T&amp; object);
+
+ template &lt;typename T>
+ void
+ update (const object_traits&lt;T>::const_pointer_type&amp; object);
+
+ template &lt;typename T>
+ void
+ update (const object_traits&lt;T>::pointer_type&amp; object);
+ </pre>
+
+ <p>The first <code>update()</code> function expects an object reference,
+ while the other two expect object pointers. If the object passed to
+ one of these functions does not exist in the database,
+ <code>update()</code> throws the <code>odb::object_not_persistent</code>
+ exception (but see a note on optimistic concurrency below).</p>
+
+ <p>Below is an example of the funds transfer that we talked about
+ in the earlier section on transactions. It uses the hypothetical
+ <code>bank_account</code> persistent class:</p>
+
+ <pre class="cxx">
+void
+transfer (database&amp; db,
+ unsigned long from_acc,
+ unsigned long to_acc,
+ unsigned int amount)
+{
+ bank_account from, to;
+
+ transaction t (db.begin ());
+
+ db.load (from_acc, from);
+
+ if (from.balance () &lt; amount)
+ throw insufficient_funds ();
+
+ db.load (to_acc, to);
+
+ to.balance (to.balance () + amount);
+ from.balance (from.balance () - amount);
+
+ db.update (to);
+ db.update (from);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>The same can be accomplished using dynamically allocated objects
+ and the <code>update()</code> function with object pointer argument,
+ for example:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+shared_ptr&lt;bank_account> from (db.load&lt;bank_account> (from_acc));
+
+if (from->balance () &lt; amount)
+ throw insufficient_funds ();
+
+shared_ptr&lt;bank_account> to (db.load&lt;bank_account> (to_acc));
+
+to->balance (to->balance () + amount);
+from->balance (from->balance () - amount);
+
+db.update (to);
+db.update (from);
+
+t.commit ();
+ </pre>
+
+ <p>If any of the <code>update()</code> functions are operating on a
+ persistent class with the optimistic concurrency model, then they will
+ throw the <code>odb::object_changed</code> exception if the state of the
+ object in the database has changed since it was last loaded into the
+ application memory. Furthermore, for such classes, <code>update()</code>
+ no longer throws the <code>object_not_persistent</code> exception if
+ there is no such object in the database. Instead, this condition is
+ treated as a change of object state and <code>object_changed</code>
+ is thrown instead. For a more detailed discussion of optimistic
+ concurrency, refer to <a href="#12">Chapter 12, "Optimistic
+ Concurrency"</a>.</p>
+
+ <p>In ODB, persistent classes, composite value types, as well as individual
+ data members can be declared read-only (see <a href="#14.1.4">Section
+ 14.1.4, "<code>readonly</code> (object)"</a>, <a href="#14.3.6">Section
+ 14.3.6, "<code>readonly</code> (composite value)"</a>, and
+ <a href="#14.4.12">Section 14.4.12, "<code>readonly</code>
+ (data member)"</a>).</p>
+
+ <p>If an individual data member is declared read-only, then
+ any changes to this member will be ignored when updating the database
+ state of an object using any of the above <code>update()</code>
+ functions. A <code>const</code> data member is automatically treated
+ as read-only. If a composite value is declared read-only then all its
+ data members are treated as read-only.</p>
+
+ <p>If the whole object is declared read-only then the database state of
+ this object cannot be changed. Calling any of the above
+ <code>update()</code> functions for such an object will result in a
+ compile-time error.</p>
+
+ <p>Similar to <code>persist()</code>, for database systems that support
+ this functionality, ODB provides bulk <code>update()</code> functions.
+ For details, refer to <a href="#15.3">Section 15.3, "Bulk Database
+ Operations"</a>.</p>
+
+ <h2><a name="3.11">3.11 Deleting Persistent Objects</a></h2>
+
+ <p>To delete a persistent object's state from the database we use the
+ <code>database::erase()</code> or <code>database::erase_query()</code>
+ function templates. If the application still has an instance of the
+ erased object, this instance becomes transient. The <code>erase()</code>
+ function has the following overloaded versions:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ erase (const T&amp; object);
+
+ template &lt;typename T>
+ void
+ erase (const object_traits&lt;T>::const_pointer_type&amp; object);
+
+ template &lt;typename T>
+ void
+ erase (const object_traits&lt;T>::pointer_type&amp; object);
+
+ template &lt;typename T>
+ void
+ erase (const typename object_traits&lt;T>::id_type&amp; id);
+ </pre>
+
+ <p>The first <code>erase()</code> function uses an object itself, in
+ the form of an object reference, to delete its state from the
+ database. The next two functions accomplish the same result but using
+ object pointers. Note that all three functions leave the passed
+ object unchanged. It simply becomes transient. The last function
+ uses the object id to identify the object to be deleted. If the
+ object does not exist in the database, then all four functions
+ throw the <code>odb::object_not_persistent</code> exception
+ (but see a note on optimistic concurrency below).</p>
+
+ <p>We have to specify the object type when calling the last
+ <code>erase()</code> function. The same is unnecessary for the
+ first three functions because the object type will be automatically
+ deduced from their arguments. The following example shows how we
+ can call these functions:</p>
+
+ <pre class="cxx">
+person&amp; john = ...
+shared_ptr&lt;jane> jane = ...
+unsigned long joe_id = ...
+
+transaction t (db.begin ());
+
+db.erase (john);
+db.erase (jane);
+db.erase&lt;person> (joe_id);
+
+t.commit ();
+ </pre>
+
+ <p>If any of the <code>erase()</code> functions except the last one are
+ operating on a persistent class with the optimistic concurrency
+ model, then they will throw the <code>odb::object_changed</code> exception
+ if the state of the object in the database has changed since it was
+ last loaded into the application memory. Furthermore, for such
+ classes, <code>erase()</code> no longer throws the
+ <code>object_not_persistent</code> exception if there is no such
+ object in the database. Instead, this condition is treated as a
+ change of object state and <code>object_changed</code> is thrown
+ instead. For a more detailed discussion of optimistic concurrency,
+ refer to <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
+
+ <p>Similar to <code>persist()</code> and <code>update()</code>, for
+ database systems that support this functionality, ODB provides
+ bulk <code>erase()</code> functions. For details, refer to
+ <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
+
+ <p>The <code>erase_query()</code> function allows us to delete
+ the state of multiple objects matching certain criteria. It uses
+ the query expression of the <code>database::query()</code> function
+ (<a href="#4">Chapter 4, "Querying the Database"</a>) and,
+ because the ODB query facility is optional, it is only available
+ if the <code>--generate-query</code> ODB compiler option was
+ specified. The <code>erase_query()</code> function has the
+ following overloaded versions:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ unsigned long long
+ erase_query ();
+
+ template &lt;typename T>
+ unsigned long long
+ erase_query (const odb::query&lt;T>&amp;);
+ </pre>
+
+ <p>The first <code>erase_query()</code> function is used to delete
+ the state of all the persistent objects of a given type stored
+ in the database. The second function uses the passed query instance
+ to only delete the state of objects matching the query criteria.
+ Both functions return the number of objects erased. When calling
+ the <code>erase_query()</code> function, we have to explicitly
+ specify the object type we are erasing. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+
+transaction t (db.begin ());
+
+db.erase_query&lt;person> (query::last == "Doe" &amp;&amp; query::age &lt; 30);
+
+t.commit ();
+ </pre>
+
+ <p>Unlike the <code>query()</code> function, when calling
+ <code>erase_query()</code> we cannot use members from pointed-to
+ objects in the query expression. However, we can still use
+ a member corresponding to a pointer as an ordinary object
+ member that has the id type of the pointed-to object
+ (<a href="#6">Chapter 6, "Relationships"</a>). This allows us
+ to compare object ids as well as test the pointer for
+ <code>NULL</code>. As an example, the following transaction
+ makes sure that all the <code>employee</code> objects that
+ reference an <code>employer</code> object that is about to
+ be deleted are deleted as well. Here we assume that the
+ <code>employee</code> class contains a pointer to the
+ <code>employer</code> class. Refer to <a href="#6">Chapter 6,
+ "Relationships"</a> for complete definitions of these
+ classes.</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee> query;
+
+transaction t (db.begin ());
+
+employer&amp; e = ... // Employer object to be deleted.
+
+db.erase_query&lt;employee> (query::employer == e.id ());
+db.erase (e);
+
+t.commit ();
+ </pre>
+
+
+ <h2><a name="3.12">3.12 Executing Native SQL Statements</a></h2>
+
+ <p>In some situations we may need to execute native SQL statements
+ instead of using the object-oriented database API described above.
+ For example, we may want to tune the database schema generated
+ by the ODB compiler or take advantage of a feature that is
+ specific to the database system we are using. The
+ <code>database::execute()</code> function, which has three
+ overloaded versions, provides this functionality:</p>
+
+ <pre class="cxx">
+ unsigned long long
+ execute (const char* statement);
+
+ unsigned long long
+ execute (const std::string&amp; statement);
+
+ unsigned long long
+ execute (const char* statement, std::size_t length)
+ </pre>
+
+ <p>The first <code>execute()</code> function expects the SQL statement
+ as a zero-terminated C-string. The last version expects the explicit
+ statement length as the second argument and the statement itself
+ may contain <code>'\0'</code> characters, for example, to represent
+ binary data, if the database system supports it. All three functions
+ return the number of rows that were affected by the statement. For
+ example:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+db.execute ("DROP TABLE test");
+db.execute ("CREATE TABLE test (n INT PRIMARY KEY)");
+
+t.commit ();
+ </pre>
+
+ <p>While these functions must always be called within a transaction,
+ it may be necessary to execute a native statement outside a
+ transaction. This can be done using the
+ <code>connection::execute()</code> functions as described in
+ <a href="#3.6">Section 3.6, "Connections"</a>.</p>
+
+ <h2><a name="3.13">3.13 Tracing SQL Statement Execution</a></h2>
+
+ <p>Oftentimes it is useful to understand what SQL statements are
+ executed as a result of high-level database operations. For
+ example, we can use this information to figure out why certain
+ transactions don't produce desired results or why they take
+ longer than expected.</p>
+
+ <p>While this information can usually be obtained from the database
+ logs, ODB provides an application-side SQL statement tracing
+ support that is both more convenient and finer-grained.
+ For example, in a typical situation that calls for tracing
+ we would like to see the SQL statements executed as a result
+ of a specific transaction. While it may be difficult to
+ extract such a subset of statements from the database logs,
+ it is easy to achieve with ODB tracing support:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+t.tracer (stderr_tracer);
+
+...
+
+t.commit ();
+ </pre>
+
+ <p>ODB allows us to specify a tracer on the database, connection,
+ and transaction levels. If specified for the database, then
+ all the statements executed on this database will be traced.
+ On the other hand, if a tracer is specified for the
+ connection, then only the SQL statements executed on this
+ connection will be traced. Similarly, a tracer specified
+ for a transaction will only show statements that are
+ executed as part of this transaction. All three classes
+ (<code>odb::database</code>, <code>odb::connection</code>,
+ and <code>odb::transaction</code>) provide the identical
+ tracing API:</p>
+
+ <pre class="cxx">
+ void
+ tracer (odb::tracer&amp;);
+
+ void
+ tracer (odb::tracer*);
+
+ odb::tracer*
+ tracer () const;
+ </pre>
+
+ <p>The first two <code>tracer()</code> functions allow us to set
+ the tracer object with the second one allowing us to clear the
+ current tracer by passing a <code>NULL</code> pointer. The
+ last <code>tracer()</code> function allows us to get the
+ current tracer object. It returns a <code>NULL</code> pointer
+ if there is no tracer in effect. Note that the tracing API
+ does not manage the lifetime of the tracer object. The tracer
+ should be valid for as long as it is being used. Furthermore,
+ the tracing API is not thread-safe. Trying to set a tracer
+ from multiple threads simultaneously will result in
+ undefined behavior.</p>
+
+ <p>The <code>odb::tracer</code> class defines a callback interface
+ that can be used to create custom tracer implementations. The
+ <code>odb::stderr_tracer</code> and <code>odb::stderr_full_tracer</code>
+ are built-in tracer implementations provided by the ODB runtime.
+ They both print SQL statements being executed to the standard error
+ stream. The full tracer, in addition to tracing statement executions,
+ also traces their preparations and deallocations. One situation where
+ the full tracer can be particularly useful is if a statement (for
+ example a custom query) contains a syntax error. In this case the
+ error will be detected during preparation and, as a result, the
+ statement will never be executed. The only way to see such a statement
+ is by using the full tracing.</p>
+
+ <p>The <code>odb::tracer</code> class is defined in the
+ <code>&lt;odb/tracer.hxx></code> header file which you will need to
+ include in order to make this class available in your application.
+ The <code>odb::tracer</code> interface provided the following
+ callback functions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class tracer
+ {
+ public:
+ virtual void
+ prepare (connection&amp;, const statement&amp;);
+
+ virtual void
+ execute (connection&amp;, const statement&amp;);
+
+ virtual void
+ execute (connection&amp;, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&amp;, const statement&amp;);
+ };
+}
+ </pre>
+
+ <p>The <code>prepare()</code> and <code>deallocate()</code> functions
+ are called when a prepared statement is created and destroyed,
+ respectively. The first <code>execute()</code> function is called
+ when a prepared statement is executed while the second one is called
+ when a normal statement is executed. The default implementations
+ for the <code>prepare()</code> and <code>deallocate()</code>
+ functions do nothing while the first <code>execute()</code> function
+ calls the second one passing the statement text as the second
+ argument. As a result, if all you are interested in are the
+ SQL statements being executed, then you only need to override the
+ second <code>execute()</code> function.</p>
+
+ <p>In addition to the common <code>odb::tracer</code> interface,
+ each database runtime provides a database-specific version
+ as <code>odb::&lt;database>::tracer</code>. It has exactly
+ the same interface as the common version except that the
+ <code>connection</code> and <code>statement</code> types
+ are database-specific, which gives us access to additional,
+ database-specific information.</p>
+
+ <p>As an example, consider a more elaborate, PostgreSQL-specific
+ tracer implementation. Here we rely on the fact that the PostgreSQL
+ ODB runtime uses names to identify prepared statements and this
+ information can be obtained from the <code>odb::pgsql::statement</code>
+ object:</p>
+
+ <pre class="cxx">
+#include &lt;odb/pgsql/tracer.hxx>
+#include &lt;odb/pgsql/database.hxx>
+#include &lt;odb/pgsql/connection.hxx>
+#include &lt;odb/pgsql/statement.hxx>
+
+class pgsql_tracer: public odb::pgsql::tracer
+{
+ virtual void
+ prepare (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": PREPARE " &lt;&lt; s.name ()
+ &lt;&lt; " AS " &lt;&lt; s.text () &lt;&lt; endl;
+ }
+
+ virtual void
+ execute (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": EXECUTE " &lt;&lt; s.name () &lt;&lt; endl;
+ }
+
+ virtual void
+ execute (odb::pgsql::connection&amp; c, const char* statement)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": " &lt;&lt; statement &lt;&lt; endl;
+ }
+
+ virtual void
+ deallocate (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": DEALLOCATE " &lt;&lt; s.name () &lt;&lt; endl;
+ }
+};
+ </pre>
+
+ <p>Note also that you can only set a database-specific tracer object
+ using a database-specific database instance, for example:</p>
+
+ <pre class="cxx">
+pgsql_tracer tracer;
+
+odb::database&amp; db = ...;
+db.tracer (tracer); // Compile error.
+
+odb::pgsql::database&amp; db = ...;
+db.tracer (tracer); // Ok.
+ </pre>
+
+ <h2><a name="3.14">3.14 ODB Exceptions</a></h2>
+
+ <p>In the previous sections we have already mentioned some of the
+ exceptions that can be thrown by the database functions. In this
+ section we will discuss the ODB exception hierarchy and document
+ all the exceptions that can be thrown by the common ODB
+ runtime.</p>
+
+ <p>The root of the ODB exception hierarchy is the abstract
+ <code>odb::exception</code> class. This class derives
+ from <code>std::exception</code> and has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ struct exception: std::exception
+ {
+ virtual const char*
+ what () const throw () = 0;
+ };
+}
+ </pre>
+
+ <p>Catching this exception guarantees that we will catch all the
+ exceptions thrown by ODB. The <code>what()</code> function
+ returns a human-readable description of the condition that
+ triggered the exception.</p>
+
+ <p>The concrete exceptions that can be thrown by ODB are presented
+ in the following listing:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ struct null_pointer: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Transaction exceptions.
+ //
+ struct already_in_transaction: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct not_in_transaction: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct transaction_already_finalized: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Session exceptions.
+ //
+ struct already_in_session: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct not_in_session: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct session_required: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Database operations exceptions.
+ //
+ struct recoverable: exception
+ {
+ };
+
+ struct connection_lost: recoverable
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct timeout: recoverable
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct deadlock: recoverable
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct object_not_persistent: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct object_already_persistent: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct object_changed: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct result_not_cached: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct database_exception: exception
+ {
+ };
+
+ // Polymorphism support exceptions.
+ //
+ struct abstract_class: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct no_type_info: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Prepared query support exceptions.
+ //
+ struct prepared_already_cached: exception
+ {
+ const char*
+ name () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct prepared_type_mismatch: exception
+ {
+ const char*
+ name () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Schema catalog exceptions.
+ //
+ struct unknown_schema: exception
+ {
+ const std::string&amp;
+ name () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct unknown_schema_version: exception
+ {
+ schema_version
+ version () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Section exceptions.
+ //
+ struct section_not_loaded: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct section_not_in_object: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Bulk operation exceptions.
+ //
+ struct multiple_exceptions: exception
+ {
+ ...
+
+ virtual const char*
+ what () const throw ();
+ };
+}
+ </pre>
+
+ <p>The <code>null_pointer</code> exception is thrown when a
+ pointer to a persistent object declared non-<code>NULL</code>
+ with the <code>db&nbsp;not_null</code> or
+ <code>db&nbsp;value_not_null</code> pragma has the <code>NULL</code>
+ value. See <a href="#6">Chapter 6, "Relationships"</a> for details.</p>
+
+ <p>The next three exceptions (<code>already_in_transaction</code>,
+ <code>not_in_transaction</code>,
+ <code>transaction_already_finalized</code>) are thrown by the
+ <code>odb::transaction</code> class and are discussed
+ in <a href="#3.5">Section 3.5, "Transactions"</a>.</p>
+
+ <p>The next two exceptions (<code>already_in_session</code>, and
+ <code>not_in_session</code>) are thrown by the <code>odb::session</code>
+ class and are discussed in <a href="#11">Chapter 11, "Session"</a>.</p>
+
+ <p>The <code>session_required</code> exception is thrown when ODB detects
+ that correctly loading a bidirectional object relationship requires a
+ session but one is not used. See <a href="#6.2">Section 6.2,
+ "Bidirectional Relationships"</a> for more information on this
+ exception.</p>
+
+ <p>The <code>recoverable</code> exception serves as a common base
+ for all the recoverable exceptions, which are: <code>connection_lost</code>,
+ <code>timeout</code>, and <code>deadlock</code>. The
+ <code>connection_lost</code> exception is thrown when a connection
+ to the database is lost. Similarly, the <code>timeout</code> exception
+ is thrown if one of the database operations or the whole transaction
+ has timed out. The <code>deadlock</code> exception is thrown when a
+ transaction deadlock is detected by the database system. These
+ exceptions can be thrown by any database function. See
+ <a href="#3.7">Section 3.7, "Error Handling and Recovery"</a>
+ for details.</p>
+
+ <p>The <code>object_already_persistent</code> exception is thrown
+ by the <code>persist()</code> database function. See
+ <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>
+ for details.</p>
+
+ <p>The <code>object_not_persistent</code> exception is thrown
+ by the <code>load()</code>, <code>update()</code>, and
+ <code>erase()</code> database functions. Refer to
+ <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
+ <a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>, and
+ <a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a> for
+ more information.</p>
+
+ <p>The <code>object_changed</code> exception is thrown
+ by the <code>update()</code> database function and certain
+ <code>erase()</code> database functions when
+ operating on objects with the optimistic concurrency model. See
+ <a href="#12">Chapter 12, "Optimistic Concurrency"</a> for details.</p>
+
+ <p>The <code>result_not_cached</code> exception is thrown by
+ the query result class. Refer to <a href="#4.4">Section 4.4,
+ "Query Result"</a> for details.</p>
+
+ <p>The <code>database_exception</code> exception is a base class for all
+ database system-specific exceptions that are thrown by the
+ database system-specific runtime library. Refer to <a href="#II">Part
+ II, "Database Systems"</a> for more information.</p>
+
+ <p>The <code>abstract_class</code> exception is thrown by the database
+ functions when we attempt to persist, update, load, or erase an
+ instance of a polymorphic abstract class. For more information
+ on abstract classes, refer to <a href="#14.1.3">Section 14.1.3,
+ "<code>abstract</code>"</a>.</p>
+
+ <p>The <code>no_type_info</code> exception is thrown by the database
+ functions when we attempt to persist, update, load, or erase an
+ instance of a polymorphic class for which no type information
+ is present in the application. This normally means that the
+ generated database support code for this class has not been
+ linked (or dynamically loaded) into the application or the
+ discriminator value has not been mapped to a persistent
+ class. For more information on polymorphism support, refer to
+ <a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>.</p>
+
+ <p>The <code>prepared_already_cached</code> exception is thrown by the
+ <code>cache_query()</code> function if a prepared query with the
+ specified name is already cached. The <code>prepared_type_mismatch</code>
+ exception is thrown by the <code>lookup_query()</code> function if
+ the specified prepared query object type or parameters type
+ does not match the one in the cache. Refer to <a href="#4.5">Section
+ 4.5, "Prepared Queries"</a> for details.</p>
+
+ <p>The <code>unknown_schema</code> exception is thrown by the
+ <code>odb::schema_catalog</code> class if a schema with the specified
+ name is not found. Refer to <a href="#3.4">Section 3.4, "Database"</a>
+ for details. The <code>unknown_schema_version</code> exception is thrown
+ by the <code>schema_catalog</code> functions that deal with database
+ schema evolution if the passed or current version is unknow. Refer
+ to <a href="#13">Chapter 13, "Database Schema Evolution"</a> for
+ details.</p>
+
+ <p>The <code>section_not_loaded</code> exception is thrown if we
+ attempt to update an object section that hasn't been loaded.
+ The <code>section_not_in_object</code> exception is thrown if
+ the section instance being loaded or updated does not belong
+ to the corresponding object. See <a href="#9">Chapter 9,
+ "Sections"</a> for more information on these exceptions.</p>
+
+ <p>The <code>multiple_exceptions</code> exception is thrown by the
+ bulk API functions. Refer to <a href="#15.3">Section 15.3, "Bulk
+ Database Operations"</a> for details.</p>
+
+ <p>The <code>odb::exception</code> class is defined in the
+ <code>&lt;odb/exception.hxx></code> header file. All the
+ concrete ODB exceptions are defined in
+ <code>&lt;odb/exceptions.hxx></code> which also includes
+ <code>&lt;odb/exception.hxx></code>. Normally you don't
+ need to include either of these two headers because they are
+ automatically included by <code>&lt;odb/database.hxx></code>.
+ However, if the source file that handles ODB exceptions
+ does not include <code>&lt;odb/database.hxx></code>, then
+ you will need to explicitly include one of these headers.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="4">4 Querying the Database</a></h1>
+
+ <p>If we don't know the identifiers of the objects that we are looking
+ for, we can use queries to search the database for objects matching
+ certain criteria. The ODB query facility is optional and we need to
+ explicitly request the generation of the necessary database support
+ code with the <code>--generate-query</code> ODB compiler option.</p>
+
+ <p>ODB provides a flexible query API that offers two distinct levels of
+ abstraction from the database system query language such as SQL.
+ At the high level we are presented with an easy to use yet powerful
+ object-oriented query language, called ODB Query Language. This
+ query language is modeled after and is integrated into C++ allowing
+ us to write expressive and safe queries that look and feel like
+ ordinary C++. We have already seen examples of these queries in the
+ introductory chapters. Below is another, more interesting, example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ unsigned short age;
+ query q (query::first == "John" &amp;&amp; query::age &lt; query::_ref (age));
+
+ for (age = 10; age &lt; 100; age += 10)
+ {
+ result r (db.query&lt;person> (q));
+ ...
+ }
+ </pre>
+
+ <p>At the low level, queries can be written as predicates using
+ the database system-native query language such as the
+ <code>WHERE</code> predicate from the SQL <code>SELECT</code>
+ statement. This language will be referred to as native query
+ language. At this level ODB still takes care of converting
+ query parameters from C++ to the database system format. Below
+ is the re-implementation of the above example using SQL as
+ the native query language:</p>
+
+ <pre class="cxx">
+ query q ("first = 'John' AND age = " + query::_ref (age));
+ </pre>
+
+ <p>Note that at this level we lose the static typing of
+ query expressions. For example, if we wrote something like this:</p>
+
+ <pre class="cxx">
+ query q (query::first == 123 &amp;&amp; query::agee &lt; query::_ref (age));
+ </pre>
+
+ <p>We would get two errors during the C++ compilation. The first would
+ indicate that we cannot compare <code>query::first</code> to an
+ integer and the second would pick the misspelling in
+ <code>query::agee</code>. On the other hand, if we wrote something
+ like this:</p>
+
+ <pre class="cxx">
+ query q ("first = 123 AND agee = " + query::_ref (age));
+ </pre>
+
+ <p>It would compile fine and would trigger an error only when executed
+ by the database system.</p>
+
+ <p>We can also combine the two query languages in a single query, for
+ example:</p>
+
+ <pre class="cxx">
+ query q ("first = 'John' AND" + (query::age &lt; query::_ref (age)));
+ </pre>
+
+ <h2><a name="4.1">4.1 ODB Query Language</a></h2>
+
+ <p>An ODB query is an expression that tells the database system whether
+ any given object matches the desired criteria. As such, a query expression
+ always evaluates as <code>true</code> or <code>false</code>. At
+ the higher level, an expression consists of other expressions
+ combined with logical operators such as <code>&amp;&amp;</code> (AND),
+ <code>||</code> (OR), and <code>!</code> (NOT). For example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+
+ query q (query::first == "John" || query::age == 31);
+ </pre>
+
+ <p>At the core of every query expression lie simple expressions which
+ involve one or more object members, values, or parameters. To
+ refer to an object member we use an expression such as
+ <code>query::first</code> above. The names of members in the
+ <code>query</code> class are derived from the names of data members
+ in the object class by removing the common member name decorations,
+ such as leading and trailing underscores, the <code>m_</code> prefix,
+ etc.</p>
+
+ <p>In a simple expression an object member can be compared to a value,
+ parameter, or another member using a number of predefined operators
+ and functions. The following table gives an overview of the available
+ expressions:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="operators" border="1">
+ <tr>
+ <th>Operator</th>
+ <th>Description</th>
+ <th>Example</th>
+ </tr>
+
+ <tr>
+ <td><code>==</code></td>
+ <td>equal</td>
+ <td><code>query::age == 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>!=</code></td>
+ <td>unequal</td>
+ <td><code>query::age != 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>&lt;</code></td>
+ <td>less than</td>
+ <td><code>query::age &lt; 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>></code></td>
+ <td>greater than</td>
+ <td><code>query::age > 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>&lt;=</code></td>
+ <td>less than or equal</td>
+ <td><code>query::age &lt;= 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>>=</code></td>
+ <td>greater than or equal</td>
+ <td><code>query::age >= 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>in()</code></td>
+ <td>one of the values</td>
+ <td><code>query::age.in (30, 32, 34)</code></td>
+ </tr>
+
+ <tr>
+ <td><code>in_range()</code></td>
+ <td>one of the values in range</td>
+ <td><code>query::age.in_range (begin, end)</code></td>
+ </tr>
+
+ <tr>
+ <td><code>like()</code></td>
+ <td>matches a pattern</td>
+ <td><code>query::first.like ("J%")</code></td>
+ </tr>
+
+ <tr>
+ <td><code>is_null()</code></td>
+ <td>value is <code>NULL</code></td>
+ <td><code>query::age.is_null ()</code></td>
+ </tr>
+
+ <tr>
+ <td><code>is_not_null()</code></td>
+ <td>value is <code>NOT NULL</code></td>
+ <td><code>query::age.is_not_null ()</code></td>
+ </tr>
+ </table>
+
+ <p>The <code>in()</code> function accepts a maximum of five arguments.
+ Use the <code>in_range()</code> function if you need to compare
+ to more than five values. This function accepts a pair of
+ standard C++ iterators and compares to all the values from
+ the <code>begin</code> position inclusive and until and
+ excluding the <code>end</code> position. The following
+ code fragment shows how we can use these functions:</p>
+
+ <pre class="cxx">
+ std::vector&lt;string> names;
+
+ names.push_back ("John");
+ names.push_back ("Jack");
+ names.push_back ("Jane");
+
+ query q1 (query::first.in ("John", "Jack", "Jane"));
+ query q2 (query::first.in_range (names.begin (), names.end ()));
+ </pre>
+
+ <p>Note that the <code>like()</code> function does not perform any
+ translation of the database system-specific extensions of the
+ SQL <code>LIKE</code> operator. As a result, if you would like
+ your application to be portable among various database systems,
+ then limit the special characters used in the pattern to
+ <code>%</code> (matches zero or more characters) and <code>_</code>
+ (matches exactly one character). It is also possible to specify
+ the escape character as a second argument to the <code>like()</code>
+ function. This character can then be used to escape the special
+ characters (<code>%</code> and <code>_</code>) in the pattern.
+ For example, the following query will match any two characters
+ separated by an underscore:</p>
+
+ <pre class="cxx">
+ query q (query::name.like ("_!__", "!"));
+ </pre>
+
+ <p>The operator precedence in the query expressions are the same
+ as for equivalent C++ operators. We can use parentheses to
+ make sure the expression is evaluated in the desired order.
+ For example:</p>
+
+ <pre class="cxx">
+ query q ((query::first == "John" || query::first == "Jane") &amp;&amp;
+ query::age &lt; 31);
+ </pre>
+
+
+ <h2><a name="4.2">4.2 Parameter Binding</a></h2>
+
+ <p>An instance of the <code>odb::query</code> class encapsulates two
+ parts of information about the query: the query expression and
+ the query parameters. Parameters can be bound to C++ variables
+ either by value or by reference.</p>
+
+ <p>If a parameter is bound by value, then the value for this parameter
+ is copied from the C++ variable to the query instance at the query
+ construction time. On the other hand, if a parameter is bound by
+ reference, then the query instance stores a reference to the
+ bound variable. The actual value of the parameter is only extracted
+ at the query execution time. Consider, for example, the following
+ two queries:</p>
+
+ <pre class="cxx">
+ string name ("John");
+
+ query q1 (query::first == query::_val (name));
+ query q2 (query::first == query::_ref (name));
+
+ name = "Jane";
+
+ db.query&lt;person> (q1); // Find John.
+ db.query&lt;person> (q2); // Find Jane.
+ </pre>
+
+ <p>The <code>odb::query</code> class provides two special functions,
+ <code>_val()</code> and <code>_ref()</code>, that allow us to
+ bind the parameter either by value or by reference, respectively.
+ In the ODB query language, if the binding is not specified
+ explicitly, the value semantic is used by default. In the
+ native query language, binding must always be specified
+ explicitly. For example:</p>
+
+ <pre class="cxx">
+ query q1 (query::age &lt; age); // By value.
+ query q2 (query::age &lt; query::_val (age)); // By value.
+ query q3 (query::age &lt; query::_ref (age)); // By reference.
+
+ query q4 ("age &lt; " + age); // Error.
+ query q5 ("age &lt; " + query::_val (age)); // By value.
+ query q6 ("age &lt; " + query::_ref (age)); // By reference.
+ </pre>
+
+ <p>A query that only has by-value parameters does not depend on any
+ other variables and is self-sufficient once constructed. A query
+ that has one or more by-reference parameters depends on the
+ bound variables until the query is executed. If one such variable
+ goes out of scope and we execute the query, the behavior is
+ undefined.</p>
+
+ <h2><a name="4.3">4.3 Executing a Query</a></h2>
+
+ <p>Once we have the query instance ready and by-reference parameters
+ initialized, we can execute the query using the
+ <code>database::query()</code> function template. It has two
+ overloaded versions:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ result&lt;T>
+ query (bool cache = true);
+
+ template &lt;typename T>
+ result&lt;T>
+ query (const odb::query&lt;T>&amp;, bool cache = true);
+ </pre>
+
+ <p>The first <code>query()</code> function is used to return all the
+ persistent objects of a given type stored in the database.
+ The second function uses the passed query instance to only return
+ objects matching the query criteria. The <code>cache</code> argument
+ determines whether the objects' states should be cached in the
+ application's memory or if they should be returned by the database
+ system one by one as the iteration over the result progresses. The
+ result caching is discussed in detail in the next section.</p>
+
+ <p>When calling the <code>query()</code> function, we have to
+ explicitly specify the object type we are querying. For example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ result all (db.query&lt;person> ());
+ result johns (db.query&lt;person> (query::first == "John"));
+ </pre>
+
+ <p>Note that it is not required to explicitly create a named
+ query variable before executing it. For example, the following
+ two queries are equivalent:</p>
+
+ <pre class="cxx">
+ query q (query::first == "John");
+
+ result r1 (db.query&lt;person> (q));
+ result r1 (db.query&lt;person> (query::first == "John"));
+ </pre>
+
+ <p>Normally, we would create a named query instance if we are
+ planning to run the same query multiple times and would use the
+ in-line version for those that are executed only once (see also
+ <a href="#4.5">Section 4.5, "Prepared Queries"</a> for a more
+ optimal way to re-execute the same query multiple times). A named
+ query instance that does not have any by-reference parameters is
+ immutable and can be shared between multiple threads without
+ synchronization. On the other hand, a query instance with
+ by-reference parameters is modified every time it is executed.
+ If such a query is shared among multiple threads, then access
+ to this query instance must be synchronized from the execution
+ point and until the completion of the iteration over the result.</p>
+
+ <p>It is also possible to create queries from other queries by
+ combining them using logical operators. For example:</p>
+
+ <pre class="cxx">
+result
+find_minors (database&amp; db, const query&amp; name_query)
+{
+ return db.query&lt;person> (name_query &amp;&amp; query::age &lt; 18);
+}
+
+result r (find_minors (db, query::first == "John"));
+ </pre>
+
+ <p>The result of executing a query is zero, one, or more objects
+ matching the query criteria. The <code>query()</code> function
+ returns this result as an instance of the <code>odb::result</code>
+ class template, which provides a stream-like interface and is
+ discussed in detail in the next section.</p>
+
+ <p>In situations where we know that a query produces at most one
+ element, we can instead use the <code>database::query_one()</code> and
+ <code>database::query_value()</code> shortcut functions, for example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+
+ auto_ptr&lt;person> p (
+ db.query_one&lt;person> (
+ query::email == "jon@example.com"));
+ </pre>
+
+ <p>The shortcut query functions have the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ query_one ();
+
+ template &lt;typename T>
+ bool
+ query_one (T&amp;);
+
+ template &lt;typename T>
+ T
+ query_value ();
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ query_one (const odb::query&lt;T>&amp;);
+
+ template &lt;typename T>
+ bool
+ query_one (const odb::query&lt;T>&amp;, T&amp;);
+
+ template &lt;typename T>
+ T
+ query_value (const odb::query&lt;T>&amp;);
+ </pre>
+
+ <p>Similar to <code>query()</code>, the first three functions are used
+ to return the only persistent object of a given type stored in the
+ database. The second three versions use the passed query instance
+ to only return the object matching the query criteria.</p>
+
+ <p>Similar to the <code>database::find()</code> functions
+ (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
+ <code>query_one()</code> can either allocate a new instance of the
+ object class in the dynamic memory or it can load the object's state
+ into an existing instance. The <code>query_value()</code> function
+ allocates and returns the object by value.</p>
+
+ <p>The <code>query_one()</code> function allows us to determine
+ if the query result contains zero or one element. If no objects
+ matching the query criteria were found in the database, the
+ first version of <code>query_one()</code> returns the <code>NULL</code>
+ pointer while the second &mdash; <code>false</code>. If the second
+ version returns <code>false</code>, then the passed object
+ remains unchanged. For example:</p>
+
+ <pre class="cxx">
+ if (unique_ptr&lt;person> p = db.query_one&lt;person> (
+ query::email == "jon@example.com"))
+ {
+ ...
+ }
+
+ person p;
+ if (db.query_one&lt;person> (query::email == "jon@example.com", p))
+ {
+ ...
+ }
+ </pre>
+
+ <p>If the query executed using <code>query_one()</code> or
+ <code>query_value()</code> returns more than one element,
+ then these functions fail with an assertion. Additionally,
+ <code>query_value()</code> also fails with an assertion if
+ the query returned no elements.</p>
+
+ <p>Common situations where we can use the shortcut functions are a
+ query condition that uses a data member with the
+ <code>unique</code> constraint (at most one element returned;
+ see <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>)
+ as well as aggregate queries (exactly one element returned; see
+ <a href="#10">Chapter 10, "Views"</a>).</p>
+
+ <h2><a name="4.4">4.4 Query Result</a></h2>
+
+ <p>The <code>database::query()</code> function returns the result of
+ executing a query as an instance of the <code>odb::result</code>
+ class template, for example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ result johns (db.query&lt;person> (query::first == "John"));
+ </pre>
+
+ <p>It is best to view an instance of <code>odb::result</code>
+ as a handle to a stream, such as a socket stream. While we can
+ make a copy of a result or assign one result to another, the
+ two instances will refer to the same result stream. Advancing
+ the current position in one instance will also advance it in
+ another. The result instance is only usable within the transaction
+ it was created in. Trying to manipulate the result after the
+ transaction has terminated leads to undefined behavior.</p>
+
+ <p>The <code>odb::result</code> class template conforms to the
+ standard C++ sequence requirements and has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ class result
+ {
+ public:
+ typedef odb::result_iterator&lt;T> iterator;
+
+ public:
+ result ();
+
+ result (const result&amp;);
+
+ result&amp;
+ operator= (const result&amp;);
+
+ void
+ swap (result&amp;)
+
+ public:
+ iterator
+ begin ();
+
+ iterator
+ end ();
+
+ public:
+ void
+ cache ();
+
+ bool
+ empty () const;
+
+ std::size_t
+ size () const;
+ };
+}
+ </pre>
+
+ <p>The default constructor creates an empty result set. The
+ <code>cache()</code> function caches the returned objects'
+ state in the application's memory. We have already mentioned
+ result caching when we talked about query execution. As you
+ may remember the <code>database::query()</code> function
+ caches the result unless instructed not to by the caller.
+ The <code>cache()</code> function allows us to
+ cache the result at a later stage if it wasn't already
+ cached during query execution.</p>
+
+ <p>If the result is cached, the database state of all the returned
+ objects is stored in the application's memory. Note that
+ the actual objects are still only instantiated on demand
+ during result iteration. It is the raw database state that
+ is cached in memory. In contrast, for uncached results
+ the object's state is sent by the database system one object
+ at a time as the iteration progresses.</p>
+
+ <p>Uncached results can improve the performance of both the application
+ and the database system in situations where we have a large
+ number of objects in the result or if we will only examine
+ a small portion of the returned objects. However, uncached
+ results have a number of limitations. There can only be one
+ uncached result in a transaction. Creating another result
+ (cached or uncached) by calling <code>database::query()</code>
+ will invalidate the existing uncached result. Furthermore,
+ calling any other database functions, such as <code>update()</code>
+ or <code>erase()</code> will also invalidate the uncached result.
+ It also follows that uncached results cannot be used on objects
+ with containers (<a href="#5">Chapter 5, "Containers"</a>) since
+ loading a container would invalidate the uncached result.</p>
+
+ <p>The <code>empty()</code> function returns <code>true</code> if
+ there are no objects in the result and <code>false</code> otherwise.
+ The <code>size()</code> function can only be called for cached results.
+ It returns the number of objects in the result. If we call this
+ function on an uncached result, the <code>odb::result_not_cached</code>
+ exception is thrown.</p>
+
+ <p>To iterate over the objects in a result we use the
+ <code>begin()</code> and <code>end()</code> functions
+ together with the <code>odb::result&lt;T>::iterator</code>
+ type, for example:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ ...
+ }
+ </pre>
+
+ <p>In C++11 we can use the <code>auto</code>-typed variabe instead
+ of spelling the iterator type explicitly, for example:</p>
+
+ <pre class="cxx">
+ for (auto i (r.begin ()); i != r.end (); ++i)
+ {
+ ...
+ }
+ </pre>
+
+ <p>The C++11 range-based <code>for</code>-loop can be used to further
+ simplify the iteration:</p>
+
+ <pre class="cxx">
+ for (person&amp; p: r)
+ {
+ ...
+ }
+ </pre>
+
+ <p>The result iterator is an input iterator which means that the
+ only two position operations that it supports are to move to the
+ next object and to determine whether the end of the result stream
+ has been reached. In fact, the result iterator can only be in two
+ states: the current position and the end position. If we have
+ two iterators pointing to the current position and then we
+ advance one of them, the other will advance as well. This,
+ for example, means that it doesn't make sense to store an
+ iterator that points to some object of interest in the result
+ stream with the intent of dereferencing it after the iteration
+ is over. Instead, we would need to store the object itself. We
+ also cannot iterate over the same result multiple times without
+ re-executing the query.</p>
+
+ <p>The result iterator has the following dereference functions
+ that can be used to access the pointed-to object:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ class result_iterator
+ {
+ public:
+ T*
+ operator-> () const;
+
+ T&amp;
+ operator* () const;
+
+ typename object_traits&lt;T>::pointer_type
+ load ();
+
+ void
+ load (T&amp; x);
+
+ typename object_traits&lt;T>::id_type
+ id ();
+ };
+}
+ </pre>
+
+ <p>When we call the <code>*</code> or <code>-></code> operator,
+ the iterator will allocate a new instance of the object class
+ in the dynamic memory, load its state from the database
+ state, and return a reference or pointer to the new instance. The
+ iterator maintains the ownership of the returned object and will
+ return the same pointer for subsequent calls to either of these
+ operators until it is advanced to the next object or we call
+ the first <code>load()</code> function (see below). For example:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end ();)
+ {
+ cout &lt;&lt; i->last () &lt;&lt; endl; // Create an object.
+ person&amp; p (*i); // Reference to the same object.
+ cout &lt;&lt; p.age () &lt;&lt; endl;
+ ++i; // Free the object.
+ }
+ </pre>
+
+ <p>The overloaded <code>result_iterator::load()</code> functions are
+ similar to <code>database::load()</code>. The first function
+ returns a dynamically allocated instance of the current
+ object. As an optimization, if the iterator already owns an object
+ as a result of an earlier
+ call to the <code>*</code> or <code>-></code> operator, then it
+ relinquishes the ownership of this object and returns it instead.
+ This allows us to write code like this without worrying about
+ a double allocation:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (i->last == "Doe")
+ {
+ auto_ptr p (i.load ());
+ ...
+ }
+ }
+ </pre>
+
+ <p>Note, however, that because of this optimization, a subsequent
+ to <code>load()</code> call to the <code>*</code> or <code>-></code>
+ operator results in the allocation of a new object.</p>
+
+ <p>The second <code>load()</code> function allows
+ us to load the current object's state into an existing instance.
+ For example:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ person p;
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ i.load (p);
+ cout &lt;&lt; p.last () &lt;&lt; endl;
+ cout &lt;&lt; i.age () &lt;&lt; endl;
+ }
+ </pre>
+
+ <p>The <code>id()</code> function return the object id of the current
+ object. While we can achieve the same by loading the object and getting
+ its id, this function is more efficient since it doesn't actually
+ create the object. This can be useful when all we need is the object's
+ identifier. For example:</p>
+
+ <pre class="cxx">
+ std::set&lt;unsigned long> set = ...; // Persons of interest.
+
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (set.find (i.id ()) != set.end ()) // No object loaded.
+ {
+ cout &lt;&lt; i->first () &lt;&lt; endl; // Object loaded.
+ }
+ }
+ </pre>
+
+ <h2><a name="4.5">4.5 Prepared Queries</a></h2>
+
+ <p>Most modern relational database systems have the notion of a prepared
+ statement. Prepared statements allow us to perform the potentially
+ expensive tasks of parsing SQL, preparing the query execution
+ plan, etc., once and then executing the same query multiple
+ times, potentially using different values for parameters in
+ each execution.</p>
+
+ <p>In ODB all the non-query database operations such as
+ <code>persist()</code>, <code>load()</code>, <code>update()</code>,
+ etc., are implemented in terms of prepared statements that are cached
+ and reused. While the <code>query()</code>, <code>query_one()</code>,
+ and <code>query_value()</code> database operations also use prepared
+ statements, these statements are not cached or reused by default since
+ ODB has no knowledge of whether a query will be executed multiple times
+ or only once. Instead, ODB provides a mechanism, called prepared queries,
+ that allows us to prepare a query once and execute it multiple
+ times. In other words, ODB prepared queries are a thin wrapper
+ around the underlying database's prepared statement functionality.</p>
+
+ <p>In most cases ODB shields the application developer from database
+ connection management and multi-threading issues. However, when it
+ comes to prepared queries, a basic understanding of how ODB manages
+ these aspects is required. Conceptually, the <code>odb::database</code>
+ class represents a specific database, that is, a data store. However,
+ underneath, it maintains one or more connections to this database.
+ A connection can be used only by a single thread at a time. When
+ we start a transaction (by calling <code>database::begin()</code>),
+ the transaction instance obtains a connection and holds on to it
+ until the transaction is committed or rolled back. During this time
+ no other thread can use this connection. When the transaction
+ releases the connection, it may be closed or reused by another
+ transaction in this or another thread. What exactly happens to
+ a connection after it has been released depends on the connection
+ factory that is used by the <code>odb::database</code> instance.
+ For more information on connection factories, refer to
+ <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>A query prepared on one connection cannot be executed on another.
+ In other words, a prepared query is associated with the connection.
+ One important implication of this restriction is that we cannot
+ prepare a query in one transaction and then try to execute it
+ in another without making sure that both transactions use the
+ same connection.</p>
+
+ <p>To enable the prepared query functionality we need to specify
+ the <code>--generate-prepared</code> ODB compiler option. If
+ we are planning to always prepare our queries, then we can
+ disable the once-off query execution support by also specifying
+ the <code>--omit-unprepared</code> option.</p>
+
+ <p>To prepare a query we use the <code>prepare_query()</code> function
+ template. This function can be called on both the <code>odb::database</code>
+ and <code>odb::connection</code> instances. The <code>odb::database</code>
+ version simply obtains the connection used by the currently active
+ transaction and calls the corresponding <code>odb::connection</code>
+ version. If no transaction is currently active, then this function
+ throws the <code>odb::not_in_transaction</code> exception
+ (<a href="#3.5">Section 3.5, "Transactions"</a>). The
+ <code>prepare_query()</code> function has the following signature:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ prepared_query&lt;T>
+ prepare_query (const char* name, const odb::query&lt;T>&amp;);
+ </pre>
+
+ <p>The first argument to the <code>prepare_query()</code> function is
+ 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 <code>"<i>object</i>_query"</code> (for example,
+ <code>"person_query"</code>) is reserved for the once-off queries
+ executed by the <code>database::query()</code> function. Note that
+ the <code>prepare_query()</code> function makes only a shallow copy
+ of this argument, which means that the name must be valid for the
+ lifetime of the returned <code>prepared_query</code> instance.</p>
+
+ <p>The second argument to the <code>prepare_query()</code> function
+ is the query criteria. It has the same semantics as in the
+ <code>query()</code> function discussed in <a href="#4.3">Section
+ 4.3, "Executing a Query"</a>. Similar to <code>query()</code>, we
+ also have to explicitly specify the object type that we will be
+ querying. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::prepared_query&lt;person> prep_query;
+
+prep_query pq (
+ db.prepare_query&lt;person> ("person-age-query", query::age > 50));
+ </pre>
+
+ <p>The result of executing the <code>prepare_query()</code> function is
+ the <code>prepared_query</code> instance that represent the prepared
+ query. It is best to view <code>prepared_query</code> as a handle to
+ the underlying prepared statement. While we can make a copy of it or
+ assign one <code>prepared_query</code> to another, the two instances
+ will refer to the same prepared statement. Once the last instance of
+ <code>prepared_query</code> referencing a specific prepared statement
+ is destroyed, this statement is released. The <code>prepared_query</code>
+ class template has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ struct prepared_query
+ {
+ prepared_query ();
+
+ prepared_query (const prepared_query&amp;)
+ prepared_query&amp; operator= (const prepared_query&amp;)
+
+ result&lt;T>
+ execute (bool cache = true);
+
+ typename object_traits&lt;T>::pointer_type
+ execute_one ();
+
+ bool
+ execute_one (T&amp; object);
+
+ T
+ execute_value ();
+
+ const char*
+ name () const;
+
+ statement&amp;
+ statement () const;
+
+ operator unspecified_bool_type () const;
+ };
+}
+ </pre>
+
+ <p>The default constructor creates an empty <code>prepared_query</code>
+ instance, that is, an instance that does not reference a prepared
+ statement and therefore cannot be executed. The only way to create
+ a non-empty prepared query is by calling the <code>prepare_query()</code>
+ function discussed above. To test whether the prepared query is empty,
+ we can use the implicit conversion operator to a boolean type. For
+ example:</p>
+
+ <pre class="cxx">
+ prepared_query&lt;person> pq;
+
+ if (pq)
+ {
+ // Not empty.
+ ...
+ }
+ </pre>
+
+ <p>The <code>execute()</code> function executes the query and returns
+ the result instance. The <code>cache</code> argument indicates
+ whether the result should be cached and has the same semantics
+ as in the <code>query()</code> function. In fact, conceptually,
+ <code>prepare_query()</code> and <code>execute()</code> are just
+ the <code>query()</code> function split into two:
+ <code>prepare_query()</code> takes the first
+ <code>query()</code> argument (the query condition) while
+ <code>execute()</code> takes the second (the cache flag). Note
+ also that re-executing a prepared query invalidates the
+ previous execution result, whether cached or uncached. </p>
+
+ <p>The <code>execute_one()</code> and <code>execute_value()</code>
+ functions can be used as shortcuts to execute a query that is
+ known to return at most one or exactly one object, respectively.
+ The arguments and return values in these functions have the same
+ semantics as in <code>query_one()</code> and <code>query_value()</code>.
+ And similar to <code>execute()</code> above, <code>prepare_query()</code>
+ and <code>execute_one/value()</code> can be seen as the
+ <code>query_one/value()</code> function split into two:
+ <code>prepare_query()</code> takes the first
+ <code>query_one/value()</code> argument (the query condition) while
+ <code>execute_one/value()</code> takes the second argument (if any)
+ and returns the result. Note also that <code>execute_one/value()</code>
+ never caches its result but invalidates the result of any previous
+ <code>execute()</code> call on the same prepared query.</p>
+
+ <p>The <code>name()</code> function returns the prepared query name.
+ This is the same name as was passed as the first argument in the
+ <code>prepare_query()</code> call. The <code>statement()</code>
+ function returns a reference to the underlying prepared statement.
+ Note also that calling any of these functions on an empty
+ <code>prepared_query</code> instance results in undefined behavior.</p>
+
+ <p>The simplest use-case for a prepared query is the need to
+ execute the same query multiple times within a single transaction.
+ Consider the following example that queries for people that are older
+ than a number of different ages. This and subsequent code fragments
+ are taken from the <code>prepared</code> example in the
+ <code>odb-examples</code> package.</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::prepared_query&lt;person> prep_query;
+typedef odb::result&lt;person> result;
+
+transaction t (db.begin ());
+
+unsigned short age;
+query q (query::age > query::_ref (age));
+prep_query pq (db.prepare_query&lt;person> ("person-age-query", q));
+
+for (age = 90; age > 40; age -= 10)
+{
+ result r (pq.execute ());
+ ...
+}
+
+t.commit ();
+ </pre>
+
+ <p>Another scenario is the need to reuse the same query in multiple
+ transactions that are executed at once. As was mentioned above,
+ in this case we need to make sure that the prepared query and
+ all the transactions use the same connection. Consider an
+ alternative version of the above example that executes each
+ query in a separate transaction:</p>
+
+ <pre class="cxx">
+connection_ptr conn (db.connection ());
+
+unsigned short age;
+query q (query::age > query::_ref (age));
+prep_query pq (conn->prepare_query&lt;person> ("person-age-query", q));
+
+for (age = 90; age > 40; age -= 10)
+{
+ transaction t (conn->begin ());
+
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+}
+ </pre>
+
+
+ <p>Note that with this approach we hold on to the database connection
+ until all the transactions involving the prepared query are
+ executed. In particular, this means that while we are busy, the
+ connection cannot be reused by another thread. Therefore, this
+ approach is only recommended if all the transactions are executed
+ close to each other. Also note that an uncached (see below)
+ prepared query is invalidated once we release the connection
+ on which it was prepared.</p>
+
+ <p>If we need to reuse a prepared query in transactions that are
+ executed at various times, potentially in different threads, then
+ the recommended approach is to cache the prepared query on the
+ connection. To support this functionality the <code>odb::database</code>
+ and <code>odb::connection</code> classes provide the following
+ function templates. Similar to <code>prepare_query()</code>,
+ the <code>odb::database</code> versions of the below
+ functions call the corresponding <code>odb::connection</code>
+ versions using the currently active transaction to resolve
+ the connection.</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ cache_query (const prepared_query&lt;T>&amp;);
+
+ template &lt;typename T, typename P>
+ void
+ cache_query (const prepared_query&lt;T>&amp;,
+ std::[auto|unique]_ptr&lt;P> params);
+
+ template &lt;typename T>
+ prepared_query&lt;T>
+ lookup_query (const char* name);
+
+ template &lt;typename T, typename P>
+ prepared_query&lt;T>
+ lookup_query (const char* name, P*&amp; params);
+ </pre>
+
+ <p>The <code>cache_query()</code> function caches the passed prepared
+ query on the connection. The second overloaded version of
+ <code>cache_query()</code> also takes a pointer to the
+ by-reference query parameters. In C++98/03 it should be
+ <code>std::auto_ptr</code> while in C++11 <code>std::auto_ptr</code>
+ or <code>std::unique_ptr</code> can be used. The
+ <code>cache_query()</code> function assumes ownership of the
+ passed <code>params</code> argument. If a prepared query
+ with the same name is already cached on this connection,
+ then the <code>odb::prepared_already_cached</code> exception
+ is thrown.</p>
+
+ <p>The <code>lookup_query()</code> function looks up a previously
+ cached prepared query given its name. The second overloaded
+ version of <code>lookup_query()</code> also returns a pointer
+ to the by-reference query parameters. If a prepared query
+ with this name has not been cached, then an empty
+ <code>prepared_query</code> instance is returned. If a
+ prepared query with this name has been cached but either
+ the object type or the parameters type does not match
+ that which was cached, then the <code>odb::prepared_type_mismatch</code>
+ exception is thrown.</p>
+
+ <p>As a first example of the prepared query cache functionality,
+ consider the case that does not use any by-reference parameters:</p>
+
+ <pre class="cxx">
+for (unsigned short i (0); i &lt; 5; ++i)
+{
+ transaction t (db.begin ());
+
+ prep_query pq (db.lookup_query&lt;person> ("person-age-query"));
+
+ if (!pq)
+ {
+ pq = db.prepare_query&lt;person> (
+ "person-val-age-query", query::age > 50);
+ db.cache_query (pq);
+ }
+
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+
+ // Do some other work.
+ //
+ ...
+}
+ </pre>
+
+ <p>The following example shows how to do the same but for a query that
+ includes by-reference parameters. In this case the parameters are
+ cached together with the prepared query.</p>
+
+ <pre class="cxx">
+for (unsigned short age (90); age > 40; age -= 10)
+{
+ transaction t (db.begin ());
+
+ unsigned short* age_param;
+ prep_query pq (
+ db.lookup_query&lt;person> ("person-age-query", age_param));
+
+ if (!pq)
+ {
+ auto_ptr&lt;unsigned short> p (new unsigned short);
+ age_param = p.get ();
+ query q (query::age > query::_ref (*age_param));
+ pq = db.prepare_query&lt;person> ("person-age-query", q);
+ db.cache_query (pq, p); // Assumes ownership of p.
+ }
+
+ *age_param = age; // Initialize the parameter.
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+
+ // Do some other work.
+ //
+ ...
+}
+ </pre>
+
+ <p>As is evident from the above examples, when we use a prepared
+ query cache, each transaction that executes a query must also
+ include code that prepares and caches this query if it hasn't already
+ been done. If a prepared query is used in a single place in the
+ application, then this is normally not an issue since all the
+ relevant code is kept in one place. However, if the same query
+ is used in several different places in the application, then
+ we may end up duplicating the same preparation and caching
+ code, which makes it hard to maintain.</p>
+
+ <p>To resolve this issue ODB allows us to register a prepared
+ query factory that will be called to prepare and cache a
+ query during the call to <code>lookup_query()</code>. To
+ register a factory we use the <code>database::query_factory()</code>
+ function. In C++98/03 it has the following signature:</p>
+
+ <pre class="cxx">
+ void
+ query_factory (const char* name,
+ void (*factory) (const char* name, connection&amp;));
+ </pre>
+
+ <p>While in C++11 it uses the <code>std::function</code> class
+ template:</p>
+
+ <pre class="cxx">
+ void
+ query_factory (const char* name,
+ std::function&lt;void (const char* name, connection&amp;)>);
+ </pre>
+
+ <p>The first argument to the <code>query_factory()</code> function is
+ the prepared query name that this factory will be called to prepare
+ and cache. An empty name is treated as a fallback wildcard factory
+ that is capable of preparing any query. The second argument is the
+ factory function or, in C++11, function object or lambda.</p>
+
+ <p>The example fragment shows how we can use the prepared query
+ factory:</p>
+
+ <pre class="cxx">
+struct params
+{
+ unsigned short age;
+ string first;
+};
+
+static void
+query_factory (const char* name, connection&amp; c)
+{
+ auto_ptr&lt;params> p (new params);
+ query q (query::age > query::_ref (p->age) &amp;&amp;
+ query::first == query::_ref (p->first));
+ prep_query pq (c.prepare_query&lt;person> (name, q));
+ c.cache_query (pq, p);
+}
+
+db.query_factory ("person-age-name-query", &amp;query_factory);
+
+for (unsigned short age (90); age > 40; age -= 10)
+{
+ transaction t (db.begin ());
+
+ params* p;
+ prep_query pq (db.lookup_query&lt;person> ("person-age-name-query", p));
+ assert (pq);
+
+ p->age = age;
+ p->first = "John";
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+}
+ </pre>
+
+ <p>In C++11 we could have instead used a lambda function as well as
+ <code>unique_ptr</code> rather than <code>auto_ptr</code>:</p>
+
+ <pre>
+db.query_factory (
+ "person-age-name-query",
+ [] (const char* name, connection&amp; c)
+ {
+ unique_ptr&lt;params> p (new params);
+ query q (query::age > query::_ref (p->age) &amp;&amp;
+ query::first == query::_ref (p->first));
+ prep_query pq (c.prepare_query&lt;person> (name, q));
+ c.cache_query (pq, std::move (p));
+ });
+ </pre>
+
+ Note that the <code>database::query_factory()</code> function is not
+ thread-safe and should be called before starting any threads that may
+ require this functionality. Normally, all the prepared query factories
+ are registered as part of the database instance creation.
+
+ <!-- CHAPTER -->
+
+ <hr class="page-break"/>
+ <h1><a name="5">5 Containers</a></h1>
+
+ <p>The ODB runtime library provides built-in persistence support for all the
+ commonly used standard C++98/03 containers, namely,
+ <code>std::vector</code>, <code>std::list</code>, <code>std::deque</code>,
+ <code>std::set</code>, <code>std::multiset</code>, <code>std::map</code>, and
+ <code>std::multimap</code> as well as C++11 <code>std::array</code>,
+ <code>std::forward_list</code>, <code>std::unordered_set</code>,
+ <code>std::unordered_multiset</code>, <code>std::unordered_map</code>,
+ and <code>std::unordered_multimap</code>.
+ Plus, ODB profile libraries, that are
+ available for commonly used frameworks and libraries (such as Boost and
+ Qt), provide persistence support for containers found in these frameworks
+ and libraries (<a href="#III">Part III, "Profiles"</a>). Both the
+ ODB runtime library and profile libraries also provide a number of
+ change-tracking container equivalents which can be used to minimize
+ the number of database operations necessary to synchronize the container
+ state with the database (<a href="#5.4">Section 5.4, "Change-Tracking
+ Containers"</a>). It is also easy to persist custom container types
+ as discussed later in <a href="#5.5">Section 5.5, "Using Custom
+ Containers"</a>.</p>
+
+ <p>We don't need to do anything special to declare a member of a
+ container type in a persistent class. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>The complete version of the above code fragment and the other code
+ samples presented in this chapter can be found in the <code>container</code>
+ example in the <code>odb-examples</code> package.</p>
+
+ <p>A data member in a persistent class that is of a container type
+ behaves like a value type. That is, when an object is made persistent,
+ the elements of the container are stored in the database. Similarly,
+ when a persistent object is loaded from the database, the contents
+ of the container are automatically loaded as well. A data member
+ of a container type can also use a smart pointer, as discussed
+ in <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
+ Value Semantics"</a>.</p>
+
+ <p>While an ordinary member is mapped to one or more columns in the
+ object's table, a member of a container type is mapped to a separate
+ table. The exact schema of such a table depends on the kind of
+ container. ODB defines the following container kinds: ordered,
+ set, multiset, map, and multimap. The container kinds and the
+ contents of the tables to which they are mapped are discussed
+ in detail in the following sections.</p>
+
+ <p>Containers in ODB can contain simple value types (<a href="#7.1">Section
+ 7.1, "Simple Value Types"</a>), composite value types
+ (<a href="#7.2">Section 7.2, "Composite Value Types"</a>), and pointers
+ to objects (<a href="#6">Chapter 6, "Relationships"</a>). Containers of
+ containers, either directly or indirectly via a composite value
+ type, are not allowed. A key in a map or multimap container can
+ be a simple or composite value type but not a pointer to an object.
+ An index in the ordered container should be a simple integer value
+ type.</p>
+
+ <p>The value type in the ordered, set, and map containers as well as
+ the key type in the map containers should be default-constructible.
+ The default constructor in these types can be made private in which
+ case the <code>odb::access</code> class should be made a friend of
+ the value or key type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+public:
+ name (const std::string&amp;, const std::string&amp;);
+ ...
+private:
+ friend class odb::access;
+ name ();
+ ...
+};
+
+#pragma db object
+class person
+{
+ ...
+private:
+ std::vector&lt;name> aliases_;
+ ...
+};
+ </pre>
+
+
+ <h2><a name="5.1">5.1 Ordered Containers</a></h2>
+
+ <p>In ODB an ordered container is any container that maintains (explicitly
+ or implicitly) an order of its elements in the form of an integer index.
+ Standard C++ containers that are ordered include <code>std::vector</code>
+ <code>std::list</code>, and <code>std::deque</code> as well as C++11 <code>std::array</code> and
+ <code>std::forward_list</code>. While elements in <code>std::set</code>
+ are also kept in a specific order, this order is not based on an
+ integer index but rather on the relationship between elements. As
+ a result, <code>std::set</code> is not considered an ordered
+ container for the purpose of persistence.</p>
+
+ <p>The database table for an ordered container consists of at least
+ three columns. The first column contains the object id of a
+ persistent class instance of which the container is a member.
+ The second column contains the element index within a container.
+ And the last column contains the element value. If the object
+ id or element value are composite, then, instead of a single
+ column, they can occupy multiple columns. For an ordered
+ container table the ODB compiler also defines two indexes:
+ one for the object id column(s) and the other for the index
+ column. Refer to <a href="#14.7">Section 14.7, "Index Definition
+ Pragmas"</a> for more information on how to customize these
+ indexes.</p>
+
+ <p>Consider the following persistent object as an example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db id auto
+ unsigned long id_;
+
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>The resulting database table (called <code>person_nicknames</code>) will
+ contain the object id column of type <code>unsigned&nbsp;long</code>
+ (called <code>object_id</code>), the index column of an integer type
+ (called <code>index</code>), and the value column of type
+ <code>std::string</code> (called <code>value</code>).</p>
+
+ <p>A number of ODB pragmas allow us to customize the table name, column
+ names, and native database types of an ordered container both, on
+ the per-container and per-member basis. For more information on
+ these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>. The following example shows some of the possible
+ customizations:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db table("nicknames") \
+ id_column("person_id") \
+ index_type("SMALLINT UNSIGNED") \
+ index_column("nickname_number") \
+ value_type("VARCHAR(255)") \
+ value_column("nickname")
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>While the C++ container used in a persistent class may be ordered,
+ sometimes we may wish to store such a container in the database without
+ the order information. In the example above, for instance, the order
+ of person's nicknames is probably not important. To instruct the ODB
+ compiler to ignore the order in ordered containers we can use the
+ <code>db&nbsp;unordered</code> pragma (<a href="#14.3.9">Section 14.3.9,
+ "<code>unordered</code>"</a>, <a href="#14.4.19">Section 14.4.19,
+ "<code>unordered</code>"</a>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db unordered
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>The table for an ordered container that is marked unordered won't
+ have the index column and the order in which elements are retrieved
+ from the database may not be the same as the order in which they
+ were stored.</p>
+
+ <h2><a name="5.2">5.2 Set and Multiset Containers</a></h2>
+
+ <p>In ODB set and multiset containers (referred to as just set
+ containers) are associative containers that contain elements
+ based on some relationship between them. A set container may
+ or may not guarantee a particular order of the elements that
+ it stores. Standard C++ containers that are considered set
+ containers for the purpose of persistence include
+ <code>std::set</code> and <code>std::multiset</code> as well
+ as C++11 <code>std::unordered_set</code> and
+ <code>std::unordered_multiset</code>.</p>
+
+ <p>The database table for a set container consists of at least
+ two columns. The first column contains the object id of a
+ persistent class instance of which the container is a member.
+ And the second column contains the element value. If the object
+ id or element value are composite, then, instead of a single
+ column, they can occupy multiple columns. ODB compiler also
+ defines an index on a set container table for the object id
+ column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
+ Pragmas"</a> for more information on how to customize this
+ index.</p>
+
+ <p>Consider the following persistent object as an example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db id auto
+ unsigned long id_;
+
+ std::set&lt;std::string> emails_;
+ ...
+};
+ </pre>
+
+ <p>The resulting database table (called <code>person_emails</code>) will
+ contain the object id column of type <code>unsigned&nbsp;long</code>
+ (called <code>object_id</code>) and the value column of type
+ <code>std::string</code> (called <code>value</code>).</p>
+
+ <p>A number of ODB pragmas allow us to customize the table name,
+ column names, and native database types of a set container, both on
+ the per-container and per-member basis. For more information on
+ these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>. The following example shows some of the possible
+ customizations:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db table("emails") \
+ id_column("person_id") \
+ value_type("VARCHAR(255)") \
+ value_column("email")
+ std::set&lt;std::string> emails_;
+ ...
+};
+ </pre>
+
+ <h2><a name="5.3">5.3 Map and Multimap Containers</a></h2>
+
+ <p>In ODB map and multimap containers (referred to as just map
+ containers) are associative containers that contain key-value
+ elements based on some relationship between keys. A map container
+ may or may not guarantee a particular order of the elements that
+ it stores. Standard C++ containers that are considered map
+ containers for the purpose of persistence include
+ <code>std::map</code> and <code>std::multimap</code> as well
+ as C++11 <code>std::unordered_map</code> and
+ <code>std::unordered_multimap</code>.</p>
+
+ <p>The database table for a map container consists of at least
+ three columns. The first column contains the object id of a
+ persistent class instance of which the container is a member.
+ The second column contains the element key. And the last column
+ contains the element value. If the object id, element key, or
+ element value are composite, then instead of a single column
+ they can occupy multiple columns. ODB compiler also
+ defines an index on a map container table for the object id
+ column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
+ Pragmas"</a> for more information on how to customize this
+ index.</p>
+
+ <p>Consider the following persistent object as an example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db id auto
+ unsigned long id_;
+
+ std::map&lt;unsigned short, float> age_weight_map_;
+ ...
+};
+ </pre>
+
+ <p>The resulting database table (called <code>person_age_weight_map</code>)
+ will contain the object id column of type <code>unsigned&nbsp;long</code>
+ (called <code>object_id</code>), the key column of type
+ <code>unsigned short</code> (called <code>key</code>), and the value
+ column of type <code>float</code> (called <code>value</code>).</p>
+
+ <p>A number of ODB pragmas allow us to customize the table name,
+ column names, and native database types of a map container, both on
+ the per-container and per-member basis. For more information on
+ these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>. The following example shows some of the possible
+ customizations:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db table("weight_map") \
+ id_column("person_id") \
+ key_type("INT UNSIGNED") \
+ key_column("age") \
+ value_type("DOUBLE") \
+ value_column("weight")
+ std::map&lt;unsigned short, float> age_weight_map_;
+ ...
+};
+ </pre>
+
+ <h2><a name="5.4">5.4 Change-Tracking Containers</a></h2>
+
+ <p>When a persistent object containing one of the standard containers
+ is updated in the database, ODB has no knowledge of which elements
+ were inserted, erased, or modified. As a result, ODB has no choice
+ but to assume the whole container has changed and update the state
+ of every single element. This can result in a significant overhead
+ if a container contains a large number of elements and we only
+ changed a small subset of them.</p>
+
+ <p>To eliminate this overhead, ODB provides a notion of <em>change-tracking
+ containers</em>. A change-tracking container, besides containing
+ its elements, just like an ordinary container, also includes the
+ change state for each element. When it is time to update such a
+ container in the database, ODB can use this change information to
+ perform a minimum number of database operations necessary to
+ synchronize the container state with the database.</p>
+
+ <p>The current version of the ODB runtime library provides a change-tracking
+ equivalent of <code>std::vector</code> (<a href="#5.4.1">Section 5.4.1,
+ "Change-Tracking <code>vector</code>"</a>) with support for other
+ standard container equivalents planned for future releases. ODB
+ profile libraries also provide change-tracking equivalents for some
+ containers found in the corresponding frameworks and libraries
+ (<a href="#III">Part III, "Profiles"</a>).</p>
+
+ <p>A change-tracking container equivalent can normally be used as a drop-in
+ replacement for an ordinary container except for a few minor
+ interface differences (discussed in the corresponding sub-sections).
+ In particular, we don't need to do anything extra to effect
+ change tracking. ODB will automatically start, stop, and reset
+ change tracking when necessary. The following example illustrates
+ this point using <code>odb::vector</code> as a replacement for
+ <code>std::vector</code>.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ odb::vector&lt;std::string> names;
+};
+
+person p; // No change tracking (not persistent).
+p.names.push_back ("John Doe");
+
+{
+ transaction t (db.begin ());
+ db.persist (p); // Start change tracking (persistent).
+ t.commit ();
+}
+
+p.names.push_back ("Johnny Doo");
+
+{
+ transaction t (db.begin ());
+ db.update (p); // One INSERT; reset change state.
+ t.commit ();
+}
+
+p.names.modify (0) = "Doe, John"; // Instead of operator[].
+p.names.pop_back ();
+
+{
+ transaction t (db.begin ());
+ db.update (p); // One UPDATE, one DELETE; reset change state.
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+ auto_ptr&lt;person> p1 (db.load&lt;person> (...)); // Start change tracking.
+ p1->names.insert (p1->names.begin (), "Joe Do");
+ db.update (*p1); // One UPDATE, one INSERT; reset change state.
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+ db.erase (p); // One DELETE; stop change tracking (not persistent).
+ t.commit ();
+}
+ </pre>
+
+ <p>One interesting aspect of change tracking is what happens when a
+ transaction that contains an update is later rolled back. In this
+ case, while the change-tracking container has reset the change
+ state (after update), actual changes were not committed to the
+ database. Change-tracking containers handle this case by
+ automatically registering a rollback callback and then, if it is
+ called, marking the container as "completely changed". In this
+ state, the container no longer tracks individual element changes
+ and, when updated, falls back to the complete state update, just
+ like an ordinary container. The following example illustrates
+ this point:</p>
+
+ <pre class="cxx">
+person p;
+p.names.push_back ("John Doe");
+
+{
+ transaction t (db.begin ());
+ db.persist (p); // Start change tracking (persistent).
+ t.commit ();
+}
+
+p.names.push_back ("Johnny Doo");
+
+for (;;)
+{
+ try
+ {
+ transaction t (db.begin ());
+
+ // First try: one INSERT.
+ // Next try: one DELETE, two INSERTs.
+ //
+ db.update (p); // Reset change state.
+
+ t.commit (); // If throws (rollback), mark as completely changed.
+ break;
+ }
+ catch (const odb::recoverable&amp;)
+ {
+ continue;
+ }
+}
+ </pre>
+
+ <p>For the interaction of change-tracking containers with change-updated
+ object sections, refer to <a href="#9.4">Section 9.4, "Sections and
+ Change-Tracking Containers"</a>. Note also that change-tracking
+ containers cannot be accessed with by-value accessors
+ (<a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>)
+ since in certain situations such access may involve a
+ modification of the container (for example, clearing the change
+ flag after update).</p>
+
+ <h3><a name="5.4.1">5.4.1 Change-Tracking <code>vector</code></a></h3>
+
+ <p>Class template <code>odb::vector</code>, defined in
+ <code>&lt;odb/vector.hxx></code>, is a change-tracking
+ equivalent for <code>std::vector</code>. It
+ is implemented in terms of <code>std::vector</code> and is
+ implicit-convertible to and implicit-constructible from
+ <code>const std::vector&amp;</code>. In particular, this
+ means that we can use <code>odb::vector</code> instance
+ anywhere <code>const std::vector&amp;</code> is
+ expected. In addition, <code>odb::vector</code> constant
+ iterator (<code>const_iterator</code>) is the same type as
+ that of <code>std::vector</code>.</p>
+
+ <p><code>odb::vector</code> incurs 2-bit per element overhead
+ in order to store the change state. It cannot
+ be stored unordered in the database (<a href="#14.4.19">Section
+ 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
+ side of a relationship (<a href="#6.2">6.2 "Bidirectional
+ Relationships"</a>). In this case, no change tracking is performed
+ since no state for such a container is stored in the database.</p>
+
+ <p>The number of database operations required to update the state
+ of <code>odb::vector</code> corresponds well to the complexity
+ of <code>std::vector</code> functions. In particular, adding or
+ removing an element from the back of the vector (for example,
+ with <code>push_back()</code> and <code>pop_back()</code>),
+ requires only a single database statement execution. In contrast,
+ inserting or erasing an element somewhere in the middle of the
+ vector will require a database statement for every element that
+ follows it.</p>
+
+ <p><code>odb::vector</code> replicates most of the <code>std::vector</code>
+ interface as defined in both C++98/03 and C++11 standards. However,
+ functions and operators that provide direct write access to
+ the elements had to be altered or disabled in order to support
+ change tracking. Additional functions used to interface with
+ <code>std::vector</code> and to control the change tracking state
+ were also added. The following listing summarizes the differences
+ between the <code>odb::vector</code> and <code>std::vector</code>
+ interfaces. Any <code>std::vector</code> function or operator
+ not mentioned in this listing has exactly the same signature
+ and semantics in <code>odb::vector</code>. Functions and
+ operators that were disabled are shown as commented out and
+ are followed by functions/operators that replace them.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;class T, class A = std::allocator&lt;T> >
+ class vector
+ {
+ ...
+
+ // Element access.
+ //
+
+ //reference operator[] (size_type);
+ reference modify (size_type);
+
+ //reference at (size_type);
+ reference modify_at (size_type);
+
+ //reference front ();
+ reference modify_front ();
+
+ //reference back ();
+ reference modify_back ();
+
+ //T* data () noexcept;
+ T* modify_data () noexcept; // C++11 only.
+
+ // Iterators.
+ //
+ typedef typename std::vector&lt;T, A>::const_iterator const_iterator;
+
+ class iterator
+ {
+ ...
+
+ // Element Access.
+ //
+
+ //reference operator* () const;
+ const_reference operator* () const;
+ reference modify () const;
+
+ //pointer operator-> () const;
+ const_pointer operator-> () const;
+
+ //reference operator[] (difference_type);
+ const_reference operator[] (difference_type);
+ reference modify (difference_type) const;
+
+ // Interfacing with std::vector::iterator.
+ //
+ typename std::vector&lt;T, A>::iterator base () const;
+ };
+
+ // Return std::vector iterators. The begin() functions mark
+ // all the elements as modified.
+ //
+ typename std::vector&lt;T, A>::iterator mbegin ();
+ typename std::vector&lt;T, A>::iterator mend ();
+ typename std::vector&lt;T, A>::reverse_iterator mrbegin ();
+ typename std::vector&lt;T, A>::reverse_iterator mrend ();
+
+ // Interfacing with std::vector.
+ //
+ vector (const std::vector&lt;T, A>&amp;);
+ vector (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
+
+ vector&amp; operator= (const std::vector&lt;T, A>&amp;);
+ vector&amp; operator= (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
+
+ operator const std::vector&lt;T, A>&amp; () const;
+ std::vector&lt;T, A>&amp; base ();
+ const std::vector&lt;T, A>&amp; base ();
+
+ // Change tracking.
+ //
+ bool _tracking () const;
+ void _start () const;
+ void _stop () const;
+ void _arm (transaction&amp;) const;
+ };
+}
+ </pre>
+
+ <p>The following example highlights some of the differences between
+ the two interfaces. <code>std::vector</code> versions are commented
+ out.</p>
+
+ <pre class="cxx">
+#include &lt;vector>
+#include &lt;odb/vector.hxx>
+
+void f (const std::vector&lt;int>&amp;);
+
+odb::vector&lt;int> v ({1, 2, 3});
+
+f (v); // Ok, implicit conversion.
+
+if (v[1] == 2) // Ok, const access.
+ //v[1]++;
+ v.modify (1)++;
+
+//v.back () = 4;
+v.modify_back () = 4;
+
+for (auto i (v.begin ()); i != v.end (); ++i)
+{
+ if (*i != 0) // Ok, const access.
+ //*i += 10;
+ i.modify () += 10;
+}
+
+std::sort (v.mbegin (), v.mend ());
+ </pre>
+
+ <p>Note also the subtle difference between copy/move construction
+ and copy/move assignment of <code>odb::vector</code> instances.
+ While copy/move constructor will copy/move both the elements as
+ well as their change state, in contrast, assignment is tracked
+ as any other change to the vector content.</p>
+
+ <h2><a name="5.5">5.5 Using Custom Containers</a></h2>
+
+ <p>While the ODB runtime and profile libraries provide support for
+ a wide range of containers, it is also easy to persist custom
+ container types or make a change-tracking version out of one.</p>
+
+ <p>To achieve this you will need to implement the
+ <code>container_traits</code> class template specialization for
+ your container. First, determine the container kind (ordered, set,
+ multiset, map, or multimap) for your container type. Then use a
+ specialization for one of the standard C++ containers found in
+ the common ODB runtime library (<code>libodb</code>) as a base
+ for your own implementation.</p>
+
+ <p>Once the container traits specialization is ready for your container,
+ you will need to include it into the ODB compilation process using
+ the <code>--odb-epilogue</code> option and into the generated header
+ files with the <code>--hxx-prologue</code> option. As an example,
+ suppose we have a hash table container for which we have the traits
+ specialization implemented in the <code>hashtable-traits.hxx</code>
+ file. Then, we can create an ODB compiler options file for this
+ container and save it to <code>hashtable.options</code>:</p>
+
+ <pre>
+# Options file for the hash table container.
+#
+--odb-epilogue '#include "hashtable-traits.hxx"'
+--hxx-prologue '#include "hashtable-traits.hxx"'
+ </pre>
+
+ <p>Now, whenever we compile a header file that uses the hashtable
+ container, we can specify the following command line option to
+ make sure it is recognized by the ODB compiler as a container
+ and the traits file is included in the generated code:</p>
+
+ <pre>
+--options-file hashtable.options
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="6">6 Relationships</a></h1>
+
+ <p>Relationships between persistent objects are expressed with pointers or
+ containers of pointers. The ODB runtime library provides built-in support
+ for <code>shared_ptr</code>/<code>weak_ptr</code> (TR1 or C++11),
+ <code>std::unique_ptr</code> (C++11),
+ <code>std::auto_ptr</code>, and raw pointers. Plus, ODB profile
+ libraries, that are available for commonly used frameworks and libraries
+ (such as Boost and Qt), provide support for smart pointers found in these
+ frameworks and libraries (<a href="#III">Part III, "Profiles"</a>). It is
+ also easy to add support for a custom smart pointer as discussed later
+ in <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>. Any
+ supported smart pointer can be used in a data member as long as it can be
+ explicitly constructed from the canonical object pointer
+ (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>). For
+ example, we can use <code>weak_ptr</code> if the object pointer
+ is <code>shared_ptr</code>.</p>
+
+ <p>When an object containing a pointer to another object is loaded,
+ the pointed-to object is loaded as well. In some situations this
+ eager loading of the relationships is undesirable since it
+ can lead to a large number of otherwise unused objects being
+ instantiated from the database. To support finer control
+ over relationships loading, the ODB runtime and profile
+ libraries provide the so-called <em>lazy</em> versions of
+ the supported pointers. An object pointed-to by a lazy pointer
+ is not loaded automatically when the containing object is loaded.
+ Instead, we have to explicitly request the instantiation of the
+ pointed-to object. Lazy pointers are discussed in
+ detail in <a href="#6.4">Section 6.4, "Lazy Pointers"</a>.</p>
+
+ <p>As a simple example, consider the following employee-employer
+ relationship. Code examples presented in this chapter
+ will use the <code>shared_ptr</code> and <code>weak_ptr</code>
+ smart pointers from the TR1 (<code>std::tr1</code>) namespace.</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string first_name_;
+ std::string last_name_;
+
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>By default, an object pointer can be <code>NULL</code>. To
+ specify that a pointer always points to a valid object we can
+ use the <code>not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) for
+ single object pointers and the <code>value_not_null</code> pragma
+ (<a href="#14.4.28">Section
+ 14.4.28, "<code>value_null</code>/<code>value_not_null</code>"</a>)
+ for containers of object pointers. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ shared_ptr&lt;employer> current_employer_;
+
+ #pragma db value_not_null
+ std::vector&lt;shared_ptr&lt;employer> > previous_employers_;
+};
+ </pre>
+
+ <p>In this case, if we call either <code>persist()</code> or
+ <code>update()</code> database function on the
+ <code>employee</code> object and the <code>current_employer_</code>
+ pointer or one of the pointers stored in the
+ <code>previous_employers_</code> container is <code>NULL</code>,
+ then the <code>odb::null_pointer</code> exception will be thrown.</p>
+
+ <p>We don't need to do anything special to establish or navigate a
+ relationship between two persistent objects, as shown in the
+ following code fragment:</p>
+
+ <pre class="cxx">
+// Create an employer and a few employees.
+//
+unsigned long john_id, jane_id;
+{
+ shared_ptr&lt;employer> er (new employer ("Example Inc"));
+ shared_ptr&lt;employee> john (new employee ("John", "Doe"));
+ shared_ptr&lt;employee> jane (new employee ("Jane", "Doe"));
+
+ john->employer_ = er;
+ jane->employer_ = er;
+
+ transaction t (db.begin ());
+
+ db.persist (er);
+ john_id = db.persist (john);
+ jane_id = db.persist (jane);
+
+ t.commit ();
+}
+
+// Load a few employee objects and print their employer.
+//
+{
+ session s;
+ transaction t (db.begin ());
+
+ shared_ptr&lt;employee> john (db.load&lt;employee> (john_id));
+ shared_ptr&lt;employee> jane (db.load&lt;employee> (jane_id));
+
+ cout &lt;&lt; john->employer_->name_ &lt;&lt; endl;
+ cout &lt;&lt; jane->employer_->name_ &lt;&lt; endl;
+
+ t.commit ();
+}
+ </pre>
+
+ <p>The only notable line in the above code is the creation of a
+ session before the second transaction starts. As discussed in
+ <a href="#11">Chapter 11, "Session"</a>, a session acts as a cache
+ of persistent objects.
+ By creating a session before loading the <code>employee</code>
+ objects we make sure that their <code>employer_</code> pointers
+ point to the same <code>employer</code> object. Without a
+ session, each <code>employee</code> would have ended up pointing
+ to its own, private instance of the Example Inc employer.</p>
+
+ <p>As a general guideline, you should use a session when loading
+ objects that have pointers to other persistent objects. A
+ session makes sure that for a given object id, a single instance
+ is shared among all other objects that relate to it.</p>
+
+ <p>We can also use data members from pointed-to
+ objects in database queries (<a href="#4">Chapter 4, "Querying the
+ Database"</a>). For each pointer in a persistent class, the query
+ class defines a smart pointer-like member that contains members
+ corresponding to the data members in the pointed-to object. We
+ can then use the access via a pointer syntax (<code>-></code>)
+ to refer to data members in pointed-to objects.
+ For example, the query class for the <code>employee</code> object
+ contains the <code>employer</code> member (its name is derived from the
+ <code>employer_</code> pointer) which in turn contains the
+ <code>name</code> member (its name is derived from the
+ <code>employer::name_</code> data member of the pointed-to object).
+ As a result, we can use the <code>query::employer->name</code>
+ expression while querying the database for the <code>employee</code>
+ objects. For example, the following transaction finds all the
+ employees of Example Inc that have the Doe last name:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee> query;
+typedef odb::result&lt;employee> result;
+
+session s;
+transaction t (db.begin ());
+
+result r (db.query&lt;employee> (
+ query::employer->name == "Example Inc" &amp;&amp; query::last == "Doe"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first_ &lt;&lt; " " &lt;&lt; i->last_ &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>A query class member corresponding to a non-inverse
+ (<a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>) object
+ pointer can also be used as a normal member that has the id type
+ of the pointed-to object. For example, the following query locates
+ all the <code>employee</code> objects that don't have an associated
+ <code>employer</code> object:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee> (query::employer.is_null ()));
+ </pre>
+
+ <p>An important concept to keep in mind when working with object
+ relationships is the independence of persistent objects. In particular,
+ when an object containing a pointer to another object is made persistent
+ or is updated, the pointed-to object is not automatically persisted
+ or updated. Rather, only a reference to the object (in the form of the
+ object id) is stored for the pointed-to object in the database.
+ The pointed-to object itself is a separate entity and should
+ be made persistent or updated independently. By default, the
+ same principle also applies to erasing pointed-to objects. That
+ is, we have to make sure all the pointing objects are updated
+ accordingly. However, in the case of erase, we can specify an
+ alternative <code>on-delete</code> semantic as discussed in
+ <a href="#14.4.15">Section 14.4.15, "<code>on_delete</code>"</a>.</p>
+
+ <p>When persisting or updating an object containing a pointer to another
+ object, the pointed-to object must have a valid object id. This,
+ however, may not always be easy to achieve in complex relationships that
+ involve objects with automatically assigned identifiers. In such
+ cases it may be necessary to first persist an object with a pointer
+ set to <code>NULL</code> and then, once the pointed-to object is
+ made persistent and its identifier assigned, set the pointer
+ to the correct value and update the object in the database.</p>
+
+ <p>Persistent object relationships can be divided into two groups:
+ unidirectional and bidirectional. Each group in turn contains
+ several configurations that vary depending on the cardinality
+ of the sides of the relationship. All possible unidirectional
+ and bidirectional configurations are discussed in the following
+ sections.</p>
+
+ <h2><a name="6.1">6.1 Unidirectional Relationships</a></h2>
+
+ <p>In unidirectional relationships we are only interested in navigating
+ from object to object in one direction. Because there is no interest
+ in navigating in the opposite direction, the cardinality of the other
+ end of the relationship is unimportant. As a result, there are only
+ two possible unidirectional relationships: to-one and to-many. Each
+ of these relationships is described in the following sections. For
+ sample code that shows how to work with these relationships, refer
+ to the <code>relationship</code> example in the <code>odb-examples</code>
+ package.</p>
+
+ <h3><a name="6.1.1">6.1.1 To-One Relationships</a></h3>
+
+ <p>An example of a unidirectional to-one relationship is the
+ employee-employer relationship (an employee has one employer).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE employer (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employer VARCHAR (128) NOT NULL REFERENCES employer (name));
+ </pre>
+
+ <h3><a name="6.1.2">6.1.2 To-Many Relationships</a></h3>
+
+ <p>An example of a unidirectional to-many relationship is the
+ employee-project relationship (an employee can be involved
+ in multiple projects). The following persistent C++ classes
+ model this relationship:</p>
+
+ <pre class="cxx">
+#pragma db object
+class project
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db value_not_null unordered
+ std::vector&lt;shared_ptr&lt;project> > projects_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE project (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee_projects (
+ object_id BIGINT UNSIGNED NOT NULL,
+ value VARCHAR (128) NOT NULL REFERENCES project (name));
+ </pre>
+
+ <p>To obtain a more canonical database schema, the names of tables
+ and columns above can be customized using ODB pragmas
+ (<a href="#14">Chapter 14, "ODB Pragma Language"</a>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db value_not_null unordered \
+ id_column("employee_id") value_column("project_name")
+ std::vector&lt;shared_ptr&lt;project> > projects_;
+};
+ </pre>
+
+ <p>The resulting <code>employee_projects</code> table would then
+ look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE employee_projects (
+ employee_id BIGINT UNSIGNED NOT NULL,
+ project_name VARCHAR (128) NOT NULL REFERENCES project (name));
+ </pre>
+
+
+ <h2><a name="6.2">6.2 Bidirectional Relationships</a></h2>
+
+ <p>In bidirectional relationships we are interested in navigating
+ from object to object in both directions. As a result, each
+ object class in a relationship contains a pointer to the other
+ object. If smart pointers are used, then a weak pointer should
+ be used as one of the pointers to avoid ownership cycles. For
+ example:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>Note that when we establish a bidirectional relationship, we
+ have to set both pointers consistently. One way to make sure
+ that a relationship is always in a consistent state is to
+ provide a single function that updates both pointers at the
+ same time. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class position: public enable_shared_from_this&lt;position>
+{
+ ...
+
+ void
+ fill (shared_ptr&lt;employee> e)
+ {
+ employee_ = e;
+ e->positions_ = shared_from_this ();
+ }
+
+private:
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+private:
+ friend class position;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>At the beginning of this chapter we examined how to use a session
+ to make sure a single object is shared among all other objects pointing
+ to it. With bidirectional relationships involving weak pointers the
+ use of a session becomes even more crucial. Consider the following
+ transaction that tries to load the <code>position</code> object
+ from the above example without using a session:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ())
+shared_ptr&lt;position> p (db.load&lt;position> (1));
+...
+t.commit ();
+ </pre>
+
+ <p>When we load the <code>position</code> object, the <code>employee</code>
+ object, which it points to, is also loaded. While <code>employee</code>
+ is initially stored as <code>shared_ptr</code>, it is then assigned to
+ the <code>employee_</code> member which is <code>weak_ptr</code>. Once
+ the assignment is complete, the shared pointer goes out of scope
+ and the only pointer that points to the newly loaded
+ <code>employee</code> object is the <code>employee_</code> weak
+ pointer. And that means the <code>employee</code> object is deleted
+ immediately after being loaded. To help avoid such pathological
+ situations ODB detects cases where a newly loaded object will
+ immediately be deleted and throws the <code>odb::session_required</code>
+ exception.</p>
+
+ <p>As the exception name suggests, the easiest way to resolve this
+ problem is to use a session:</p>
+
+ <pre class="cxx">
+session s;
+transaction t (db.begin ())
+shared_ptr&lt;position> p (db.load&lt;position> (1));
+...
+t.commit ();
+ </pre>
+
+ <p>In our example, the session will maintain a shared pointer to the
+ loaded <code>employee</code> object preventing its immediate
+ deletion. Another way to resolve this problem is to avoid
+ immediate loading of the pointed-to objects using lazy weak
+ pointers. Lazy pointers are discussed in <a href="#6.4">Section 6.4,
+ "Lazy Pointers"</a> later in this chapter.</p>
+
+ <p>Above, to model a bidirectional relationship in persistent classes,
+ we used two pointers, one in each object. While this is a natural
+ representation in C++, it does not translate to a canonical
+ relational model. Consider the database schema generated for
+ the above two classes:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employee BIGINT UNSIGNED REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
+ </pre>
+
+ <p>While this database schema is valid, it is unconventional. We have
+ a reference from a row in the <code>position</code> table to a row
+ in the <code>employee</code> table. We also have a reference
+ from this same row in the <code>employee</code> table back to
+ the row in the <code>position</code> table. From the relational
+ point of view, one of these references is redundant since
+ in SQL we can easily navigate in both directions using just one
+ of these references.</p>
+
+ <p>To eliminate redundant database schema references we can use the
+ <code>inverse</code> pragma (<a href="#14.4.14">Section 14.4.14,
+ "<code>inverse</code>"</a>) which tells the ODB compiler that
+ a pointer is the inverse side of a bidirectional relationship.
+ Either side of a relationship can be made inverse. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db inverse(position_)
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>The resulting database schema looks like this:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
+ </pre>
+
+ <p>As you can see, an inverse member does not have a corresponding
+ column (or table, in case of an inverse container of pointers)
+ and, from the point of view of database operations, is effectively
+ read-only. The only way to change a bidirectional relationship
+ with an inverse side is to set its direct (non-inverse)
+ pointer. Also note that an ordered container (<a href="#5.1">Section
+ 5.1, "Ordered Containers"</a>) of pointers that is an inverse side
+ of a bidirectional relationship is always treated as unordered
+ (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>)
+ because the contents of such a container are implicitly built from
+ the direct side of the relationship which does not contain the
+ element order (index).</p>
+
+ <p>There are three distinct bidirectional relationships that we
+ will cover in the following sections: one-to-one, one-to-many,
+ and many-to-many. We will only talk about bidirectional
+ relationships with inverse sides since they result in canonical
+ database schemas. For sample code that shows how to work with
+ these relationships, refer to the <code>inverse</code> example
+ in the <code>odb-examples</code> package.</p>
+
+ <h3><a name="6.2.1">6.2.1 One-to-One Relationships</a></h3>
+
+ <p>An example of a bidirectional one-to-one relationship is the
+ presented above employee-position relationship (an employee
+ fills one position and a position is filled by one employee).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db inverse(position_)
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
+ </pre>
+
+ <p>If instead the other side of this relationship is made inverse,
+ then the database tables will change as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employee BIGINT UNSIGNED REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+ </pre>
+
+ <h3><a name="6.2.2">6.2.2 One-to-Many Relationships</a></h3>
+
+ <p>An example of a bidirectional one-to-many relationship is the
+ employer-employee relationship (an employer has multiple
+ employees and an employee is employed by one employer).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+
+ #pragma db value_not_null inverse(employer_)
+ std::vector&lt;weak_ptr&lt;employee> > employees_
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>The corresponding database tables differ significantly depending
+ on which side of the relationship is made inverse. If the <em>one</em>
+ side (<code>employer</code>) is inverse as in the code
+ above, then the resulting database schema looks like this:</p>
+
+ <pre class="sql">
+CREATE TABLE employer (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employer VARCHAR (128) NOT NULL REFERENCES employer (name));
+ </pre>
+
+ <p>If instead the <em>many</em> side (<code>employee</code>) of this
+ relationship is made inverse, then the database tables will change
+ as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE employer (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employer_employees (
+ object_id VARCHAR (128) NOT NULL REFERENCES employer (name),
+ value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+ </pre>
+
+ <h3><a name="6.2.3">6.2.3 Many-to-Many Relationships</a></h3>
+
+ <p>An example of a bidirectional many-to-many relationship is the
+ employee-project relationship (an employee can work on multiple
+ projects and a project can have multiple participating employees).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class project
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+
+ #pragma db value_not_null inverse(projects_)
+ std::vector&lt;weak_ptr&lt;employee> > employees_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db value_not_null unordered
+ std::vector&lt;shared_ptr&lt;project> > projects_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE project (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee_projects (
+ object_id BIGINT UNSIGNED NOT NULL REFERENCES employee (id),
+ value VARCHAR (128) NOT NULL REFERENCES project (name));
+ </pre>
+
+ <p>If instead the other side of this relationship is made inverse,
+ then the database tables will change as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE project (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE project_employees (
+ object_id VARCHAR (128) NOT NULL REFERENCES project (name),
+ value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+ </pre>
+
+ <h2><a name="6.3">6.3 Circular Relationships</a></h2>
+
+ <p>A relationship between two persistent classes is circular if each
+ of them references the other. Bidirectional relationships are
+ always circular. A unidirectional relationship combined with
+ inheritance (<a href="#8">Chapter 8, "Inheritance"</a>) can also
+ be circular. For example, the <code>employee</code> class could
+ derive from <code>person</code> which, in turn, could contain a
+ pointer to <code>employee</code>.</p>
+
+ <p>We don't need to do anything extra if persistent classes with
+ circular dependencies are defined in the same header
+ file. Specifically, ODB will make sure that the database tables
+ and foreign key constraints are created in the correct order. As a
+ result, unless you have good reasons not to, it is recommended that
+ you keep persistent classes with circular dependencies in the same
+ header file.</p>
+
+ <p>If you have to keep such classes in separate header files, then
+ there are two extra steps that you may need to take in order to
+ use these classes with ODB. Consider again the example from
+ <a href="#6.2.1">Section 6.2.1, "One-to-One Relationships"</a>
+ but this time with the classes defined in separate headers:</p>
+
+ <pre class="cxx">
+// position.hxx
+//
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db inverse(position_)
+ weak_ptr&lt;employee> employee_;
+};
+ </pre>
+
+ <pre class="cxx">
+// employee.hxx
+//
+#include "position.hxx"
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>Note that the <code>position.hxx</code> header contains only the forward
+ declaration for <code>employee</code>. While this is sufficient to
+ define a valid, from the C++ point of view, <code>position</code> class,
+ the ODB compiler needs to "see" the definitions of the pointed-to
+ persistent classes. There are several ways we can fulfil this
+ requirement. The easiest is to simply include <code>employee.hxx</code>
+ at the end of <code>position.hxx</code>:</p>
+
+ <pre class="cxx">
+// position.hxx
+//
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+};
+
+#include "employee.hxx"
+ </pre>
+
+ <p>We can also limit this inclusion only to the time when
+ <code>position.hxx</code> is compiled with the ODB compiler:</p>
+
+ <pre class="cxx">
+// position.hxx
+//
+
+...
+
+#ifdef ODB_COMPILER
+# include "employee.hxx"
+#endif
+ </pre>
+
+ <p>Finally, if we don't want to modify <code>position.hxx</code>,
+ then we can add <code>employee.hxx</code> to the ODB compilation
+ process with the <code>--odb-epilogue</code> option. For example:</p>
+
+ <pre class="terminal">
+odb ... --odb-epilogue "#include \"employee.hxx\"" position.hxx
+ </pre>
+
+ <p>Note also that in this example we didn't have to do anything extra
+ for <code>employee.hxx</code> because it already includes
+ <code>position.hxx</code>. However, if instead it relied only
+ on the forward declaration of the <code>position</code> class,
+ then we would have to handle it in the same way as
+ <code>position.hxx</code>.</p>
+
+ <p>The other difficulty with separately defined classes involving
+ circular relationships has to do with the correct order of foreign
+ key constraint creation in the generated database schema. In
+ the above example, if we generate the database schema as
+ standalone SQL files, then we will end up with two such files:
+ <code>position.sql</code> and <code>employee.sql</code>.
+ If we try to execute <code>employee.sql</code> first, then
+ we will get an error indicating that the table corresponding to
+ the <code>position</code> class and referenced by the foreign
+ key constraint corresponding to the <code>position_</code>
+ pointer does not yet exist.</p>
+
+ <p>Note that there is no such problem if the database schema
+ is embedded in the generated C++ code instead of being produced
+ as standalone SQL files. In this case, the ODB compiler is
+ able to ensure the correct creation order even if the classes
+ are defined in separate header files.</p>
+
+ <p>In certain cases, for example, a bidirectional relationship
+ with an inverse side, this problem can be resolved by executing
+ the database schema creation files in the correct order. In our
+ example, this would be <code>position.sql</code> first
+ and <code>employee.sql</code> second. However, this approach
+ doesn't scale beyond simple object models.</p>
+
+ <p>A more robust solution to this problem is to generate the database
+ schema for all the persistent classes into a single SQL file. This
+ way, the ODB compiler can again ensure the correct creation order
+ of tables and foreign keys. To instruct the ODB compiler to produce
+ a combined schema file for several headers we can use the
+ <code>--generate-schema-only</code> and <code>--at-once</code>
+ options. For example:</p>
+
+ <pre class="terminal">
+odb ... --generate-schema-only --at-once --input-name company \
+position.hxx employee.hxx
+ </pre>
+
+ <p>The result of the above command is a single <code>company.sql</code>
+ file (the name is derived from the <code>--input-name</code> value)
+ that contains the database creation code for both <code>position</code>
+ and <code>employee</code> classes.</p>
+
+ <h2><a name="6.4">6.4 Lazy Pointers</a></h2>
+
+ <p>Consider again the bidirectional, one-to-many employer-employee
+ relationship that was presented earlier in this chapter:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+
+ #pragma db value_not_null inverse(employer_)
+ std::vector&lt;weak_ptr&lt;employee> > employees_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>Consider also the following transaction which obtains the employer
+ name given the employee id:</p>
+
+ <pre class="cxx">
+unsigned long id = ...
+string name;
+
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employee> e (db.load&lt;employee> (id));
+name = e->employer_->name_;
+
+t.commit ();
+ </pre>
+
+ <p>While this transaction looks very simple, it actually does a lot more
+ than what meets the eye and is necessary. Consider what happens when
+ we load the <code>employee</code> object: the <code>employer_</code>
+ pointer is also automatically loaded which means the <code>employer</code>
+ object corresponding to this employee is also loaded. But the
+ <code>employer</code> object in turn contains the list of pointers
+ to all the employees, which are also loaded. As a result, when object
+ relationships are involved, a simple transaction like the above can
+ load many more objects than is necessary.</p>
+
+ <p>To overcome this problem ODB offers finer grained control over
+ the relationship loading in the form of lazy pointers. A lazy
+ pointer does not automatically load the pointed-to object
+ when the containing object is loaded. Instead, we have to
+ explicitly load the pointed-to object if and when we need to
+ access it.</p>
+
+ <p>The ODB runtime library provides lazy counterparts for all the
+ supported pointers, namely:
+ <code>odb::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
+ for C++11 <code>std::shared_ptr</code>/<code>weak_ptr</code>,
+ <code>odb::tr1::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
+ for TR1 <code>std::tr1::shared_ptr</code>/<code>weak_ptr</code>,
+ <code>odb::lazy_unique_ptr</code> for C++11 <code>std::unique_ptr</code>,
+ <code>odb::lazy_auto_ptr</code> for <code>std::auto_ptr</code>,
+ and <code>odb::lazy_ptr</code> for raw pointers. The TR1 lazy
+ pointers are defined in the <code>&lt;odb/tr1/lazy-ptr.hxx></code>
+ header while all the others &mdash; in
+ <code>&lt;odb/lazy-ptr.hxx></code>. The ODB profile
+ libraries also provide lazy pointer implementations for smart pointers
+ from popular frameworks and libraries (<a href="#III">Part III,
+ "Profiles"</a>).</p>
+
+ <p>While we will discuss the interface of lazy pointers in more detail
+ shortly, the most commonly used extra function provided by these
+ pointers is <code>load()</code>. This function loads the
+ pointed-to object if it hasn't already been loaded. After
+ the call to this function, the lazy pointer can be used
+ in the the same way as its eager counterpart. The <code>load()</code>
+ function also returns the eager pointer, in case you need to pass
+ it around. For a lazy weak pointer, the
+ <code>load()</code> function also locks the pointer.</p>
+
+ <p>The following example shows how we can change our employer-employee
+ relationship to use lazy pointers. Here we choose to use lazy pointers
+ for both sides of the relationship.</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db value_not_null inverse(employer_)
+ std::vector&lt;lazy_weak_ptr&lt;employee> > employees_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ lazy_shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>And the transaction is changed like this:</p>
+
+ <pre class="cxx">
+unsigned long id = ...
+string name;
+
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employee> e (db.load&lt;employee> (id));
+e->employer_.load ();
+name = e->employer_->name_;
+
+t.commit ();
+ </pre>
+
+
+ <p>As a general guideline we recommend that you make at least one side
+ of a bidirectional relationship lazy, especially for relationships
+ with a <em>many</em> side.</p>
+
+ <p>A lazy pointer implementation mimics the interface of its eager
+ counterpart which can be used once the pointer is loaded. It also
+ adds a number of additional functions that are specific to the
+ lazy loading functionality. Overall, the interface of a lazy
+ pointer follows this general outline:</p>
+
+ <pre class="cxx">
+template &lt;class T>
+class lazy_ptr
+{
+public:
+ //
+ // The eager pointer interface.
+ //
+
+ // Initialization/assignment from an eager pointer to a
+ // transient object.
+ //
+public:
+ template &lt;class Y> lazy_ptr (const eager_ptr&lt;Y>&amp;);
+ template &lt;class Y> lazy_ptr&amp; operator= (const eager_ptr&lt;Y>&amp;);
+
+ // Lazy loading interface.
+ //
+public:
+ // NULL loaded()
+ //
+ // true true NULL pointer to transient object
+ // false true valid pointer to persistent object
+ // true false unloaded pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ eager_ptr&lt;T> load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ eager_ptr&lt;T> get_eager () const;
+
+ // Initialization with a persistent loaded object.
+ //
+ template &lt;class Y> lazy_ptr (database&amp;, Y*);
+ template &lt;class Y> lazy_ptr (database&amp;, const eager_ptr&lt;Y>&amp;);
+
+ template &lt;class Y> void reset (database&amp;, Y*);
+ template &lt;class Y> void reset (database&amp;, const eager_ptr&lt;Y>&amp;);
+
+ // Initialization with a persistent unloaded object.
+ //
+ template &lt;class ID> lazy_ptr (database&amp;, const ID&amp;);
+
+ template &lt;class ID> void reset (database&amp;, const ID&amp;);
+
+ // Query object id and database of a persistent object.
+ //
+ template &lt;class O /* = T */>
+ // C++11: template &lt;class O = T>
+ object_traits&lt;O>::id_type object_id () const;
+
+ odb::database&amp; database () const;
+};
+ </pre>
+
+ <p>Note that to initialize a lazy pointer to a persistent object from
+ its eager pointer one must use the constructor or <code>reset()</code>
+ function with the database as its first argument. The database is
+ required to support comparison of unloaded lazy pointers to persistent
+ objects.</p>
+
+ <p>In a lazy weak pointer interface, the <code>load()</code> function
+ returns the <em>strong</em> (shared) eager pointer. The following
+ transaction demonstrates the use of a lazy weak pointer based on
+ the <code>employer</code> and <code>employee</code> classes
+ presented earlier.</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;lazy_weak_ptr&lt;employee> > employees;
+
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
+employees&amp; es (er->employees ());
+
+for (employees::iterator i (es.begin ()); i != es.end (); ++i)
+{
+ // We are only interested in employees with object id less than
+ // 100.
+ //
+ lazy_weak_ptr&lt;employee>&amp; lwp (*i);
+
+ if (lwp.object_id&lt;employee> () &lt; 100)
+ // C++11: if (lwp.object_id () &lt; 100)
+ {
+ shared_ptr&lt;employee> e (lwp.load ()); // Load and lock.
+ cout &lt;&lt; e->first_ &lt;&lt; " " &lt;&lt; e->last_ &lt;&lt; endl;
+ }
+}
+
+t.commit ();
+ </pre>
+
+ <p>Notice that inside the for-loop we use a reference to the lazy
+ weak pointer instead of making a copy. This is not merely to
+ avoid a copy. When a lazy pointer is loaded, all other lazy
+ pointers that point to the same object do not automatically
+ become loaded (though an attempt to load such copies will
+ result in them pointing to the same object, provided the
+ same session is still in effect). By using a reference
+ in the above transaction we make sure that we load the
+ pointer that is contained in the <code>employer</code>
+ object. This way, if we later need to re-examine this
+ <code>employee</code> object, the pointer will already
+ be loaded.</p>
+
+ <p>As another example, suppose we want to add an employee
+ to Example Inc. The straightforward implementation of this
+ transaction is presented below:</p>
+
+ <pre class="cxx">
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
+shared_ptr&lt;employee> e (new employee ("John", "Doe"));
+
+e->employer_ = er;
+er->employees ().push_back (e);
+
+db.persist (e);
+t.commit ();
+ </pre>
+
+ <p>Notice here that we didn't have to update the employer object
+ in the database since the <code>employees_</code> list of
+ pointers is an inverse side of a bidirectional relationship
+ and is effectively read-only, from the persistence point of
+ view.</p>
+
+ <p>A faster implementation of this transaction, that avoids loading
+ the employer object, relies on the ability to initialize an
+ <em>unloaded</em> lazy pointer with the database where the object
+ is stored as well as its identifier:</p>
+
+ <pre class="cxx">
+lazy_shared_ptr&lt;employer> er (db, std::string ("Example Inc"));
+shared_ptr&lt;employee> e (new employee ("John", "Doe"));
+
+e->employer_ = er;
+
+session s;
+transaction t (db.begin ());
+
+db.persist (e);
+
+t.commit ();
+ </pre>
+
+ <p>For the interaction of lazy pointers with lazy-loaded object
+ sections, refer to <a href="#9.3">Section 9.3, "Sections and
+ Lazy Pointers"</a>.</p>
+
+ <h2><a name="6.5">6.5 Using Custom Smart Pointers</a></h2>
+
+ <p>While the ODB runtime and profile libraries provide support for
+ the majority of widely-used pointers, it is also easy to add
+ support for a custom smart pointer.</p>
+
+ <p>To achieve this you will need to implement the
+ <code>pointer_traits</code> class template specialization for
+ your pointer. The first step is to determine the pointer kind
+ since the interface of the <code>pointer_traits</code> specialization
+ varies depending on the pointer kind. The supported pointer kinds
+ are: <em>raw</em> (raw pointer or equivalent, that is, unmanaged),
+ <em>unique</em> (smart pointer that doesn't support sharing),
+ <em>shared</em> (smart pointer that supports sharing), and
+ <em>weak</em> (weak counterpart of the shared pointer). Any of
+ these pointers can be lazy, which also affects the
+ interface of the <code>pointer_traits</code> specialization.</p>
+
+ <p>Once you have determined the pointer kind for your smart pointer,
+ use a specialization for one of the standard pointers found in
+ the common ODB runtime library (<code>libodb</code>) as a base
+ for your own implementation.</p>
+
+ <p>Once the pointer traits specialization is ready, you will need to
+ include it into the ODB compilation process using the
+ <code>--odb-epilogue</code> option and into the generated header
+ files with the <code>--hxx-prologue</code> option. As an example,
+ suppose we have the <code>smart_ptr</code> smart pointer for which
+ we have the traits specialization implemented in the
+ <code>smart-ptr-traits.hxx</code> file. Then, we can create an ODB
+ compiler options file for this pointer and save it to
+ <code>smart-ptr.options</code>:</p>
+
+ <pre>
+# Options file for smart_ptr.
+#
+--odb-epilogue '#include "smart-ptr-traits.hxx"'
+--hxx-prologue '#include "smart-ptr-traits.hxx"'
+ </pre>
+
+ <p>Now, whenever we compile a header file that uses <code>smart_ptr</code>,
+ we can specify the following command line option to make sure it is
+ recognized by the ODB compiler as a smart pointer and the traits file
+ is included in the generated code:</p>
+
+ <pre>
+--options-file smart-ptr.options
+ </pre>
+
+ <p>It is also possible to implement a lazy counterpart for your
+ smart pointer. The ODB runtime library provides a class template
+ that encapsulates the object id management and loading
+ functionality that is needed to implement a lazy pointer. All
+ you need to do is wrap it with an interface that mimics
+ your smart pointer. Using one of the existing lazy pointer
+ implementations (either from the ODB runtime library or one
+ of the profile libraries) as a base for your implementation
+ is the easiest way to get started.</p>
+
+
+ <!-- CHAPTER -->
+
+ <hr class="page-break"/>
+ <h1><a name="7">7 Value Types</a></h1>
+
+ <p>In <a href="#3.1">Section 3.1, "Concepts and Terminology"</a> we have
+ already discussed the notion of values and value types as well as the
+ distinction between simple and composite values. This chapter covers
+ simple and composite value types in more detail.</p>
+
+ <h2><a name="7.1">7.1 Simple Value Types</a></h2>
+
+ <p>A simple value type is a fundamental C++ type or a class type that
+ is mapped to a single database column. For each supported database
+ system the ODB compiler provides a default mapping to suitable
+ database types for most fundamental C++ types, such as <code>int</code>
+ or <code>float</code> as well as some class types, such as
+ <code>std::string</code>. For more information about the default
+ mapping for each database system refer to <a href="#II">Part II,
+ Database Systems</a>. We can also provide a custom mapping for
+ these or our own value types using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
+
+ <h2><a name="7.2">7.2 Composite Value Types</a></h2>
+
+ <p>A composite value type is a <code>class</code> or <code>struct</code>
+ type that is mapped to more than one database column. To declare
+ a composite value type we use the <code>db&nbsp;value</code> pragma,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class basic_name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+ </pre>
+
+ <p>The complete version of the above code fragment and the other code
+ samples presented in this section can be found in the <code>composite</code>
+ example in the <code>odb-examples</code> package.</p>
+
+ <p>A composite value type does not have to define a default constructor,
+ unless it is used as an element of a container. In this case the
+ default constructor can be made private provided we also make the
+ <code>odb::access</code> class, defined in the
+ <code>&lt;odb/core.hxx></code> header, a friend of this value type.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma db value
+class basic_name
+{
+public:
+ basic_name (const std::string&amp; first, const std::string&amp; last);
+
+ ...
+
+private:
+ friend class odb::access;
+
+ basic_name () {} // Needed for storing basic_name in containers.
+
+ ...
+};
+ </pre>
+
+ <p>The ODB compiler also needs access to the non-transient
+ (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
+ data members of a composite value type. It uses the same mechanisms
+ as for persistent classes which are discussed in
+ <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
+ Values"</a>.</p>
+
+ <p>The members of a composite value can be other value types (either
+ simple or composite), containers (<a href="#5">Chapter 5,
+ "Containers"</a>), and pointers to objects (<a href="#6">Chapter 6,
+ "Relationships"</a>).
+ Similarly, a composite value type can be used in object members,
+ as an element of a container, and as a base for another composite
+ value type. In particular, composite value types can be used as
+ element types in set containers (<a href="#5.2">Section 5.2, "Set
+ and Multiset Containers"</a>) and as key types in map containers
+ (<a href="#5.3">Section 5.3, "Map and Multimap Containers"</a>).
+ A composite value type that is used as an element of a container
+ cannot contain other containers since containers of containers
+ are not allowed. The following example illustrates some of the
+ possible use cases:</p>
+
+ <pre class="cxx">
+#pragma db value
+class basic_name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+typedef std::vector&lt;basic_name> basic_names;
+
+#pragma db value
+class name_extras
+{
+ ...
+
+ std::string nickname_;
+ basic_names aliases_;
+};
+
+#pragma db value
+class name: public basic_name
+{
+ ...
+
+ std::string title_;
+ name_extras extras_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ name name_;
+};
+ </pre>
+
+ <p>A composite value type can be defined inside a persistent class,
+ view, or another composite value and even made private, provided
+ we make <code>odb::access</code> a friend of the containing class,
+ for example:</p>
+
+<pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value
+ class name
+ {
+ ...
+
+ std::string first_;
+ std::string last_;
+ };
+
+ name name_;
+};
+ </pre>
+
+ <p>A composite value type can also be defined as an instantiation
+ of a C++ class template, for example:</p>
+
+ <pre class="cxx">
+template &lt;typename T>
+struct point
+{
+ T x;
+ T y;
+ T z;
+};
+
+typedef point&lt;int> int_point;
+#pragma db value(int_point)
+
+#pragma db object
+class object
+{
+ ...
+
+ int_point center_;
+};
+ </pre>
+
+ <p>Note that the database support code for such a composite value type
+ is generated when compiling the header containing the
+ <code>db&nbsp;value</code> pragma and not the header containing
+ the template definition or the <code>typedef</code> name. This
+ allows us to use templates defined in other files, such as
+ <code>std::pair</code> defined in the <code>utility</code>
+ standard header file:</p>
+
+ <pre class="cxx">
+#include &lt;utility> // std::pair
+
+typedef std::pair&lt;std::string, std::string> phone_numbers;
+#pragma db value(phone_numbers)
+
+#pragma db object
+class person
+{
+ ...
+
+ phone_numbers phone_;
+};
+ </pre>
+
+ <p>We can also use data members from composite value types
+ in database queries (<a href="#4">Chapter 4, "Querying the
+ Database"</a>). For each composite value in a persistent class, the
+ query class defines a nested member that contains members corresponding
+ to the data members in the value type. We can then use the member access
+ syntax (.) to refer to data members in value types. For example, the
+ query class for the <code>person</code> object presented above
+ contains the <code>name</code> member (its name is derived from
+ the <code>name_</code> data member) which in turn contains the
+ <code>extras</code> member (its name is derived from the
+ <code>name::extras_</code> data member of the composite value type).
+ This process continues recursively for nested composite value types
+ and, as a result, we can use the <code>query::name.extras.nickname</code>
+ expression while querying the database for the <code>person</code>
+ objects. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::result&lt;person> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;person> (
+ query::name.extras.nickname == "Squeaky"));
+
+...
+
+t.commit ();
+ </pre>
+
+ <h3><a name="7.2.1">7.2.1 Composite Object Ids</a></h3>
+
+ <p>An object id can be of a composite value type, for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ name name_;
+};
+ </pre>
+
+ <p>However, a value type that can be used as an object id has a number
+ of restrictions. Such a value type cannot have container, object
+ pointer, or read-only data members. It also must be
+ default-constructible, copy-constructible, and copy-assignable.
+ Furthermore, if the persistent class in which
+ this composite value type is used as object id has session support
+ enabled (<a href="#11">Chapter 11, "Session"</a>), then it must also
+ implement the less-than comparison operator (<code>operator&lt;</code>).</p>
+
+ <h3><a name="7.2.2">7.2.2 Composite Value Column and Table Names</a></h3>
+
+ <p>Customizing a column name for a data member of a simple value
+ type is straightforward: we simply specify the desired name with
+ the <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section
+ 14.4.9, "<code>column</code>"</a>). For composite value
+ types things are slightly more complex since they are mapped to
+ multiple columns. Consider the following example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ name name_;
+};
+ </pre>
+
+ <p>The column names for the <code>first_</code> and <code>last_</code>
+ members are constructed by using the sanitized name of the
+ <code>person::name_</code> member as a prefix and the names of the
+ members in the value type (<code>first_</code> and <code>last_</code>)
+ as suffixes. As a result, the database schema for the above classes
+ will look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ name_first TEXT NOT NULL,
+ name_last TEXT NOT NULL);
+ </pre>
+
+ <p>We can customize both the prefix and the suffix using the
+ <code>db&nbsp;column</code> pragma as shown in the following
+ example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ #pragma db column("first_name")
+ std::string first_;
+
+ #pragma db column("last_name")
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db column("person_")
+ name name_;
+};
+ </pre>
+
+ <p>The database schema changes as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ person_first_name TEXT NOT NULL,
+ person_last_name TEXT NOT NULL);
+ </pre>
+
+ <p>We can also make the column prefix empty, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db column("")
+ name name_;
+};
+ </pre>
+
+ <p>This will result in the following schema:</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ first_name TEXT NOT NULL,
+ last_name TEXT NOT NULL);
+ </pre>
+
+ <p>The same principle applies when a composite value type is used
+ as an element of a container, except that instead of
+ <code>db&nbsp;column</code>, either the <code>db&nbsp;value_column</code>
+ (<a href="#14.4.36">Section 14.4.36, "<code>value_column</code>"</a>) or
+ <code>db&nbsp;key_column</code>
+ (<a href="#14.4.35">Section 14.4.35, "<code>key_column</code>"</a>)
+ pragmas are used to specify the column prefix.</p>
+
+ <p>When a composite value type contains a container, an extra table
+ is used to store its elements (<a href="#5">Chapter 5, "Containers"</a>).
+ The names of such tables are constructed in a way similar to the
+ column names, except that by default both the object name and the
+ member name are used as a prefix. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+ std::vector&lt;std::string> nicknames_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ name name_;
+};
+ </pre>
+
+ <p>The corresponding database schema will look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE person_name_nicknames (
+ object_id BIGINT UNSIGNED NOT NULL,
+ index BIGINT UNSIGNED NOT NULL,
+ value TEXT NOT NULL)
+
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ name_first TEXT NOT NULL,
+ name_last TEXT NOT NULL);
+ </pre>
+
+ <p>To customize the container table name we can use the
+ <code>db&nbsp;table</code> pragma (<a href="#14.4.20">Section
+ 14.4.20, "<code>table</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ #pragma db table("nickname")
+ std::vector&lt;std::string> nicknames_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db table("person_")
+ name name_;
+};
+ </pre>
+
+ <p>This will result in the following schema changes:</p>
+
+ <pre class="sql">
+CREATE TABLE person_nickname (
+ object_id BIGINT UNSIGNED NOT NULL,
+ index BIGINT UNSIGNED NOT NULL,
+ value TEXT NOT NULL)
+ </pre>
+
+ <p>Similar to columns, we can make the table prefix empty.</p>
+
+
+ <h2><a name="7.3">7.3 Pointers and <code>NULL</code> Value Semantics</a></h2>
+
+ <p>Relational database systems have a notion of the special
+ <code>NULL</code> value that is used to indicate the absence
+ of a valid value in a column. While by default ODB maps
+ values to columns that do not allow <code>NULL</code> values,
+ it is possible to change that with the <code>db&nbsp;null</code>
+ pragma (<a href="#14.4.6">Section 14.4.6,
+ "<code>null</code>/<code>not_null</code>"</a>).</p>
+
+ <p>To properly support the <code>NULL</code> semantics, the
+ C++ value type must have a notion of a <code>NULL</code>
+ value or a similar special state concept. Most basic
+ C++ types, such as <code>int</code> or <code>std::string</code>,
+ do not have this notion and therefore cannot be used directly
+ for <code>NULL</code>-enabled data members (in the case of a
+ <code>NULL</code> value being loaded from the database,
+ such data members will be default-initialized).</p>
+
+ <p>To allow the easy conversion of value types that do not support
+ the <code>NULL</code> semantics into the ones that do, ODB
+ provides the <code>odb::nullable</code> class template. It
+ allows us to wrap an existing C++ type into a container-like
+ class that can either be <code>NULL</code> or contain a
+ value of the wrapped type. ODB also automatically enables
+ the <code>NULL</code> values for data members of the
+ <code>odb::nullable</code> type. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/nullable.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_; // TEXT NOT NULL
+ odb::nullable&lt;std::string> middle_; // TEXT NULL
+ std::string last_; // TEXT NOT NULL
+};
+ </pre>
+
+ <p>The <code>odb::nullable</code> class template is defined
+ in the <code>&lt;odb/nullable.hxx></code> header file and
+ has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ class nullable
+ {
+ public:
+ typedef T value_type;
+
+ nullable ();
+ nullable (const T&amp;);
+ nullable (const nullable&amp;);
+ template &lt;typename Y> explicit nullable (const nullable&lt;Y>&amp;);
+
+ nullable&amp; operator= (const T&amp;);
+ nullable&amp; operator= (const nullable&amp;);
+ template &lt;typename Y> nullable&amp; operator= (const nullable&lt;Y>&amp;);
+
+ void swap (nullable&amp;);
+
+ // Accessor interface.
+ //
+ bool null () const;
+
+ T&amp; get ();
+ const T&amp; get () const;
+
+ // Pointer interface.
+ //
+ operator bool_convertible () const;
+
+ T* operator-> ();
+ const T* operator-> () const;
+
+ T&amp; operator* ();
+ const T&amp; operator* () const;
+
+ // Reset to the NULL state.
+ //
+ void reset ();
+ };
+}
+ </pre>
+
+ <p>The following example shows how we can use this interface:</p>
+
+ <pre class="cxx">
+ nullable&lt;string> ns;
+
+ // Using the accessor interface.
+ //
+ if (ns.null ())
+ {
+ s = "abc";
+ }
+ else
+ {
+ string s (ns.get ());
+ ns.reset ();
+ }
+
+ // The same using the pointer interface.
+ //
+ if (!ns)
+ {
+ s = "abc";
+ }
+ else
+ {
+ string s (*ns);
+ ns.reset ();
+ }
+ </pre>
+
+
+ <p>The <code>odb::nullable</code> class template requires the wrapped
+ type to have public default and copy constructors as well as the
+ copy assignment operator. Note also that the <code>odb::nullable</code>
+ implementation is not the most efficient in that it always contains
+ a fully constructed value of the wrapped type. This is normally
+ not a concern for simple types such as the C++ fundamental
+ types or <code>std::string</code>. However, it may become
+ an issue for more complex types. In such cases you may want to
+ consider using a more efficient implementation of the
+ <em>optional value</em> concept such as the
+ <code>optional</code> class template from Boost
+ (<a href="#23.4">Section 23.4, "Optional Library"</a>).</p>
+
+ <p>Another common C++ representation of a value that can be
+ <code>NULL</code> is a pointer. ODB will automatically
+ handle data members that are pointers to values, however,
+ it will not automatically enable <code>NULL</code> values
+ for such data members, as is the case for <code>odb::nullable</code>.
+ Instead, if the <code>NULL</code> value is desired, we will
+ need to enable it explicitly using the <code>db&nbsp;null</code>
+ pragma. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_;
+
+ #pragma db null
+ std::auto_ptr&lt;std::string> middle_;
+
+ std::string last_;
+};
+ </pre>
+
+ <p>The ODB compiler includes built-in support for using
+ <code>std::auto_ptr</code>, <code>std::unique_ptr</code> (C++11),
+ and <code>shared_ptr</code> (TR1 or C++11) as pointers to values.
+ Plus, ODB profile libraries, that are
+ available for commonly used frameworks and libraries (such as Boost and
+ Qt), provide support for smart pointers found in these frameworks
+ and libraries (<a href="#III">Part III, "Profiles"</a>).</p>
+
+ <p>ODB also supports the <code>NULL</code> semantics for composite
+ values. In the relational database the <code>NULL</code> composite
+ value is translated to <code>NULL</code> values for all the simple
+ data members of this composite value. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct name
+{
+ std::string first_;
+ odb::nullable&lt;std::string> middle_;
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+ odb::nullable&lt;name> name_;
+};
+ </pre>
+
+ <p>ODB does not support the <code>NULL</code> semantics for containers.
+ This also means that a composite value that contains a container
+ cannot be <code>NULL</code>. With this limitation in mind, we can
+ still use smart pointers in data members of container types. The
+ only restriction is that these pointers must not be <code>NULL</code>.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ std::auto_ptr&lt;std::vector&lt;std::string> > aliases_;
+};
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="8">8 Inheritance</a></h1>
+
+ <p>In C++ inheritance can be used to achieve two different goals.
+ We can employ inheritance to reuse common data and functionality
+ in multiple classes. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+public:
+ const std::string&amp; first () const;
+ const std::string&amp; last () const;
+
+private:
+ std::string first_;
+ std::string last_;
+};
+
+class employee: public person
+{
+ ...
+};
+
+class contractor: public person
+{
+ ...
+};
+ </pre>
+
+ <p>In the above example both the <code>employee</code> and
+ <code>contractor</code> classes inherit the <code>first_</code>
+ and <code>last_</code> data members as well as the <code>first()</code>
+ and <code>last()</code> accessors from the <code>person</code> base
+ class.</p>
+
+ <p>A common trait of this inheritance style, referred to as <em>reuse
+ inheritance</em> from now on, is the lack of virtual functions and
+ a virtual destructor in the base class. Also with this style the
+ application code is normally written in terms of the derived classes
+ instead of the base.</p>
+
+ <p>The second way to utilize inheritance in C++ is to provide polymorphic
+ behavior through a common interface. In this case the base class
+ defines a number of virtual functions and, normally, a virtual
+ destructor while the derived classes provide specific
+ implementations of these virtual functions. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+public:
+ enum employment_status
+ {
+ unemployed,
+ temporary,
+ permanent,
+ self_employed
+ };
+
+ virtual employment_status
+ employment () const = 0;
+
+ virtual
+ ~person ();
+};
+
+class employee: public person
+{
+public:
+ virtual employment_status
+ employment () const
+ {
+ return temporary_ ? temporary : permanent;
+ }
+
+private:
+ bool temporary_;
+};
+
+class contractor: public person
+{
+public:
+ virtual employment_status
+ employment () const
+ {
+ return self_employed;
+ }
+};
+ </pre>
+
+ <p>With this inheritance style, which we will call <em>polymorphism
+ inheritance</em>, the application code normally works with derived
+ classes via the base class interface. Note also that it is very common
+ to mix both styles in the same hierarchy. For example, the above two
+ code fragments can be combined so that the <code>person</code> base
+ class provides the common data members and functions as well as
+ defines the polymorphic interface.</p>
+
+ <p>The following sections describe the available strategies for
+ mapping reuse and polymorphism inheritance styles to a relational
+ data model. Note also that the distinction between the two styles is
+ conceptual rather than formal. For example, it is possible to treat
+ a class hierarchy that defines virtual functions as a case of reuse
+ inheritance if this results in the desired database mapping and
+ semantics.</p>
+
+ <p>Generally, classes that employ reuse inheritance are mapped to
+ completely independent entities in the database. They use different
+ object id spaces and should always be passed to and returned from
+ the database operations as pointers or references to derived types.
+ In other words, from the persistence point of view, such classes
+ behave as if the data members from the base classes were copied
+ verbatim into the derived ones.</p>
+
+ <p>In contrast, classes that employ polymorphism inheritance share
+ the object id space and can be passed to and returned from the
+ database operations <em>polymorphically</em> as pointers or
+ references to the base class.</p>
+
+ <p>For both inheritance styles it is sometimes desirable to prevent
+ instances of a base class from being stored in the database.
+ To achieve this a persistent
+ class can be declared abstract using the <code>db&nbsp;abstract</code>
+ pragma (<a href="#14.1.3">Section 14.1.3, "<code>abstract</code>"</a>).
+ Note that a <em>C++-abstract</em> class, or a class that
+ has one or more pure virtual functions and therefore cannot be
+ instantiated, is also <em>database-abstract</em>. However, a
+ database-abstract class is not necessarily C++-abstract. The
+ ODB compiler automatically treats C++-abstract classes as
+ database-abstract.</p>
+
+ <h2><a name="8.1">8.1 Reuse Inheritance</a></h2>
+
+ <p>Each non-abstract class from the reuse inheritance hierarchy is
+ mapped to a separate database table that contains all its data
+ members, including those inherited from base classes. An abstract
+ persistent class does not have to define an object id, nor a default
+ constructor, and it does not have a corresponding database table.
+ An abstract class cannot be a pointed-to object in a relationship.
+ Multiple inheritance is supported as long as each base
+ class is only inherited once. The following example shows a
+ persistent class hierarchy employing reuse inheritance:</p>
+
+ <pre class="cxx">
+// Abstract person class. Note that it does not declare the
+// object id.
+//
+#pragma db object abstract
+class person
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+// Abstract employee class. It derives from the person class and
+// declares the object id for all the concrete employee types.
+//
+#pragma db object abstract
+class employee: public person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+};
+
+// Concrete permanent_employee class. Note that it doesn't define
+// any data members of its own.
+//
+#pragma db object
+class permanent_employee: public employee
+{
+ ...
+};
+
+// Concrete temporary_employee class. It adds the employment
+// duration in months.
+//
+#pragma db object
+class temporary_employee: public employee
+{
+ ...
+
+ unsigned long duration_;
+};
+
+// Concrete contractor class. It derives from the person class
+// (and not employee; an independent contractor is not considered
+// an employee). We use the contractor's external email address
+// as the object id.
+//
+#pragma db object
+class contractor: public person
+{
+ ...
+
+ #pragma db id
+ std::string email_;
+};
+ </pre>
+
+ <p>The sample database schema for this hierarchy is shown below.</p>
+
+ <pre class="sql">
+CREATE TABLE permanent_employee (
+ first TEXT NOT NULL,
+ last TEXT NOT NULL,
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT);
+
+CREATE TABLE temporary_employee (
+ first TEXT NOT NULL,
+ last TEXT NOT NULL,
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ duration BIGINT UNSIGNED NOT NULL);
+
+CREATE TABLE contractor (
+ first TEXT NOT NULL,
+ last TEXT NOT NULL,
+ email VARCHAR (128) NOT NULL PRIMARY KEY);
+ </pre>
+
+ <p>The complete version of the code presented in this section is
+ available in the <code>inheritance/reuse</code> example in the
+ <code>odb-examples</code> package.</p>
+
+ <h2><a name="8.2">8.2 Polymorphism Inheritance</a></h2>
+
+ <p>There are three general approaches to mapping a polymorphic
+ class hierarchy to a relational database. These are
+ <em>table-per-hierarchy</em>, <em>table-per-difference</em>,
+ and <em>table-per-class</em>. With the table-per-hierarchy
+ mapping, all the classes in a hierarchy are stored in a single,
+ "wide" table. <code>NULL</code> values are stored in columns
+ corresponding to data members of derived classes that are
+ not present in any particular instance.</p>
+
+ <p>In the table-per-difference mapping, each class is mapped
+ to a separate table. For a derived class, this table contains
+ only columns corresponding to the data members added by this
+ derived class.</p>
+
+ <p>Finally, in the table-per-class mapping, each class is mapped
+ to a separate table. For a derived class, this table contains
+ columns corresponding to all the data members, from this derived
+ class all the way down to the root of the hierarchy.</p>
+
+ <p>The table-per-difference mapping is generally considered as
+ having the best balance of flexibility, performance, and space
+ efficiency. It also results in a more canonical relational
+ database model compared to the other two approaches. As a
+ result, this is the mapping currently implemented in ODB.
+ Other mappings may be supported in the future. Note that
+ multiple polymorphism inheritance or mixing polymorphism and
+ reuse inheritance is not supported.</p>
+
+ <p>A pointer or reference to an ordinary, non-polymorphic object
+ has just one type &mdash; the class type of that object. When we
+ start working with polymorphic objects, there are two types
+ to consider: the <em>static type</em>, or the declaration type
+ of a reference or pointer, and the object's actual or <em>dynamic
+ type</em>. An example will help illustrate the difference:</p>
+
+ <pre class="cxx">
+class person {...};
+class employee: public person {...};
+
+person p;
+employee e;
+
+person&amp; r1 (p);
+person&amp; r2 (e);
+
+auto_ptr&lt;person> p1 (new employee);
+ </pre>
+
+ <p>In the above example, the <code>r1</code> reference's both static
+ and dynamic types are <code>person</code>.
+ In contrast, the <code>r2</code> reference's static type is
+ <code>person</code> while its dynamic type (the actual object
+ that it refers to) is <code>employee</code>. Similarly,
+ <code>p1</code> points to the object of the <code>person</code>
+ static type but <code>employee</code> dynamic type.</p>
+
+ <p>In C++, the primary mechanisms for working with polymorphic objects
+ are virtual functions. We call a virtual function only knowing the
+ object's static type, but the version corresponding to the object's
+ dynamic type is automatically executed. This is the essence of
+ runtime polymorphism support in C++: we can operate in terms of a base
+ class interface but get the derived class' behavior. Similarly, the
+ essence of the runtime polymorphism support in ODB is to allow us to
+ persist, load, update, and query in terms of the base class interface
+ but have the derived class actually stored in the database.</p>
+
+ <p>To declare a persistent class as polymorphic we use the
+ <code>db&nbsp;polymorphic</code> pragma. We only need to
+ declare the root class of a hierarchy as polymorphic; ODB will
+ treat all the derived classes as polymorphic automatically. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class person
+{
+ ...
+
+ virtual
+ ~person () = 0; // Automatically abstract.
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ bool temporary_;
+};
+
+#pragma db object
+class contractor: public person
+{
+
+ std::string email_;
+};
+ </pre>
+
+ <p>A persistent class hierarchy declared polymorphic must also be
+ polymorphic in the C++ sense, that is, the root class must
+ declare or inherit at least one virtual function. It is
+ recommended that the root class also declares a virtual destructor.
+ The root class of the polymorphic hierarchy must contain
+ the data member designated as object id (a persistent class
+ without an object id cannot be polymorphic). Note also that,
+ unlike reuse inheritance, abstract polymorphic classes have
+ a table in the database, just like non-abstract classes.</p>
+
+ <p>Persistent classes in the same polymorphic hierarchy must use the
+ same kind of object pointer (<a href="#3.3">Section 3.3,
+ "Object and View Pointers"</a>). If the object pointer
+ for the root class is specified as a template or using the
+ special raw pointer syntax (<code>*</code>), then the ODB
+ compiler will automatically use the same object pointer
+ for all the derived classes. For example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic pointer(std::shared_ptr)
+class person
+{
+ ...
+};
+
+#pragma db object // Object pointer is std::shared_ptr&lt;employee>.
+class employee: public person
+{
+ ...
+};
+
+#pragma db object // Object pointer is std::shared_ptr&lt;contractor>.
+class contractor: public person
+{
+ ...
+};
+ </pre>
+
+ <p>Similarly, if we enable or disable session support
+ (<a href="#11">Chapter 11, "Session"</a>) for the root class, then
+ the ODB compiler will automatically enable or disable it for all
+ the derived classes.</p>
+
+ <p>For polymorphic persistent classes, all the database operations can
+ be performed on objects with different static and dynamic types.
+ Similarly, operations that load persistent objects from the
+ database (<code>load()</code>, <code>query()</code>, etc.), can
+ return objects with different static and dynamic types. For
+ example:</p>
+
+ <pre class="cxx">
+unsigned long id1, id2;
+
+// Persist.
+//
+{
+ shared_ptr&lt;person> p1 (new employee (...));
+ shared_ptr&lt;person> p2 (new contractor (...));
+
+ transaction t (db.begin ());
+ id1 = db.persist (p1); // Stores employee.
+ id2 = db.persist (p2); // Stores contractor.
+ t.commit ();
+}
+
+// Load.
+//
+{
+ shared_ptr&lt;person> p;
+
+ transaction t (db.begin ());
+ p = db.load&lt;person> (id1); // Loads employee.
+ p = db.load&lt;person> (id2); // Loads contractor.
+ t.commit ();
+}
+
+// Query.
+//
+{
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ transaction t (db.begin ());
+
+ result r (db.query&lt;person> (query::last == "Doe"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ person&amp; p (*i); // Can be employee or contractor.
+ }
+
+ t.commit ();
+}
+
+// Update.
+//
+{
+ shared_ptr&lt;person> p;
+ shared_ptr&lt;employee> e;
+
+ transaction t (db.begin ());
+
+ e = db.load&lt;employee> (id1);
+ e->temporary (false);
+ p = e;
+ db.update (p); // Updates employee.
+
+ t.commit ();
+}
+
+// Erase.
+//
+{
+ shared_ptr&lt;person> p;
+
+ transaction t (db.begin ());
+ p = db.load&lt;person> (id1); // Loads employee.
+ db.erase (p); // Erases employee.
+ db.erase&lt;person> (id2); // Erases contractor.
+ t.commit ();
+}
+ </pre>
+
+
+ <p>The table-per-difference mapping, as supported by ODB, requires
+ two extra columns, in addition to those corresponding to the
+ data members. The first, called <em>discriminator</em>, is added
+ to the table corresponding to the root class of the hierarchy.
+ This column is used to determine the dynamic type of each
+ object. The second column is added to tables corresponding
+ to the derived classes and contains the object id. This
+ column is used to form a foreign key constraint referencing
+ the root class table.</p>
+
+ <p>When querying the database for polymorphic objects, it is
+ possible to obtain the discriminator value without
+ instantiating the object. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::result&lt;person> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;person> (query::last == "Doe"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ std::string d (i.discriminator ());
+ ...
+}
+
+t.commit ();
+ </pre>
+
+ <p>In the current implementation, ODB has limited support for
+ customizing names, types, and values of the extra columns.
+ Currently, the discriminator column is always called
+ <code>typeid</code> and contains a namespace-qualified class
+ name (for example, <code>"employee"</code> or
+ <code>"hr::employee"</code>). The id column in the derived
+ class table has the same name as the object id column in
+ the root class table. Future versions of ODB will add support
+ for customizing these extra columns.</p>
+
+ <p>The sample database schema for the above polymorphic hierarchy
+ is shown below.</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ typeid VARCHAR(128) NOT NULL,
+ first TEXT NOT NULL,
+ last TEXT NOT NULL);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ temporary TINYINT(1) NOT NULL,
+
+ CONSTRAINT employee_id_fk
+ FOREIGN KEY (id)
+ REFERENCES person (id)
+ ON DELETE CASCADE);
+
+CREATE TABLE contractor (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ email TEXT NOT NULL,
+
+ CONSTRAINT contractor_id_fk
+ FOREIGN KEY (id)
+ REFERENCES person (id)
+ ON DELETE CASCADE);
+ </pre>
+
+ <p>The complete version of the code presented in this section is
+ available in the <code>inheritance/polymorphism</code> example
+ in the <code>odb-examples</code> package.</p>
+
+ <h3><a name="8.2.1">8.2.1 Performance and Limitations</a></h3>
+
+ <p>A database operation on a non-polymorphic object normally translates
+ to a single database statement execution (objects with containers
+ and eager object pointers can be the exception). Because polymorphic
+ objects have their data members
+ stored in multiple tables, some database operations on such objects
+ may result in multiple database statements being executed while others
+ may require more complex statements. There is also some functionality
+ that is not available to polymorphic objects.</p>
+
+ <p>The first part of this section discusses the performance implications
+ to keep in mind when designing and working with polymorphic hierarchies.
+ The second part talks about limitations of polymorphic objects.</p>
+
+ <p>The most important aspect of a polymorphic hierarchy that
+ affects database performance is its depth. The distance between
+ the root of the hierarchy and the derived class translates
+ directly to the number of database statements that will have to
+ be executed in order to persist, update, or erase this derived class.
+ It also translates directly to the number of SQL <code>JOIN</code>
+ clauses that will be needed to load or query the database for this
+ derived class. As a result, to achieve best performance, we should
+ try to keep our polymorphic hierarchies as flat as possible.</p>
+
+ <p>When loading an object or querying the database for objects,
+ ODB will need to execute two statements if this object's static
+ and dynamic types are different but only one statement if
+ they are the same. This example will help illustrate the
+ difference:</p>
+
+ <pre class="cxx">
+unsigned long id;
+
+{
+ employee e (...);
+
+ transaction t (db.begin ());
+ id = db.persist (e);
+ t.commit ();
+}
+
+{
+ shared_ptr&lt;person> p;
+
+ transaction t (db.begin ());
+ p = db.load&lt;person> (id); // Requires two statement.
+ p = db.load&lt;employee> (id); // Requires only one statement.
+ t.commit ();
+}
+ </pre>
+
+ <p>As a result, we should try to load and query using the most
+ derived class possible.</p>
+
+ <p>Finally, for polymorphic objects, erasing via the object instance
+ is faster than erasing via its object id. In the former case the
+ object's dynamic type can be determined locally in the application
+ while in the latter case an extra statement has to be executed to
+ achieve the same result. For example:</p>
+
+ <pre class="cxx">
+shared_ptr&lt;person> p = ...;
+
+transaction t (db.begin ());
+db.erase&lt;person> (p.id ()); // Slower (executes extra statement).
+db.erase (p); // Faster.
+t.commit ();
+ </pre>
+
+ <p>Polymorphic objects can use all the mechanisms that are available
+ to ordinary objects. These include containers (<a href="#5">Chapter 5,
+ "Containers"</a>), object relationships, including to polymorphic
+ objects (<a href="#6">Chapter 6, "Relationships"</a>), views
+ (<a href="#10">Chapter 10, "Views"</a>), session (<a href="#11">Chapter
+ 11, "Session"</a>), and optimistic concurrency (<a href="#12">Chapter
+ 12, "Optimistic Concurrency"</a>). There are, however, a few
+ limitations, mainly due to the underlying use of SQL to access the
+ data.</p>
+
+ <p>When a polymorphic object is "joined" in a view, and the join
+ condition (either in the form of an object pointer or a custom
+ condition) comes from the object itself (as opposed to one of
+ the objects joined previously), then this condition must only
+ use data members from the derived class. For example, consider
+ the following polymorphic object hierarchy and a view:</p>
+
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class employee
+{
+ ...
+};
+
+#pragma db object
+class permanent_employee: public employee
+{
+ ...
+};
+
+#pragma db object
+class temporary_employee: public employee
+{
+ ...
+
+ shared_ptr&lt;permanent_employee> manager_;
+};
+
+#pragma db object
+class contractor: public temporary_employee
+{
+ shared_ptr&lt;permanent_employee> manager_;
+};
+
+#pragma db view object(permanent_employee) \
+ object(contractor: contractor::manager_)
+struct contractor_manager
+{
+ ...
+};
+ </pre>
+
+ <p>This view will not function correctly because the join condition
+ (<code>manager_</code>) comes from the base class
+ (<code>temporary_employee</code>) instead of the derived
+ (<code>contractor</code>). The reason for this limitation is the
+ <code>JOIN</code> clause order in the underlying SQL <code>SELECT</code>
+ statement. In the view presented above, the table corresponding
+ to the base class (<code>temporary_employee</code>) will have to
+ be joined first which will result in this view matching both
+ the <code>temporary_employee</code> and <code>contractor</code>
+ objects instead of just <code>contractor</code>. It is usually
+ possible to resolve this issue by reordering the objects in the
+ view. Our example, for instance, can be fixed by swapping the
+ two objects:</p>
+
+ <pre class="cxx">
+#pragma db view object(contractor) \
+ object(permanent_employee: contractor::manager_)
+struct contractor_manager
+{
+ ...
+};
+ </pre>
+
+ <p>The <code>erase_query()</code> database function (<a href="#3.11">Section
+ 3.11, "Deleting Persistent Objects"</a>) also has limited functionality
+ when used on polymorphic objects. Because many database implementations
+ do not support <code>JOIN</code> clauses in the SQL <code>DELETE</code>
+ statement, only data members from the derived class being erased can
+ be used in the query condition. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee> query;
+
+transaction t (db.begin ());
+db.erase_query&lt;employee> (query::permanent); // Ok.
+db.erase_query&lt;employee> (query::last == "Doe"); // Error.
+t.commit ();
+ </pre>
+
+ <h2><a name="8.3">8.3 Mixed Inheritance</a></h2>
+
+ <p>It is possible to mix the reuse and polymorphism inheritance
+ styles in the same hierarchy. In this case, the reuse inheritance
+ must be used for the "bottom" (base) part of the hierarchy while
+ the polymorphism inheritance &mdash; for the "top" (derived) part.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+};
+
+#pragma db object polymorphic
+class employee: public person // Reuse inheritance.
+{
+ ...
+};
+
+#pragma db object
+class temporary_employee: public employee // Polymorphism inheritance.
+{
+ ...
+};
+
+#pragma db object
+class permanent_employee: public employee // Polymorphism inheritance.
+{
+ ...
+};
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="9">9 Sections</a></h1>
+
+ <p>ODB sections are an optimization mechanism that allows us to
+ partition data members of a persistent class into groups that
+ can be separately loaded and/or updated. This can be useful,
+ for example, if an object contains expensive to load or update
+ data members (such as <code>BLOB</code>s or containers) and
+ that are accessed or modified infrequently. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/section.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy) update(manual)
+ odb::section keys_;
+
+ #pragma db section(keys_) type("BLOB")
+ char public_key_[1024];
+
+ #pragma db section(keys_) type("BLOB")
+ char private_key_[1024];
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;person> p (db.load&lt;person> (...)); // Keys are not loaded.
+
+if (need_keys)
+{
+ db.load (*p, p->keys_); // Load keys.
+ ...
+}
+
+db.update (*p); // Keys are not updated.
+
+if (update_keys)
+{
+ ...
+ db.update (*p, p->keys_); // Update keys.
+}
+
+t.commit ();
+ </pre>
+
+ <p>A complete example that shows how to use sections is available in
+ the <code>section</code> directory in the <code>odb-examples</code>
+ package.</p>
+
+ <p>Why do we need to group data members into sections? Why can't
+ each data member be loaded and updated independently if and
+ when necessary? The reason for this requirement is that loading
+ or updating a group of data members with a single database
+ statement is significantly more efficient than loading or updating
+ each data member with a separate statement. Because ODB
+ prepares and caches statements used to load and update
+ persistent objects, generating a custom statement for
+ a specific set of data members that need to be loaded or
+ updated together is not a viable approach either. To resolve
+ this, ODB allows us to group data members that are
+ often updated and/or loaded together into sections. To
+ achieve the best performance, we should aim to find a balance
+ between having too many sections with too few data
+ members and too few sections with too many data
+ members. We can use the access and modification patterns
+ of our application as a base for this decision.</p>
+
+ <p>To add a new section to a persistent class we declare a new
+ data member of the <code>odb::section</code> type. At this
+ point we also need to specify the loading and updating behavior
+ of this section with the <code>db&nbsp;load</code> and
+ <code>db&nbsp;update</code> pragmas, respectively.</p>
+
+ <p>The loading behavior of a section can be either <code>eager</code>
+ or <code>lazy</code>. An eager-loaded section is always loaded as
+ part of the object load. A lazy-loaded section is not loaded
+ as part of the object load and has to be explicitly loaded with
+ the <code>database::load()</code> function (discussed below) if
+ and when necessary.</p>
+
+ <p>The updating behavior of a section can be <code>always</code>,
+ <code>change</code>, or <code>manual</code>. An always-updated
+ section is always updated as part of the object update,
+ provided it has been loaded. A change-updated section
+ is only updated as part of the object update if it has been loaded
+ and marked as changed. A manually-updated section is never updated
+ as part of the object update and has to be explicitly updated with
+ the <code>database::update()</code> function (discussed below) if
+ and when necessary.</p>
+
+ <p>If no loading behavior is specified explicitly, then an eager-loaded
+ section is assumed. Similarly, if no updating behavior is specified,
+ then an always-updated section is assumed. An eager-loaded, always-updated
+ section is pointless and therefore illegal. Only persistent classes
+ with an object id can have sections.</p>
+
+ <p>To specify that a data member belongs to a section we use the
+ <code>db&nbsp;section</code> pragma with the section's member
+ name as its single argument. Except for special data members
+ such as the object id and optimistic concurrency version, any
+ direct, non-transient member of a persistent class can belong
+ to a section, including composite values, containers, and
+ pointers to objects. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class text
+{
+ std::string data;
+ std::string lang;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ text bio_;
+
+ #pragma db section(extras_)
+ std::vector&lt;std::string> nicknames_;
+
+ #pragma db section(extras_)
+ std::shared_ptr&lt;person> emergency_contact_;
+};
+ </pre>
+
+ <p>An empty section is pointless and therefore illegal, except
+ in abstract or polymorphic classes where data members can be
+ added to a section by derived classes (see <a href="#9.1">Section
+ 9.1, "Sections and Inheritance"</a>).</p>
+
+ <p>The <code>odb::section</code> class is defined in the
+ <code>&lt;odb/section.hxx></code> header file and has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class section
+ {
+ public:
+ // Load state.
+ //
+ bool
+ loaded () const;
+
+ void
+ unload ();
+
+ void
+ load ();
+
+ // Change state.
+ //
+ bool
+ changed () const;
+
+ void
+ change ();
+
+ // User data.
+ //
+ unsigned char
+ user_data () const;
+
+ void
+ user_data (unsigned char);
+ };
+}
+ </pre>
+
+ <p>The <code>loaded()</code> accessor can be used to determine whether a
+ section is already loaded. The <code>unload()</code> modifier marks a
+ loaded section as not loaded. This, for example, can be useful if you
+ don't want the section to be reloaded during the object reload. The
+ <code>load()</code> modifier marks an unloaded section as loaded
+ without actually loading any of its data members. This, for example,
+ can be useful if you don't want to load the old state before overwriting
+ it with <code>update()</code>.</p>
+
+ <p>The <code>changed()</code> accessor can be used to query the
+ section's change state. The <code>change()</code> modifier
+ marks the section as changed. It is valid to call this modifier
+ for an unloaded (or transient) section, however, the state will
+ be reset back to unchanged once the section (or object) is loaded.
+ The change state is only relevant to sections with change-updated
+ behavior and is ignored for all other sections.</p>
+
+ <p>The size of the section class is one byte with four bits available
+ to store a custom state via the <code>user_data()</code> accessor
+ and modifier.</p>
+
+ <p>The <code>odb::database</code> class provides special
+ versions of the <code>load()</code> and <code>update()</code>
+ functions that allow us to load and update sections of a
+ persistent class. Their signatures are as follows:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ load (T&amp; object, section&amp;);
+
+ template &lt;typename T>
+ void
+ update (const T&amp; object, const section&amp;);
+ </pre>
+
+ <p>Before calling the section <code>load()</code> function, the
+ object itself must already be loaded. If the section is already
+ loaded, then the call to <code>load()</code> will reload its
+ data members. It is illegal to explicitly load an eager-loaded
+ section.</p>
+
+ <p>Before calling the section <code>update()</code> function, the
+ section (and therefore the object) must be in the loaded state.
+ If the section is not loaded, the <code>odb::section_not_loaded</code>
+ exception is thrown. The section <code>update()</code> function
+ does not check but does clear the section's change state. In
+ other words, section <code>update()</code> will always update
+ section data members in the database and clear the change flag.
+ Note also that any section, that is, always-, change-, or
+ manually-updated, can be explicitly updated with this function.</p>
+
+ <p>Both section <code>load()</code> and <code>update()</code>, just
+ like the rest of the database operations, must be performed within
+ a transaction. Notice also that both <code>load()</code> and
+ <code>update()</code> expect a reference to the section as
+ their second argument. This reference must refer to the data
+ member in the object passed as the first argument. If instead
+ it refers to some other instance of the <code>section</code>
+ class, for example, a local copy or a temporary, then the
+ <code>odb::section_not_in_object</code> exception is thrown.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ ...
+
+ odb::section
+ keys () const {return keys_;}
+
+private:
+ odb::section keys_;
+
+ ...
+};
+
+auto_ptr&lt;person> p (db.load&lt;person> (...));
+
+section s (p->keys ());
+db.load (*p, s); // Throw section_not_in_object, copy.
+
+db.update (*p, p->keys ()); // Throw section_not_in_object, copy.
+ </pre>
+
+ <p>At first glance it may seem more appropriate to make the
+ <code>section</code> class non-copyable in order to prevent
+ such errors from happening. However, it is perfectly reasonable
+ to expect to be able to copy (or assign) sections as part of
+ the object copying (or assignment). As a result, sections are
+ left copyable and copy-assignable, however, this functionality
+ should not be used in accessors or modifiers. Instead, section
+ accessors and modifiers should always be by-reference. Here is
+ how we can fix our previous example:</p>
+
+<pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ ...
+
+ const odb::section&amp;
+ keys () const {return keys_;}
+
+ odb::section&amp;
+ keys () {return keys_;}
+
+private:
+ odb::section keys_;
+
+ ...
+};
+
+auto_ptr&lt;person> p (db.load&lt;person> (...));
+
+section&amp; s (p->keys ());
+db.load (*p, s); // Ok, reference.
+
+db.update (*p, p->keys ()); // Ok, reference.
+ </pre>
+
+ <p>Several other database operations affect sections. The state of
+ a section in a transient object is undefined. That is, before
+ the call to object <code>persist()</code> or <code>load()</code>
+ functions, or after the call to object <code>erase()</code>
+ function, the values returned by the <code>section::loaded()</code> and
+ <code>section::changed()</code> accessors are undefined.</p>
+
+ <p>After the call to <code>persist()</code>, all sections, including
+ eager-loaded ones, are marked as loaded and unchanged. If instead we
+ are loading an object with the <code>load()</code> call or as
+ a result of a query, then eager-loaded sections are loaded
+ and marked as loaded and unchanged while lazy-loaded ones are marked
+ as unloaded. If a lazy-loaded section is later loaded with the
+ section <code>load()</code> call, then it is marked as loaded and
+ unchanged.</p>
+
+ <p>When we update an object with the <code>update()</code> call,
+ manually-updated sections are ignored while always-updated
+ sections are updated if they are loaded. Change-updated
+ sections are only updated if they are both loaded and marked
+ as changed. After the update, such sections are reset to the
+ unchanged state. When we reload an object with the
+ <code>reload()</code> call, sections that were loaded are
+ automatically reloaded and reset to the unchanged state.</p>
+
+ <p>To further illustrate the state transitions of a section,
+ consider this example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy) update(change)
+ odb::section keys_;
+
+ ...
+};
+
+transaction t (db.begin ());
+
+person p ("John", "Doe"); // Section state is undefined (transient).
+
+db.persist (p); // Section state: loaded, unchanged.
+
+auto_ptr&lt;person> l (
+ db.load&lt;person> (...)); // Section state: unloaded, unchanged.
+
+db.update (*l); // Section not updated since not loaded.
+db.update (p); // Section not updated since not changed.
+
+p.keys_.change (); // Section state: loaded, changed.
+db.update (p); // Section updated, state: loaded, unchanged.
+
+db.update (*l, l->keys_); // Throw section_not_loaded.
+db.update (p, p.keys_); // Section updated even though not changed.
+
+db.reload (*l); // Section not reloaded since not loaded.
+db.reload (p); // Section reloaded, state: loaded, unchanged.
+
+db.load (*l, l->keys_); // Section loaded, state: loaded, unchanged.
+db.load (p, p.keys_); // Section reloaded, state: loaded, unchanged.
+
+db.erase (p); // Section state is undefined (transient).
+
+t.commit ();
+ </pre>
+
+ <p>When using change-updated behavior, it is our responsibility to
+ mark the section as changed when any of the data members belonging
+ to this section is modified. A natural place to mark the section
+ as changed is the modifiers for section data members, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ typedef std::array&lt;char, 1024> key_type;
+
+ const key_type&amp;
+ public_key () const {return public_key_;}
+
+ void
+ public_key (const key_type&amp; k)
+ {
+ public_key_ = k;
+ keys_.change ();
+ }
+
+ const key_type&amp;
+ private_key () const {return private_key_;}
+
+ void
+ private_key (const key_type&amp; k)
+ {
+ private_key_ = k;
+ keys_.change ();
+ }
+
+private:
+ #pragma db load(lazy) update(change)
+ odb::section keys_;
+
+ #pragma db section(keys_) type("BLOB")
+ key_type public_key_;
+
+ #pragma db section(keys_) type("BLOB")
+ key_type private_key_;
+
+ ...
+};
+ </pre>
+
+ <p>One interesting aspect of change-updated sections is what happens
+ when a transaction that performed an object or section update is
+ later rolled back. In this case, while the change state of a
+ section has been reset (after update), actual changes were not
+ committed to the database. Change-updated sections handle this
+ case by automatically registering a rollback callback and then,
+ if it is called, restoring the original change state. The
+ following code illustrates this semantics (continuing with
+ the previous example):</p>
+
+ <pre class="cxx">
+auto_ptr&lt;person> p;
+
+try
+{
+ transaction t (db.begin ());
+ p = db.load&lt;person> (...);
+ db.load (*p, p->keys_);
+
+ p->private_key (new_key); // The section is marked changed.
+ db.update (*p); // The section is reset to unchanged.
+
+ throw failed (); // Triggers rollback.
+ t.commit ();
+}
+catch (const failed&amp;)
+{
+ // The section is restored back to changed.
+}
+ </pre>
+
+
+ <h2><a name="9.1">9.1 Sections and Inheritance</a></h2>
+
+ <p>With both reuse and polymorphism inheritance (<a href="#8">Chapter 8,
+ "Inheritance"</a>) it is possible to add new sections to derived
+ classes. It is also possible to add data members from derived
+ classes to sections declared in the base. For example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class person
+{
+ ...
+
+ virtual void
+ print ();
+
+ #pragma db load(lazy)
+ odb::section print_;
+
+ #pragma db section(print_)
+ std::string bio_;
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ virtual void
+ print ();
+
+ #pragma db section(print_)
+ std::vector&lt;std::string> employment_history_;
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;person> p (db.load&lt;person> (...)); // Person or employee.
+db.load (*p, p->print_); // Load data members needed for print.
+p->print ();
+
+t.commit ();
+ </pre>
+
+ <p>When data members of a section are spread over several classes in a
+ reuse inheritance hierarchy, both section load and update are
+ performed with a single database statement. In contrast, with
+ polymorphism inheritance, section load is performed with a
+ single statement while update requires a separate statement
+ for each class that adds to the section.</p>
+
+ <p>Note also that in polymorphism inheritance the section-to-object
+ association is static. Or, in other words, you can load a section
+ via an object only if its static type actually contains this
+ section. The following example will help illustrate this
+ point further:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class person
+{
+ ...
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ ...
+};
+
+#pragma db object
+class manager: public employee
+{
+ ...
+};
+
+auto_ptr&lt;manager> m (db.load&lt;manager> (...));
+
+person&amp; p (*m);
+employee&amp; e (*m);
+section&amp; s (m->extras_);
+
+db.load (p, s); // Error: extras_ is not in person.
+db.load (e, s); // Ok: extras_ is in employee.
+ </pre>
+
+ <h2><a name="9.2">9.2 Sections and Optimistic Concurrency</a></h2>
+
+ <p>When sections are used in a class with the optimistic concurrency
+ model (<a href="#12">Chapter 12, "Optimistic Concurrency"</a>),
+ both section update and load operations compare the object version
+ to that in the database and throw the <code>odb::object_changed</code>
+ exception if they do not match. In addition, the section update
+ operation increments the version to indicate that the object state
+ has changed. For example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long long version_;
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ std::string bio_;
+};
+
+auto_ptr&lt;person> p;
+
+{
+ transaction t (db.begin ());
+ p = db.load&lt;person> (...);
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ try
+ {
+ db.load (*p, p->extras_); // Throws if object state has changed.
+ }
+ catch (const object_changed&amp;)
+ {
+ db.reload (*p);
+ db.load (*p, p->extras_); // Cannot fail.
+ }
+
+ t.commit ();
+}
+ </pre>
+
+ <p>Note also that if an object update triggers one or more
+ section updates, then each such update will increment the
+ object version. As a result, an update of an object that
+ contains sections may result in a version increment by
+ more than one.</p>
+
+ <p>When sections are used together with optimistic concurrency and
+ inheritance, an extra step may be required to enable this
+ functionality. If you plan to add new sections to derived
+ classes, then the root class of the hierarchy
+ (the one that declares the version data member) must be
+ declared as sectionable with the <code>db&nbsp;sectionable</code>
+ pragma. For example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic sectionable
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long long version_;
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ std::vector&lt;std::string> employment_history_;
+};
+ </pre>
+
+ <p>This requirement has to do with the need to generate extra
+ version increment code in the root class that will be used
+ by sections added in the derived classes. If you forget to
+ declare the root class as sectionable and later add a
+ section to one of the derived classes, the ODB compiler
+ will issue diagnostics.</p>
+
+ <h2><a name="9.3">9.3 Sections and Lazy Pointers</a></h2>
+
+ <p>If a lazy pointer (<a href="#6.4">Section 6.4, "Lazy Pointers"</a>)
+ belongs to a lazy-loaded section, then we end up with two levels of
+ lazy loading. Specifically, when the section is loaded, the lazy
+ pointer is initialized with the object id but the object itself
+ is not loaded. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ odb::lazy_shared_ptr&lt;employer> employer_;
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;employee> e (db.load&lt;employee> (...)); // employer_ is NULL.
+
+db.load (*e, e->extras_); // employer_ contains valid employer id.
+
+e->employer_.load (); // employer_ points to employer object.
+
+t.commit ();
+ </pre>
+
+ <h2><a name="9.4">9.4 Sections and Change-Tracking Containers</a></h2>
+
+ <p>If a change-tracking container (<a href="#5.4">Section 5.4,
+ "Change-Tracking Containers"</a>) belongs to a change-updated
+ section, then prior to an object update ODB will check if the
+ container has been changed and if so, automatically mark the
+ section as changed. For example:</p>
+
+<pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy) update(change)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ odb::vector&lt;std::string> nicknames_;
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;person> p (db.load&lt;person> (...));
+db.load (*p, p->extras_);
+
+p->nicknames_.push_back ("JD");
+
+db.update (*p); // Section is automatically updated even
+ // though it was not marked as changed.
+t.commit ();
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="10">10 Views</a></h1>
+
+ <p>An ODB view is a C++ <code>class</code> or <code>struct</code> type
+ that embodies a light-weight, read-only projection of one or more
+ persistent objects or database tables or the result of a native SQL
+ query execution.</p>
+
+ <p>Some of the common applications of views include loading a subset
+ of data members from objects or columns from database tables, executing
+ and handling results of arbitrary SQL queries, including aggregate
+ queries and stored procedure calls, as well as joining multiple
+ objects and/or database tables using object relationships or custom
+ join conditions.</p>
+
+ <p>Many relational databases also define the concept of views. Note,
+ however, that ODB views are not mapped to database views. Rather,
+ by default, an ODB view is mapped to an SQL <code>SELECT</code>
+ query. However, if desired, it is easy to create an ODB view
+ that is based on a database view.</p>
+
+ <p>Usually, views are defined in terms of other persistent entities,
+ such as persistent objects, database tables, sequences, etc.
+ Therefore, before we can examine our first view, we need to
+ define a few persistent objects and a database table. We will
+ use this model in examples throughout this chapter. Here we
+ assume that you are familiar with ODB object relationship
+ support (<a href="#6">Chapter 6, "Relationships"</a>).</p>
+
+ <pre class="cxx">
+#pragma db object
+class country
+{
+ ...
+
+ #pragma db id
+ std::string code_; // ISO 2-letter country code.
+
+ std::string name_;
+};
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+
+ unsigned short age_;
+
+ shared_ptr&lt;country> residence_;
+ shared_ptr&lt;country> nationality_;
+
+ shared_ptr&lt;employer> employed_by_;
+};
+ </pre>
+
+ <p>Besides these objects, we also have the legacy
+ <code>employee_extra</code> table that is not mapped to any persistent
+ class. It has the following definition:</p>
+
+ <pre class="sql">
+CREATE TABLE employee_extra(
+ employee_id INTEGER NOT NULL,
+ vacation_days INTEGER NOT NULL,
+ previous_employer_id INTEGER)
+ </pre>
+
+ <p>The above persistent objects and database table as well as many of
+ the views shown in this chapter are based on the
+ <code>view</code> example which can be found in the
+ <code>odb-examples</code> package of the ODB distribution.</p>
+
+ <p>To declare a view we use the <code>db&nbsp;view</code> pragma,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_name
+{
+ std::string first;
+ std::string last;
+};
+ </pre>
+
+ <p>The above example shows one of the simplest views that we can create.
+ It has a single associated object (<code>employee</code>) and its
+ purpose is to extract the employee's first and last names without
+ loading any other data, such as the referenced <code>country</code>
+ and <code>employer</code> objects.</p>
+
+ <p>Views use the same query facility (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) as persistent objects. Because support for queries
+ is optional and views cannot be used without this support, you need
+ to compile any header that defines a view with the
+ <code>--generate-query</code> ODB compiler option.</p>
+
+ <p>To query the database for a view we use the
+ <code>database::query()</code>, <code>database::query_one()</code>, or
+ <code>database::query_value()</code> functions in exactly the same way
+ as we would use them to query the database for an object. For example,
+ the following code fragment shows how we can find the names of all the
+ employees that are younger than 31:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_name> query;
+typedef odb::result&lt;employee_name> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_name> (query::age &lt; 31));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ const employee_name&amp; en (*i);
+ cout &lt;&lt; en.first &lt;&lt; " " &lt;&lt; en.last &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>A view can be defined as a projection of one or more objects, one
+ or more tables, a combination of objects and tables, or it can be
+ the result of a custom SQL query. The following sections discuss each
+ of these kinds of view in more detail.</p>
+
+ <h2><a name="10.1">10.1 Object Views</a></h2>
+
+ <p>To associate one or more objects with a view we use the
+ <code>db&nbsp;object</code> pragma (<a href="#14.2.1">Section
+ 14.2.1, "<code>object</code>"</a>). We have already seen
+ a simple, single-object view in the introduction to this chapter.
+ To associate the second and subsequent objects we repeat the
+ <code>db&nbsp;object</code> pragma for each additional object,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ std::string first;
+ std::string last;
+ std::string name;
+};
+ </pre>
+
+ <p>The complete syntax of the <code>db&nbsp;object</code> pragma is
+ shown below:</p>
+
+ <p><code><b>object(</b><i>name</i>
+ [<b>=</b> <i>alias</i>]
+ [<i>join-type</i>]
+ [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
+
+ <p>The <i>name</i> part is a potentially qualified persistent class
+ name that has been defined previously. The optional <i>alias</i>
+ part gives this object an alias. If provided, the alias is used
+ in several contexts instead of the object's unqualified name. We
+ will discuss aliases further as we cover each of these contexts
+ below. The optional <i>join-type</i> part specifies the way this
+ object is associated. It can be <code>left</code>, <code>right</code>,
+ <code>full</code>, <code>inner</code>, and <code>cross</code>
+ with <code>left</code> being the default.
+ Finally, the optional <i>join-condition</i> part provides the
+ criteria which should be used to associate this object with any
+ of the previously associated objects or, as we will see in
+ <a href="#10.4">Section 10.4, "Mixed Views"</a>, tables. Note that
+ while the first associated object can have an alias, it cannot
+ have a join type or condition.</p>
+
+ <p>For each subsequent associated object the ODB compiler needs
+ a join condition and there are several ways to specify
+ it. The easiest way is to omit it altogether and let the ODB
+ compiler try to come up with a join condition automatically.
+ To do this the ODB compiler will examine each previously
+ associated object for object relationships
+ (<a href="#6">Chapter 6, "Relationships"</a>) that
+ may exist between these objects and the object being associated.
+ If such a relationship exists and is unambiguous, that is
+ there is only one such relationship, then the ODB compiler
+ will automatically use it to come up with the join condition for
+ this object. This is exactly what happens in the previous
+ example: there is a single relationship
+ (<code>employee::employed_by</code>) between the
+ <code>employee</code> and <code>employer</code> objects.</p>
+
+ <p>On the other hand, consider this view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(country)
+struct employee_residence
+{
+ std::string first;
+ std::string last;
+ std::string name;
+};
+ </pre>
+
+ <p>While there is a relationship between <code>country</code> and
+ <code>employee</code>, it is ambiguous. It can be
+ <code>employee::residence_</code> (which is what we want) or
+ it can be <code>employee::nationality_</code> (which we don't
+ want). As result, when compiling the above view, the ODB
+ compiler will issue an error indicating an ambiguous object
+ relationship. To resolve this ambiguity, we can explicitly
+ specify the object relationship that should be used to create
+ the join condition as the name of the corresponding data member.
+ Here is how we can fix the <code>employee_residence</code>
+ view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(country: employee::residence_)
+struct employee_residence
+{
+ std::string first;
+ std::string last;
+ std::string name;
+};
+ </pre>
+
+ <p>It is possible to associate the same object with a single view
+ more than once using different join conditions. However, in
+ this case, we have to use aliases to assign different names
+ for each association. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res_country: employee::residence_) \
+ object(country = nat_country: employee::nationality_)
+struct employee_country
+{
+ ...
+};
+ </pre>
+
+ <p>Note that correctly defining data members in this view requires
+ the use of a mechanism that we haven't yet covered. We will
+ see how to do this shortly.</p>
+
+ <p>If we assign an alias to an object and refer to a data member of
+ this object in one of the join conditions, we have to use the
+ unqualified alias name instead of the potentially qualified
+ object name. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee = ee) object(country: ee::residence_)
+struct employee_residence
+{
+ ...
+};
+ </pre>
+
+ <p>The last way to specify a join condition is to provide a custom
+ query expression. This method is primarily useful if you would
+ like to associate an object using a condition that does not
+ involve an object relationship. Consider, for example, a
+ modified <code>employee</code> object from the beginning of
+ the chapter with an added country of birth member. For one
+ reason or another we have decided not to use a relationship to
+ the <code>country</code> object, as we have done with
+ residence and nationality.</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ std::string birth_place_; // Country name.
+};
+ </pre>
+
+ <p>If we now want to create a view that returns the birth country code
+ for an employee, then we have to use a custom join condition when
+ associating the <code>country</code> object. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country: employee::birth_place_ == country::name_)
+struct employee_birth_code
+{
+ std::string first;
+ std::string last;
+ std::string code;
+};
+ </pre>
+
+ <p>The syntax of the query expression in custom join conditions
+ is the same as in the query facility used to query the database
+ for objects (<a href="#4">Chapter 4, "Querying the Database"</a>)
+ except that for query members, instead of using
+ <code>odb::query&lt;object>::member</code> names, we refer directly
+ to object members.</p>
+
+ <p>Looking at the views we have defined so far, you may be wondering
+ how the ODB compiler knows which view data members correspond to which
+ object data members. While the names are similar, they are not exactly
+ the same, for example <code>employee_name::first</code> and
+ <code>employee::first_</code>.</p>
+
+ <p>As with join conditions, when it comes to associating data members,
+ the ODB compiler tries to do this automatically. It first searches
+ all the associated objects for an exact name match. If no match is
+ found, then the ODB compiler compares the so-called public names.
+ A public name of a member is obtained by removing the common member
+ name decorations, such as leading and trailing underscores, the
+ <code>m_</code> prefix, etc. In both of these searches the ODB
+ compiler also makes sure that the types of the two members are the
+ same or compatible.</p>
+
+ <p>If one of the above searches returned a match and it is unambiguous, that
+ is there is only one match, then the ODB compiler will automatically
+ associate the two members. On the other hand, if no match is found
+ or the match is ambiguous, the ODB compiler will issue an error.
+ To associate two differently-named members or to resolve an ambiguity,
+ we can explicitly specify the member association using the
+ <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section 14.4.9,
+ "<code>column</code>"</a>). For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column(employer::name_)
+ std::string employer_name;
+};
+ </pre>
+
+ <p>If an object data member specifies the SQL type with
+ the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
+ 14.4.3, "<code>type</code>"</a>), then this type is also used for
+ the associated view data members.</p>
+
+ <p>Note also that similar to join conditions, if we assign an alias to
+ an object and refer to a data member of this object in one of the
+ <code>db&nbsp;column</code> pragmas, then we have to use the
+ unqualified alias name instead of the potentially qualified
+ object name. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res_country: employee::residence_) \
+ object(country = nat_country: employee::nationality_)
+struct employee_country
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column(res_country::name_)
+ std::string res_country_name;
+
+ #pragma db column(nat_country::name_)
+ std::string nat_country_name;
+};
+ </pre>
+
+ <p>Besides specifying just the object member, we can also specify a
+ <em>+-expression</em> in the <code>db&nbsp;column</code> pragma. A
+ +-expression consists of string literals and object
+ member references connected using the <code>+</code> operator.
+ It is primarily useful for defining aggregate views based on
+ SQL aggregate functions, for example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_count
+{
+ #pragma db column("count(" + employee::id_ + ")")
+ std::size_t count;
+};
+ </pre>
+
+ <p>When querying the database for a view, we may want to provide
+ additional query criteria based on the objects associated with
+ this view. To support this a view defines query members for all
+ the associated objects which allows us to refer to such objects'
+ members using the <code>odb::query&lt;view>::member</code> expressions.
+ This is similar to how we can refer to object members using the
+ <code>odb::query&lt;object>::member</code> expressions when
+ querying the database for an object. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_count> query;
+
+transaction t (db.begin ());
+
+// Find the number of employees with the Doe last name. Result of this
+// aggregate query contains only one element so use the query_value()
+// shortcut function.
+//
+employee_count ec (
+ db.query_value&lt;employee_count> (query::last == "Doe"));
+
+cout &lt;&lt; ec.count &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>In the above query we used the last name data member from the associated
+ <code>employee</code> object to only count employees with the specific
+ name.</p>
+
+ <p>When a view has only one associated object, the query members
+ corresponding to this object are defined directly in the
+ <code>odb::query&lt;view></code> scope. For instance,
+ in the above example, we referred to the last name member as
+ <code>odb::query&lt;employee_count>::last</code>. However, if
+ a view has multiple associated objects, then query members
+ corresponding to each such object are defined in a nested
+ scope named after the object. As an example, consider
+ the <code>employee_employer</code> view again:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column(employer::name_)
+ std::string employer_name;
+};
+ </pre>
+
+ <p>Now, to refer to the last name data member from the <code>employee</code>
+ object we use the
+ <code>odb::query&lt;...>::employee::last</code> expression.
+ Similarly, to refer to the employer name, we use the
+ <code>odb::query&lt;...>::employer::name</code> expression.
+ For example:</p>
+
+ <pre class="cxx">
+typedef odb::result&lt;employee_employer> result;
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_employer> (
+ query::employee::last == "Doe" &amp;&amp;
+ query::employer::name == "Simple Tech Ltd"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->employer_name &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>If we assign an alias to an object, then this alias is used to
+ name the query members scope instead of the object name. As an
+ example, consider the <code>employee_country</code> view again:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res_country: employee::residence_) \
+ object(country = nat_country: employee::nationality_)
+struct employee_country
+{
+ ...
+};
+ </pre>
+
+ <p>And a query which returns all the employees that have the same
+ country of residence and nationality:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_country> query;
+typedef odb::result&lt;employee_country> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_country> (
+ query::res_country::name == query::nat_country::name));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->res_country_name &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Note also that unlike object query members, view query members do
+ no support referencing members in related objects. For example,
+ the following query is invalid:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_name> query;
+typedef odb::result&lt;employee_name> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_name> (
+ query::employed_by->name == "Simple Tech Ltd"));
+
+t.commit ();
+ </pre>
+
+ <p>To get this behavior, we would instead need to associate the
+ <code>employer</code> object with this view and then use the
+ <code>query::employer::name</code> expression instead of
+ <code>query::employed_by->name</code>.</p>
+
+ <p>As we have discussed above, if specified, an object alias is
+ used instead of the object name in the join condition, data
+ member references in the <code>db&nbsp;column</code> pragma,
+ as well as to name the query members scope. The object alias
+ is also used as a table name alias in the underlying
+ <code>SELECT</code> statement generated by the ODB compiler.
+ Normally, you would not use the table alias directly with
+ object views. However, if for some reason you need to refer
+ to a table column directly, for example, as part of a native
+ query expression, and you need to qualify the column with
+ the table, then you will need to use the table alias instead.</p>
+
+ <h2><a name="10.2">10.2 Object Loading Views</a></h2>
+
+ <p>A special variant of object views is object loading views. Object
+ loading views allow us to load one or more complete objects
+ instead of, or in addition to, a subset of data member. While we
+ can often achieve the same end result by calling
+ <code>database::load()</code>, using a view has several advantages.</p>
+
+ <p>If we need to load multiple objects, then using a view allows us
+ to do this with a single <code>SELECT</code> statement execution
+ instead of one for each object that would be necessary in case of
+ <code>load()</code>. A view can also be useful for loading only
+ a single object if the query criterion that we would like to use
+ involves other, potentially unrelated, objects. We will examine
+ concrete examples of these and other scenarios in the rest of this
+ section.</p>
+
+ <p>To load a complete object as part of a view we use a data member of
+ the pointer to object type, just like for object relationships
+ (<a href="#6">Chapter 6, "Relationships"</a>). As an example, here
+ is how we can load both the <code>employee</code> and
+ <code>employer</code> objects from the previous section with a single
+ statement:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ shared_ptr&lt;employee> ee;
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>We use an object loading view just like any other view. In the
+ result of a query, as we would expect, the pointer data members
+ point to the loaded objects. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+
+for (const employee_employer&amp; r:
+ db.query&lt;employee_employer> (query::employee::age &lt; 31))
+{
+ cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>As another example, consider a query that loads the <code>employer</code>
+ objects using some condition based on its employees. For instance, we
+ want to find all the employers that employ people over 65 years old.
+ We can use this object loading view to implement such a query (notice
+ the <code>distinct</code> result modifier discussed later in
+ <a href="#10.5">Section 10.5, "View Query Conditions"</a>):</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) object(employee) query(distinct)
+struct employer_view
+{
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>And this is how we can use this view to find all the employers that
+ employ seniors:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employer_view> query;
+
+db.query&lt;employer_view> (query::employee::age > 65)
+ </pre>
+
+ <p>We can even use object loading views to load completely unrelated
+ (from the ODB object relationships point of view) objects. For example,
+ the following view will load all the employers that are named the
+ same as a country (notice the <code>inner</code> join type):</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) \
+ object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+ shared_ptr&lt;employer> e;
+ shared_ptr&lt;country> c;
+};
+ </pre>
+
+ <p>An object loading view can contain ordinary data members
+ in addition to object pointers. For example, if we are only
+ interested in the country code in the above view, then we
+ can reimplement it like this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) \
+ object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+ shared_ptr&lt;employer> e;
+ std::string code;
+};
+ </pre>
+
+ <p>Object loading views also have a few rules and restrictions.
+ Firstly, the pointed-to object in the data member must be associated
+ with the view. Furthermore, if the associated object has an alias,
+ then the data member name must be the same as the alias (more
+ precisely, the public name derived from the data member must
+ match the alias; which means we can use normal data member
+ decorations such as trailing underscores, etc., see the previous
+ section for more information on public names). The following view
+ illustrates the use of aliases as data member names:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) \
+ object(country = nat: employee::nationality_)
+struct employee_country
+{
+ shared_ptr&lt;country> res;
+ shared_ptr&lt;country> nat_;
+};
+ </pre>
+
+ <p>Finally, the object pointers must be direct data members of
+ the view. Using, for example, a composite value that contains
+ pointers as a view data member is not supported. Note also
+ that depending on the join type you are using, some of the
+ resulting pointers might be <code>NULL</code>.</p>
+
+ <p>Up until now we have consistently used <code>shared_ptr</code>
+ as an object pointer in our views. Can we use other pointers,
+ such as <code>unique_ptr</code> or raw pointers? To answer
+ this question we first need to discuss what happens with
+ object pointers that may be inside objects that a view
+ loads. As a concrete example, let us revisit the
+ <code>employee_employer</code> view from the beginning of
+ this section:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ shared_ptr&lt;employee> ee;
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>This view loads two objects: <code>employee</code> and
+ <code>employer</code>. The <code>employee</code> object,
+ however, also contains a pointer to <code>employer</code>
+ (see the <code>employed_by_</code> data member). In fact,
+ this is the same object that the view loads since <code>employer</code>
+ is associated with the view using this same relationship (ODB
+ automatically uses it since it is the only one). The correct
+ result of loading such a view is then clear: both <code>er</code> and
+ <code>er->employed_by_</code> must point to (or share) the
+ same instance.</p>
+
+ <p>Just like object loading via the <code>database</code> class
+ functions, views achieve this correct behavior of only loading
+ a single instance of the same object with the help of session's
+ object cache (<a href="#11">Chapter 11, "Session"</a>). In fact,
+ object loading views enforce this by throwing the
+ <code>session_required</code> exception if there is no current
+ session and the view loads an object that is also indirectly
+ loaded by one of the other objects. The ODB compiler will also
+ issue diagnostics if such an object has session support
+ disabled (<a href="#14.1.10">Section 14.1.10,
+ "<code>session</code>"</a>).</p>
+
+ <p>With this understanding we can now provide the correct implementation
+ of our transaction that uses the <code>employee_employer</code> view:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+odb::session s;
+
+for (const employee_employer&amp; r:
+ db.query&lt;employee_employer> (query::employee::age &lt; 31))
+{
+ assert (r.ee->employed_by_ == r.er);
+ cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>It might seem logical, then, to always load all the objects from
+ all the eager relationships with the view. After all, this will
+ lead to them all being loaded with a single statement. While
+ this is theoretically true, the reality is slightly more nuanced.
+ If there is a high probability of the object already have been
+ loaded and sitting in the cache, then not loading the object
+ as part of the view (and therefore not fetching all its data
+ from the database) might result in better performance.</p>
+
+ <p>Now we can also answer the question about which pointers we can
+ use in object loading views. From the above discussion it should
+ be clear that if an object that we are loading is also part of a
+ relationship inside another object that we are loading, then we
+ should use some form of a shared ownership pointer. If, however,
+ there are no relationships involved, as is the case, for example,
+ in our <code>employer_named_country</code> and
+ <code>employee_country</code> views above, then we can use a
+ unique ownership pointer such as <code>unique_ptr</code>.</p>
+
+ <p>Note also that your choice of a pointer type can be limited by the
+ "official" object pointer type assigned to the object
+ (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>).
+ For example, if the object pointer type is <code>shared_ptr</code>,
+ you will not be able to use <code>unique_ptr</code> to load
+ such an object into a view since initializing <code>unique_ptr</code>
+ from <code>shared_ptr</code> would be a mistake.</p>
+
+ <p>Unless you want to perform your own object cleanup, raw object
+ pointers in views are not particularly useful. They do have one
+ special semantics, however: If a raw pointer is used as a view
+ member, then, before creating a new instance, the implementation
+ will check if the member is <code>NULL</code>. If it is not, then
+ it is assumed to point to an existing instance and the implementation
+ will load the data into it instead of creating a new one. The
+ primary use of this special functionality is to implement by-value
+ loading with the ability to detect <code>NULL</code> values.</p>
+
+ <p>To illustrate this functionality, consider the following view that
+ load the employee's residence country by value:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+ typedef country* country_ptr;
+
+ #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) \
+ set(this.res_null = ((?) == nullptr))
+
+ country res;
+ bool res_null;
+};
+ </pre>
+
+ <p>Here we are using a virtual data member
+ (<a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>) to
+ add an object pointer member to the view. Its accessor expression
+ returns the pointer to the <code>res</code> member so that
+ the implementation can load the data into it. The modifier
+ expression checks the passed pointer to initialize the
+ <code>NULL</code> value indicator. Here, the two possible
+ values that can be passed to the modifier expression are
+ the address of the <code>res</code> member that we returned
+ earlier from the accessor and <code>NULL</code> (strictly
+ speaking, there is a third possibility: the address of an
+ object that was found in the session cache).</p>
+
+ <p>If we are not interested in the <code>NULL</code> indicator,
+ then the above view can simplified to this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+ typedef country* country_ptr;
+
+ #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) set()
+
+ country res;
+};
+ </pre>
+
+ <p>That is, we specify an empty modifier expression which leads to
+ the value being ignored.</p>
+
+ <p>As another example of by-value loading, consider a view that allows
+ us to load objects into existing instances that have been allocated
+ outside the view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) \
+ object(country = nat: employee::nationality_)
+struct employee_country
+{
+ employee_country (country&amp; r, country&amp; n): res (&amp;r), nat (&amp;n) {}
+
+ country* res;
+ country* nat;
+};
+ </pre>
+
+ <p>And here is how we can use this view:</p>
+
+ <pre class="cxx">
+typedef odb::result&lt;employee_country> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_country> (...);
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ country res, nat;
+ employee_country v (res, nat);
+ i.load (v);
+
+ if (v.res != nullptr)
+ ... // Result is in res.
+
+ if (v.nat != nullptr)
+ ... // Result is in nat.
+}
+
+t.commit ();
+ </pre>
+
+ <p>As a final example of the by-value loading, consider the following
+ view which implements a slightly more advanced logic: if the object
+ is already in the session cache, then it sets the pointer data member
+ in the view (<code>er_p</code>) to that. Otherwise, it loads the data
+ into the by-value instance (<code>er</code>). We can also check
+ whether the pointer data member points to the instance to distinguish
+ between the two outcomes. And we can check it for <code>nullptr</code>
+ to detect <code>NULL</code> values.</p>
+
+ <pre class="cxx">
+#pragma db view object(employer)
+struct employer_view
+{
+ // Since we may be getting the pointer as both smart and raw, we
+ // need to create a bit of support code to use in the modifier
+ // expression.
+ //
+ void set_er (employer* p) {er_p = p;} // &amp;er or NULL.
+ void set_er (shared_ptr&lt;employer> p) {er_p = p.get ();} // From cache.
+
+ #pragma db get(&amp;this.er) set(set_er(?))
+ employer* er_p;
+
+ #pragma db transient
+ employer er;
+
+ // Return-by-value support (e.g., query_value()).
+ //
+ employer_view (): er_p (0) {}
+ employer_view (const employer_view&amp; x)
+ : er_p (x.er_p == &amp;x.er ? &amp;er : x.er_p), er (x.er) {}
+};
+ </pre>
+
+ <p>We can use object loading views with polymorphic objects
+ (<a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>). Note,
+ however, that when loading a derived object via the base pointer
+ in a view, a separate statement will be executed to load the
+ dynamic part of the object. There is no support for by-value
+ loading for polymorphic objects.</p>
+
+ <p>We can also use object loading views with objects without id
+ (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).
+ Note, however, that for such objects, <code>NULL</code> values
+ are not automatically detected (since there is no primary key,
+ which is otherwise guaranteed to be not <code>NULL</code>, there
+ might not be a column on which to base this detection). The
+ workaround for this limitation is to load an otherwise not
+ <code>NULL</code> column next to the object which will serve
+ as an indicator. For example:</p>
+
+ <pre class="cxx">
+#pragma db object no_id
+class object
+{
+ ...
+
+ int n; // NOT NULL
+ std::string s;
+};
+
+#include &lt;odb/nullable.hxx>
+
+#pragma db view object(object)
+struct view
+{
+
+ odb::nullable&lt;int> n; // If 'n' is NULL, then, logically, so is 'o'.
+ unique_ptr&lt;object> o;
+};
+ </pre>
+
+ <h2><a name="10.3">10.3 Table Views</a></h2>
+
+ <p>A table view is similar to an object view except that it is
+ based on one or more database tables instead of persistent
+ objects. Table views are primarily useful when dealing with
+ ad-hoc tables that are not mapped to persistent classes.</p>
+
+ <p>To associate one or more tables with a view we use the
+ <code>db&nbsp;table</code> pragma (<a href="#14.2.2">Section 14.2.2,
+ "<code>table</code>"</a>). To associate the second and subsequent
+ tables we repeat the <code>db&nbsp;table</code> pragma for each
+ additional table. For example, the following view is based on the
+ <code>employee_extra</code> legacy table we have defined at the
+ beginning of the chapter.</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra")
+struct employee_vacation
+{
+ #pragma db column("employee_id") type("INTEGER")
+ unsigned long employee_id;
+
+ #pragma db column("vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>Besides the table name in the <code>db&nbsp;table</code> pragma
+ we also have to specify the column name for each view data
+ member. Note that unlike for object views, the ODB compiler
+ does not try to automatically come up with column names for
+ table views. Furthermore, we cannot use references to object
+ members either, since there are no associated objects in table
+ views. Instead, the actual column name or column expression
+ must be specified as a string literal. The column name can
+ also be qualified with a table name either in the
+ <code>"table.column"</code> form or, if either a table
+ or a column name contains a period, in the
+ <code>"table"."column"</code> form. The following example
+ illustrates the use of a column expression:</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra")
+struct employee_max_vacation
+{
+ #pragma db column("max(vacation_days)") type("INTEGER")
+ unsigned short max_vacation_days;
+};
+ </pre>
+
+ <p>Both the associated table names and the column names can be qualified
+ with a database schema, for example:</p>
+
+ <pre class="cxx">
+#pragma db view table("hr.employee_extra")
+struct employee_max_vacation
+{
+ #pragma db column("hr.employee_extra.vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>For more information on database schemas and the format of the
+ qualified names, refer to <a href="#14.1.8">Section 14.1.8,
+ "<code>schema</code>"</a>.</p>
+
+ <p>Note also that in the above examples we specified the SQL type
+ for each of the columns to make sure that the ODB compiler
+ has knowledge of the actual types as specified in the database
+ schema. This is required to obtain correct and optimal
+ generated code.</p>
+
+
+ <p>The complete syntax of the <code>db&nbsp;table</code> pragma
+ is similar to the <code>db&nbsp;object</code> pragma and is shown
+ below:</p>
+
+ <p><code><b>table("</b><i>name</i><b>"</b>
+ [<b>=</b> <b>"</b><i>alias</i><b>"</b>]
+ [<i>join-type</i>]
+ [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
+
+ <p>The <i>name</i> part is a database table name. The optional
+ <i>alias</i> part gives this table an alias. If provided, the
+ alias must be used instead of the table whenever a reference
+ to a table is used. Contexts where such a reference may
+ be needed include the join condition (discussed below),
+ column names, and query expressions. The optional <i>join-type</i>
+ part specifies the way this table is associated. It can
+ be <code>left</code>, <code>right</code>, <code>full</code>,
+ <code>inner</code>, and <code>cross</code> with <code>left</code>
+ being the default. Finally, the optional <i>join-condition</i>
+ part provides the criteria which should be used to associate this
+ table with any of the previously associated tables or, as we will see in
+ <a href="#10.4">Section 10.4, "Mixed Views"</a>, objects. Note that
+ while the first associated table can have an alias, it cannot have
+ a join type or condition.</p>
+
+ <p>Similar to object views, for each subsequent associated table the
+ ODB compiler needs a join condition. However, unlike for object views,
+ for table views the ODB compiler does not try to come up with one
+ automatically. Furthermore, we cannot use references to object
+ members corresponding to object relationships either, since there
+ are no associated objects in table views. Instead, for each
+ subsequent associated table, a join condition must be
+ specified as a custom query expression. While the syntax of the
+ query expression is the same as in the query facility used to query
+ the database for objects (<a href="#4">Chapter 4, "Querying the
+ Database"</a>), a join condition for a table is normally specified
+ as a single string literal containing a native SQL query expression.</p>
+
+ <p>As an example of a multi-table view, consider the
+ <code>employee_health</code> table that we define in addition
+ to <code>employee_extra</code>:</p>
+
+ <pre class="sql">
+CREATE TABLE employee_health(
+ employee_id INTEGER NOT NULL,
+ sick_leave_days INTEGER NOT NULL)
+ </pre>
+
+ <p>Given these two tables we can now define a view that returns both
+ the vacation and sick leave information for each employee:</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra" = "extra") \
+ table("employee_health" = "health": \
+ "extra.employee_id = health.employee_id")
+struct employee_leave
+{
+ #pragma db column("extra.employee_id") type("INTEGER")
+ unsigned long employee_id;
+
+ #pragma db column("vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+
+ #pragma db column("sick_leave_days") type("INTEGER")
+ unsigned short sick_leave_days;
+};
+ </pre>
+
+ <p>Querying the database for a table view is the same as for an
+ object view except that we can only use native query expressions.
+ For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_leave> query;
+typedef odb::result&lt;employee_leave> result;
+
+transaction t (db.begin ());
+
+unsigned short v_min = ...
+unsigned short l_min = ...
+
+result r (db.query&lt;employee_leave> (
+ "vacation_days > " + query::_val(v_min) + "AND" +
+ "sick_leave_days > " + query::_val(l_min)));
+
+t.commit ();
+ </pre>
+
+
+ <h2><a name="10.4">10.4 Mixed Views</a></h2>
+
+ <p>A mixed view has both associated objects and tables. As a first
+ example of a mixed view, let us improve <code>employee_vacation</code>
+ from the previous section to return the employee's first
+ and last names instead of the employee id. To achieve this we
+ have to associate both the <code>employee</code> object and
+ the <code>employee_extra</code> table with the view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ table("employee_extra" = "extra": "extra.employee_id = " + employee::id_)
+struct employee_vacation
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column("extra.vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>When querying the database for a mixed view, we can use query members
+ for the parts of the query expression that involves object members
+ but have to fall back to using the native syntax for the parts that
+ involve table columns. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_vacation> query;
+typedef odb::result&lt;employee_vacation> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_vacation> (
+ (query::last == "Doe") + "AND extra.vacation_days &lt;> 0"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->vacation_days &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>As another example, consider a more advanced view that associates
+ two objects via a legacy table. This view allows us to find the
+ previous employer name for each employee:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ table("employee_extra" = "extra": "extra.employee_id = " + employee::id_) \
+ object(employer: "extra.previous_employer_id = " + employer::id_)
+struct employee_prev_employer
+{
+ std::string first;
+ std::string last;
+
+ // If previous_employer_id is NULL, then the name will be NULL as well.
+ // We use the odb::nullable wrapper to handle this.
+ //
+ #pragma db column(employer::name_)
+ odb::nullable&lt;std::string> prev_employer_name;
+};
+ </pre>
+
+ <h2><a name="10.5">10.5 View Query Conditions</a></h2>
+
+ <p>Object, table, and mixed views can also specify an optional query
+ condition that should be used whenever the database is queried for
+ this view. To specify a query condition we use the
+ <code>db&nbsp;query</code> pragma (<a href="#14.2.3">Section 14.2.3,
+ "<code>query</code>"</a>).</p>
+
+ <p>As an example, consider a view that returns some information about
+ all the employees that are over a predefined retirement age.
+ One way to implement this would be to define a standard object
+ view as we have done in the previous sections and then use a
+ query like this:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee_retirement> (query::age > 50));
+ </pre>
+
+ <p>The problem with the above approach is that we have to keep
+ repeating the <code>query::age > 50</code> expression every
+ time we execute the query, even though this expression always
+ stays the same. View query conditions allow us to solve this
+ problem. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) query(employee::age > 50)
+struct employee_retirement
+{
+ std::string first;
+ std::string last;
+ unsigned short age;
+};
+ </pre>
+
+ <p>With this improvement we can rewrite our query like this:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee_retirement> ());
+ </pre>
+
+ <p>But what if we may also need to restrict the result set based on
+ some varying criteria, such as the employee's last name? Or, in other
+ 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 the special
+ <code>(?)</code> placeholder that indicates the position in the
+ constant query expression where the runtime expression should be
+ inserted. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) query(employee::age > 50 &amp;&amp; (?))
+struct employee_retirement
+{
+ std::string first;
+ std::string last;
+ unsigned short name;
+};
+ </pre>
+
+ <p>With this change we can now use additional query criteria in our
+ view:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee_retirement> (query::last == "Doe"));
+ </pre>
+
+ <p>The syntax of the expression in a query condition is the same as in
+ the query facility used to query the database for objects
+ (<a href="#4">Chapter 4, "Querying the Database"</a>) except for
+ two differences. Firstly, for query members, instead of
+ using <code>odb::query&lt;object>::member</code> names, we refer
+ directly to object members, using the object alias instead of the
+ object name if an alias was assigned. Secondly, query conditions
+ support the special <code>(?)</code> placeholder which can be used
+ both in the C++-integrated query expressions as was shown above
+ and in native SQL expressions specified as string literals. The
+ following view is an example of the latter case:</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra") \
+ query("vacation_days &lt;> 0 AND (?)")
+struct employee_vacation
+{
+ ...
+};
+ </pre>
+
+ <p>Another common use case for query conditions are views with the
+ <code>ORDER BY</code> or <code>GROUP BY</code> clause. Such
+ clauses are normally present in the same form in every query
+ involving such views. As an example, consider an aggregate
+ view which calculate the minimum and maximum ages of employees
+ for each employer:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer) \
+ query((?) + "GROUP BY" + employer::name_)
+struct employer_age
+{
+ #pragma db column(employer::name_)
+ std::string employer_name;
+
+ #pragma db column("min(" + employee::age_ + ")")
+ unsigned short min_age;
+
+ #pragma db column("max(" + employee::age_ + ")")
+ unsigned short max_age;
+};
+ </pre>
+
+ <p>The query condition can be optionally followed (or replaced,
+ if no constant query expression is needed) by one or more
+ <em>result modifiers</em>. Currently supported result modifiers
+ are <code>distinct</code> (which is translated to <code>SELECT
+ DISTINCT</code>) and <code>for_update</code> (which is translated
+ to <code>FOR UPDATE</code> or equivalent for database systems
+ that support it). As an example, consider a view that
+ allows us to get some information about employers ordered
+ by the object id and without any duplicates:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) object(employee) \
+ query((?) + "ORDER BY" + employer::name_, distinct)
+struct employer_info
+{
+ ...
+};
+ </pre>
+
+ <p>If we don't require ordering, then this view can be re-implemented
+ like this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) object(employee) query(distinct)
+struct employer_info
+{
+ ...
+};
+ </pre>
+
+ <h2><a name="10.6">10.6 Native Views</a></h2>
+
+ <p>The last kind of view supported by ODB is a native view. Native
+ views are a low-level mechanism for capturing results of native
+ SQL queries, stored procedure calls, etc. Native views don't have
+ associated tables or objects. Instead, we use the
+ <code>db&nbsp;query</code> pragma to specify the native SQL query,
+ which should normally include the select-list and, if applicable,
+ the from-list. For example, here is how we can re-implement the
+ <code>employee_vacation</code> table view from Section 10.3 above
+ as a native view:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT employee_id, vacation_days " \
+ "FROM employee_extra")
+struct employee_vacation
+{
+ #pragma db type("INTEGER")
+ unsigned long employee_id;
+
+ #pragma db type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>In native views the columns in the query select-list are
+ associated with the view data members in the order specified.
+ That is, the first column is stored in the first member, the
+ second column &mdash; in the second member, and so on. The ODB compiler
+ does not perform any error checking in this association. As a result
+ you must make sure that the number and order of columns in the
+ query select-list match the number and order of data members
+ in the view. This is also the reason why we are not
+ required to provide the column name for each data member in native
+ views, as is the case for object and table views.</p>
+
+ <p>Note also that while it is always possible to implement a table
+ view as a native view, the table views must be preferred since
+ they are safer. In a native view, if you add, remove, or
+ rearrange data members without updating the column list in the
+ query, or vice versa, at best, this will result in a runtime
+ error. In contrast, in a table view such changes will result
+ in the query being automatically updated.</p>
+
+ <p>Similar to object and table views, the query specified for
+ a native view can contain the special <code>(?)</code>
+ placeholder which is replaced with the query expression
+ specified at the query execution time.
+ If the native query does not contain a placeholder, as in
+ the example above, then any query expression specified at
+ the query execution time is appended to the query text
+ along with the <code>WHERE</code> keyword, if required.
+ The following example shows the usage of the placeholder:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT employee_id, vacation_days " \
+ "FROM employee_extra " \
+ "WHERE vacation_days &lt;> 0 AND (?)")
+struct employee_vacation
+{
+ ...
+};
+ </pre>
+
+ <p>As another example, consider a view that returns the next
+ value of a database sequence:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT nextval('my_seq')")
+struct sequence_value
+{
+ unsigned long long value;
+};
+ </pre>
+
+ <p>While this implementation can be acceptable in some cases, it has
+ a number of drawbacks. Firstly, the name of the sequence is
+ fixed in the view, which means if we have a second sequence, we
+ will have to define another, almost identical view. Similarly,
+ the operation that we perform on the sequence is also fixed.
+ In some situations, instead of returning the next value, we may
+ need the last value.</p>
+
+ <p>Note that we cannot use the placeholder mechanism to resolve
+ these problems since placeholders can only be used in the
+ <code>WHERE</code>, <code>GROUP BY</code>, and similar
+ clauses. In other words, the following won't work:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT nextval('(?)')")
+struct sequence_value
+{
+ unsigned long long value;
+};
+
+result r (db.query&lt;sequence_value> ("my_seq"));
+ </pre>
+
+ <p>To support these kinds of use cases, ODB allows us to specify the
+ complete query for a native view at runtime rather than at the view
+ definition. To indicate that a native view has a runtime query,
+ we can either specify the empty <code>db&nbsp;query</code>
+ pragma or omit the pragma altogether. For example:</p>
+
+ <pre class="cxx">
+#pragma db view
+struct sequence_value
+{
+ unsigned long long value;
+};
+ </pre>
+
+ <p>Given this view, we can perform the following queries:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;sequence_value> query;
+typedef odb::result&lt;sequence_value> result;
+
+string seq_name = ...
+
+result l (db.query&lt;sequence_value> (
+ "SELECT lastval('" + seq_name + "')"));
+
+result n (db.query&lt;sequence_value> (
+ "SELECT nextval('" + seq_name + "')"));
+ </pre>
+
+ <p>Native views can also be used to call and handle results of
+ stored procedures. The semantics and limitations of stored
+ procedures vary greatly between database systems while some
+ do not support this functionality at all. As a result, support
+ for calling stored procedures using native views is described
+ for each database system in <a href="#II">Part II, "Database
+ Systems"</a>.</p>
+
+ <h2><a name="10.7">10.7 Other View Features and Limitations</a></h2>
+
+ <p>Views cannot be derived from other views. However, you can derive
+ a view from a transient C++ class. View data members cannot be
+ object pointers. If you need to access data from a pointed-to
+ object, then you will need to associate such an object with
+ the view. Similarly, view data members cannot be containers.
+ These two limitations also apply to composite value types that
+ contain object pointers or containers. Such composite values
+ cannot be used as view data members.</p>
+
+ <p>On the other hand, composite values that do not contain object
+ pointers or containers can be used in views. As an example,
+ consider a modified version of the <code>employee</code> persistent
+ class that stores a person's name as a composite value:</p>
+
+ <pre class="cxx">
+#pragma db value
+class person_name
+{
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ person_name name_;
+
+ ...
+};
+ </pre>
+
+ <p>Given this change, we can re-implement the <code>employee_name</code>
+ view like this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_name
+{
+ person_name name;
+};
+ </pre>
+
+ <p>It is also possible to extract some or all of the nested members
+ of a composite value into individual view data members. Here is
+ how we could have defined the <code>employee_name</code> view
+ if we wanted to keep its original structure:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_name
+{
+ #pragma db column(employee::name.first_)
+ std::string first;
+
+ #pragma db column(employee::name.last_)
+ std::string last;
+};
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="11">11 Session</a></h1>
+
+ <p>A session is an application's unit of work that may encompass several
+ database transactions. In this version of ODB a session is just an
+ object cache. In future versions it may provide additional
+ functionality, such as delayed database operations and automatic
+ object state change tracking. As discussed later in
+ <a href="#11.2">Section 11.2, "Custom Sessions"</a>, it is also
+ possible to provide a custom session implementation that provides
+ these or other features.</p>
+
+ <p>Session support is optional and can be enabled or disabled on the
+ per object basis using the <code>db&nbsp;session</code> pragma, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object session
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>We can also enable or disable session support for a group of
+ objects at the namespace level:</p>
+
+ <pre class="cxx">
+#pragma db namespace session
+namespace accounting
+{
+ #pragma db object // Session support is enabled.
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object session(false) // Session support is disabled.
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Finally, we can pass the <code>--generate-session</code> ODB compiler
+ option to enable session support by default. With this option session
+ support will be enabled for all the persistent classes except those
+ for which it was explicitly disabled using the
+ <code>db&nbsp;session</code>. An alternative to this method with the
+ same effect is to enable session support for the global namespace:</p>
+
+ <pre class="cxx">
+#pragma db namespace() session
+ </pre>
+
+ <p>Each thread of execution in an application can have only one active
+ session at a time. A session is started by creating an instance of
+ the <code>odb::session</code> class and is automatically terminated
+ when this instance is destroyed. You will need to include the
+ <code>&lt;odb/session.hxx></code> header file to make this class
+ available in your application. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+#include &lt;odb/session.hxx>
+#include &lt;odb/transaction.hxx>
+
+using namespace odb::core;
+
+{
+ session s;
+
+ // First transaction.
+ //
+ {
+ transaction t (db.begin ());
+ ...
+ t.commit ();
+ }
+
+ // Second transaction.
+ //
+ {
+ transaction t (db.begin ());
+ ...
+ t.commit ();
+ }
+
+ // Session 's' is terminated here.
+}
+ </pre>
+
+ <p>The <code>session</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class session
+ {
+ public:
+ session (bool make_current = true);
+ ~session ();
+
+ // Copying or assignment of sessions is not supported.
+ //
+ private:
+ session (const session&amp;);
+ session&amp; operator= (const session&amp;);
+
+ // Current session interface.
+ //
+ public:
+ static session&amp;
+ current ();
+
+ static bool
+ has_current ();
+
+ static void
+ current (session&amp;);
+
+ static void
+ reset_current ();
+
+ static session*
+ current_pointer ();
+
+ static void
+ current_pointer (session*);
+
+ // Object cache interface.
+ //
+ public:
+ template &lt;typename T>
+ struct cache_position {...};
+
+ template &lt;typename T>
+ cache_position&lt;T>
+ cache_insert (database&amp;,
+ const object_traits&lt;T>::id_type&amp;,
+ const object_traits&lt;T>::pointer_type&amp;);
+
+ template &lt;typename T>
+ object_traits&lt;T>::pointer_type
+ cache_find (database&amp;, const object_traits&lt;T>::id_type&amp;) const;
+
+ template &lt;typename T>
+ void
+ cache_erase (const cache_position&lt;T>&amp;);
+
+ template &lt;typename T>
+ void
+ cache_erase (database&amp;, const object_traits&lt;T>::id_type&amp;);
+ };
+}
+ </pre>
+
+ <p>The session constructor creates a new session and, if the
+ <code>make_current</code> argument is <code>true</code>, sets it as a
+ current session for this thread. If we try to make a session current
+ while there is already another session in effect for this thread,
+ then the constructor throws the <code>odb::already_in_session</code>
+ exception. The destructor clears the current session for this
+ thread if this session is the current one.</p>
+
+ <p>The static <code>current()</code> accessor returns the currently active
+ session for this thread. If there is no active session, this function
+ throws the <code>odb::not_in_session</code> exception. We can check
+ whether there is a session in effect in this thread using the
+ <code>has_current()</code> static function.</p>
+
+ <p>The static <code>current()</code> modifier allows us to set the
+ current session for this thread. The <code>reset_current()</code>
+ static function clears the current session. These two functions
+ allow for more advanced use cases, such as multiplexing
+ two or more sessions on the same thread.</p>
+
+ <p>The static <code>current_pointer()</code> overloaded functions
+ provided the same functionality but using pointers. Specifically,
+ the <code>current_pointer()</code> accessor can be used to
+ test whether there is a current session and get a pointer to it
+ all with a single call.</p>
+
+ <p>We normally don't use the object cache interface directly. However,
+ it could be useful in some cases, for example, to find out whether
+ an object has already been loaded. Note that when calling
+ <code>cache_insert()</code>, <code>cache_find()</code>, or
+ the second version of <code>cache_erase()</code>, you need to
+ specify the template argument (object type) explicitly. It is
+ also possible to access the underlying cache data structures
+ directly. This can be useful if, for example, you want to
+ iterate over the objects store in the cache. Refer to the ODB
+ runtime header files for more details on this direct access.</p>
+
+ <h2><a name="11.1">11.1 Object Cache</a></h2>
+
+ <p>A session is an object cache. Every time a session-enabled object is
+ made persistent by calling the <code>database::persist()</code> function
+ (<a href="#3.8">Section 3.8, "Making Objects Persistent"</a>), loaded
+ by calling the <code>database::load()</code> or
+ <code>database::find()</code> function (the pointer-returning overloads
+ only; <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
+ or loaded by iterating over a query result (<a href="#4.4">Section 4.4,
+ "Query Result"</a>), the pointer to the persistent object, in the form
+ of the canonical object pointer (<a href="#3.3">Section 3.3, "Object
+ and View Pointers"</a>), is stored in the session. For as long as the
+ session is in effect, any subsequent calls to load the same object will
+ return the cached instance. When an object's state is deleted from the
+ database with the <code>database::erase()</code> function
+ (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>), the
+ cached object pointer is removed from the session. For example:</p>
+
+ <pre class="cxx">
+shared_ptr&lt;person> p (new person ("John", "Doe"));
+
+session s;
+transaction t (db.begin ());
+
+unsigned long id (db.persist (p)); // p is cached in s.
+shared_ptr&lt;person> p1 (db.load&lt;person> (id)); // p1 same as p.
+
+t.commit ();
+ </pre>
+
+
+ <p>The per-object caching policies depend on the object pointer kind
+ (<a href="#6.5">Section 6.5, "Using Custom Smart Pointers"</a>).
+ Objects with a unique pointer, such as <code>std::auto_ptr</code>
+ or <code>std::unique_ptr</code>, as an object pointer are never
+ cached since it is not possible to have two such pointers pointing
+ to the same object. When an object is persisted via a pointer or
+ loaded as a dynamically allocated instance, objects with both raw
+ and shared pointers as object pointers are cached. If an object is
+ persisted as a reference or loaded into a pre-allocated instance,
+ the object is only cached if its object pointer is a raw pointer.</p>
+
+ <p>Also note that when we persist an object as a constant reference
+ or constant pointer, the session caches such an object as
+ unrestricted (non-<code>const</code>). This can lead to undefined
+ behavior if the object being persisted was actually created as
+ <code>const</code> and is later found in the session cache and
+ used as non-<code>const</code>. As a result, when using sessions,
+ it is recommended that all persistent objects be created as
+ non-<code>const</code> instances. The following code fragment
+ illustrates this point:</p>
+
+ <pre class="cxx">
+void save (database&amp; db, shared_ptr&lt;const person> p)
+{
+ transaction t (db.begin ());
+ db.persist (p); // Persisted as const pointer.
+ t.commit ();
+}
+
+session s;
+
+shared_ptr&lt;const person> p1 (new const person ("John", "Doe"));
+unsigned long id1 (save (db, p1)); // p1 is cached in s as non-const.
+
+{
+ transaction t (db.begin ());
+ shared_ptr&lt;person> p (db.load&lt;person> (id1)); // p == p1
+ p->age (30); // Undefined behavior since p1 was created const.
+ t.commit ();
+}
+
+shared_ptr&lt;const person> p2 (new person ("Jane", "Doe"));
+unsigned long id2 (save (db, p2)); // p2 is cached in s as non-const.
+
+{
+ transaction t (db.begin ());
+ shared_ptr&lt;person> p (db.load&lt;person> (id2)); // p == p2
+ p->age (30); // Ok, since p2 was not created const.
+ t.commit ();
+}
+ </pre>
+
+ <h2><a name="11.2">11.2 Custom Sessions</a></h2>
+
+ <p>ODB can use a custom session implementation instead of the
+ default <code>odb::session</code>. There could be multiple
+ reasons for an application to provide its own session. For
+ example, the application may already include a notion of an
+ object cache or registry which ODB can re-use. A custom
+ session can also provide additional functionality, such as
+ automatic change tracking, delayed database operations, or
+ object eviction. Finally, the session-per-thread approach used
+ by <code>odb::session</code> may not be suitable for all
+ applications. For instance, some may need a thread-safe
+ session that can be shared among multiple threads. For
+ an example of a custom session that implements automatic
+ change tracking by keeping original copies of the objects,
+ refer to the <code>common/session/custom</code> test
+ in the <code>odb-tests</code> package.</p>
+
+ <p>To use a custom session we need to specify its type with
+ the <code>--session-type</code> ODB compiler command line
+ option. We also need to include its definition into the
+ generated header file. This can be achieved with the
+ <code>--hxx-prologue</code> option. For example, if our
+ custom session is called <code>app::session</code> and
+ is defined in the <code>app/session.hxx</code> header
+ file, then the corresponding ODB compiler options would
+ look like this:</p>
+
+ <pre class="terminal">
+odb --hxx-prologue "#include \"app/session.hxx\"" \
+--session-type ::app::session ...
+ </pre>
+
+ <p>A custom session should provide the following interface:</p>
+
+ <pre class="cxx">
+class custom_session
+{
+public:
+ static bool
+ _has_cache ();
+
+ // Cache management functions.
+ //
+ template &lt;typename T>
+ struct cache_position
+ {
+ ...
+ };
+
+ template &lt;typename T>
+ static cache_position&lt;T>
+ _cache_insert (odb::database&amp;,
+ const typename odb::object_traits&lt;T>::id_type&amp;,
+ const typename odb::object_traits&lt;T>::pointer_type&amp;);
+
+ template &lt;typename T>
+ static typename odb::object_traits&lt;T>::pointer_type
+ _cache_find (odb::database&amp;,
+ const typename odb::object_traits&lt;T>::id_type&amp;);
+
+ template &lt;typename T>
+ static void
+ _cache_erase (const cache_position&lt;T>&amp;);
+
+ // Notification functions.
+ //
+ template &lt;typename T>
+ static void
+ _cache_persist (const cache_position&lt;T>&amp;);
+
+ template &lt;typename T>
+ static void
+ _cache_load (const cache_position&lt;T>&amp;);
+
+ template &lt;typename T>
+ static void
+ _cache_update (odb::database&amp;, const T&amp; obj);
+
+ template &lt;typename T>
+ static void
+ _cache_erase (odb::database&amp;,
+ const typename odb::object_traits&lt;T>::id_type&amp;);
+};
+ </pre>
+
+ <p>The <code>_has_cache()</code> function shall return <code>true</code>
+ if the object cache is in effect in the current thread.</p>
+
+ <p>The <code>cache_position</code> class template represents a position
+ in the cache of the inserted object. It should be default and
+ copy-constructible as well as copy-assignable. The default
+ constructor shall create a special empty/<code>NULL</code>
+ position. A call of any of the cache management or notification
+ functions with such an empty/<code>NULL</code> position shall be
+ ignored.</p>
+
+ <p>The <code>_cache_insert()</code> function shall add the object into
+ the object cache and return its position. The <code>_cache_find()</code>
+ function looks an object up in the object cache given its id.
+ It returns a <code>NULL</code> pointer if the object is not
+ found. The <code>_cache_erase()</code> cache management function
+ shall remove the object from the cache. It is called
+ if the database operation that caused the object to be inserted
+ (for example, load) failed. Note also that after insertion the object
+ state is undefined. You can only access the object state
+ (for example, make a copy or clear a flag) from one of the
+ notification functions discussed below.</p>
+
+ <p>The notification functions are called after an object has
+ been persisted, loaded, updated, or erased, respectively. If
+ your session implementation does not need some of the
+ notifications, you still have to provide their functions,
+ however, you can leave their implementations empty.</p>
+
+ <p>Notice also that all the cache management and notification
+ functions are static. This is done in order to allow for a
+ custom notion of a current session. Normally, the first
+ step a non-empty implementation will perform is lookup the
+ current session.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="12">12 Optimistic Concurrency</a></h1>
+
+ <p>The ODB transaction model (<a href="#3.5">Section 3.5,
+ "Transactions"</a>) guarantees consistency as long as we perform all the
+ database operations corresponding to a specific application transaction
+ in a single database transaction. That is, if we load an object within a
+ database transaction and update it in the same transaction, then we are
+ guaranteed that the object state that we are updating in the database is
+ exactly the same as the state we have loaded. In other words, it is
+ impossible for another process or thread to modify the object state
+ in the database between these load and update operations.</p>
+
+ <p>In this chapter we use the term <em>application transaction</em>
+ to refer to a set of operations on persistent objects that an
+ application needs to perform in order to implement some
+ application-specific functionality. The term <em>database
+ transaction</em> refers to the set of database operations
+ performed between the ODB <code>begin()</code> and <code>commit()</code>
+ calls. Up until now we have treated application transactions and
+ database transactions as essentially the same thing.</p>
+
+ <p>While this model is easy to understand and straightforward to use,
+ it may not be suitable for applications that have long application
+ transactions. The canonical example of such a situation is an
+ application transaction that requires user input between loading
+ an object and updating it. Such an operation may take an arbitrary
+ long time to complete and performing it within a single database
+ transaction will consume database resources as well as prevent
+ other processes/threads from updating the object for too long.</p>
+
+ <p>The solution to this problem is to break up the long-lived
+ application transaction into several short-lived database
+ transactions. In our example that would mean loading the object
+ in one database transaction, waiting for user input, and then
+ updating the object in another database transaction. For example:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
+unsigned short age;
+cin >> age;
+p.age (age);
+
+{
+ transaction t (db.begin ());
+ db.update (p);
+ t.commit ();
+}
+ </pre>
+
+ <p>This approach works well if we only have one process/thread that can ever
+ update the object. However, if we have multiple processes/threads
+ modifying the same object, then this approach does not guarantee
+ consistency anymore. Consider what happens in the above example if
+ another process updates the person's last name while we are waiting for
+ the user input. Since we loaded the object before this change occured,
+ our version of the person's data will still have the old name. Once we
+ receive the input from the user, we go ahead and update the object,
+ overwriting both the old age with the new one (correct) and the new name
+ with the old one (incorrect).</p>
+
+ <p>While there is no way to restore the consistency guarantee in
+ an application transaction that consists of multiple database
+ transactions, ODB provides a mechanism, called optimistic
+ concurrency, that allows applications to detect and potentially
+ recover from such inconsistencies.</p>
+
+ <p>In essence, the optimistic concurrency model detects mismatches
+ between the current object state in the database and the state
+ when it was loaded into the application memory. Such a mismatch
+ would mean that the object was changed by another process or
+ thread. There are several ways to implement such state mismatch
+ detection. Currently, ODB uses object versioning while other
+ methods, such as timestamps, may be supported in the future.</p>
+
+ <p>To declare a persistent class with the optimistic concurrency model we
+ use the <code>optimistic</code> pragma (<a href="#14.1.5">Section 14.1.5,
+ "<code>optimistic</code>"</a>). We also use the <code>version</code>
+ pragma (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>)
+ to specify which data member will store the object version. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long version_;
+};
+ </pre>
+
+ <p>The version data member is managed by ODB. It is initialized to
+ <code>1</code> when the object is made persistent and incremented
+ by <code>1</code> with each update. The <code>0</code> version value
+ is not used by ODB and the application can use it as a special value,
+ for example, to indicate that the object is transient. Note that
+ for optimistic concurrency to function properly, the application
+ should not modify the version member after making the object persistent
+ or loading it from the database and until deleting the state of this
+ object from the database. To avoid any accidental modifications
+ to the version member, we can declare it <code>const</code>, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ const unsigned long version_;
+};
+ </pre>
+
+ <p>When we call the <code>database::update()</code> function
+ (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>) and pass
+ an object that has an outdated state, the <code>odb::object_changed</code>
+ exception is thrown. At this point the application has two
+ recovery options: it can abort and potentially restart the
+ application transaction or it can reload the new object
+ state from the database, re-apply or merge the changes, and call
+ <code>update()</code> again. Note that aborting an application
+ transaction that performs updates in multiple database transactions
+ may require reverting changes that have already been committed to
+ the database. As a result, this strategy works best if all the
+ updates are performed in the last database transaction of the
+ application transaction. This way the changes can be reverted
+ by simply rolling back this last database transaction.</p>
+
+ <p>The following example shows how we can reimplement the above
+ transaction using the second recovery option:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
+unsigned short age;
+cin >> age;
+p.age (age);
+
+{
+ transaction t (db.begin ());
+
+ try
+ {
+ db.update (p);
+ }
+ catch (const object_changed&amp;)
+ {
+ db.reload (p);
+ p.age (age);
+ db.update (p);
+ }
+
+ t.commit ();
+}
+ </pre>
+
+ <p>An important point to note in the above code fragment is that the second
+ <code>update()</code> call cannot throw the <code>object_changed</code>
+ exception because we are reloading the state of the object
+ and updating it within the same database transaction.</p>
+
+ <p>Depending on the recovery strategy employed by the application,
+ an application transaction with a failed update can be significantly
+ more expensive than a successful one. As a result, optimistic
+ concurrency works best for situations with low to medium contention
+ levels where the majority of the application transactions complete
+ without update conflicts. This is also the reason why this concurrency
+ model is called optimistic.</p>
+
+ <p>In addition to updates, ODB also performs state mismatch detection
+ when we are deleting an object from the database
+ (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>).
+ To understand why this can be important, consider the following
+ application transaction:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+string answer;
+cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
+getline (cin, answer);
+
+if (answer == "yes")
+{
+ transaction t (db.begin ());
+ db.erase (p);
+ t.commit ();
+}
+ </pre>
+
+ <p>Consider again what happens if another process or thread updates
+ the object by changing the person's age while we are waiting for
+ the user input. In this case, the user makes the decision based on
+ a certain age while we may delete (or not delete) an object that has
+ a completely different age. Here is how we can fix this problem
+ using optimistic concurrency:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+string answer;
+for (bool done (false); !done; )
+{
+ if (answer.empty ())
+ cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
+ else
+ cerr &lt;&lt; "age changed to " &lt;&lt; p.age () &lt;&lt; ", still delete?" &lt;&lt; endl;
+
+ getline (cin, answer);
+
+ if (answer == "yes")
+ {
+ transaction t (db.begin ());
+
+ try
+ {
+ db.erase (p);
+ done = true;
+ }
+ catch (const object_changed&amp;)
+ {
+ db.reload (p);
+ }
+
+ t.commit ();
+ }
+ else
+ done = true;
+}
+ </pre>
+
+ <p>Note that state mismatch detection is performed only if we delete
+ an object by passing the object instance to the <code>erase()</code>
+ function. If we want to delete an object with the optimistic concurrency
+ model regardless of its state, then we need to use the <code>erase()</code>
+ function that deletes an object given its id, for example:</p>
+
+ <pre class="cxx">
+{
+ transaction t (db.begin ());
+ db.erase (p.id ());
+ t.commit ();
+}
+ </pre>
+
+ <p>Finally, note that for persistent classes with the optimistic concurrency
+ model both the <code>update()</code> function as well as the
+ <code>erase()</code> function that accepts an object instance as its
+ argument no longer throw the <code>object_not_persistent</code>
+ exception if there is no such object in the database. Instead,
+ this condition is treated as a change of object state and the
+ <code>object_changed</code> exception is thrown instead.</p>
+
+ <p>For complete sample code that shows how to use optimistic
+ concurrency, refer to the <code>optimistic</code> example in
+ the <code>odb-examples</code> package.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="13">13 Database Schema Evolution</a></h1>
+
+ <p>When we add new persistent classes or change the existing ones, for
+ example, by adding or deleting data members, the database schema
+ necessary to store the new object model changes as well. At the
+ same time, we may have existing databases that contain existing data.
+ If new versions of your application don't need to handle
+ old databases, then the schema creating functionality is all that
+ you need. However, most applications will need to work with data
+ stored by older versions of the same application.</p>
+
+ <p>We will call <em>database schema evolution</em> the overall task
+ of updating the database to match the changes in the object model.
+ Schema evolution usually consists of two sub-tasks: <em>schema
+ migration</em> and <em>data migration</em>. Schema migration
+ modifies the database schema to correspond to the current
+ object model. In a relational database, this, for example, could
+ require adding or dropping tables and columns. The data migration
+ task involves converting the data stored in the existing database
+ from the old format to the new one.</p>
+
+ <p>If performed manually, database schema evolution is a tedious and
+ error-prone task. As a result, ODB provides comprehensive support
+ for automated or, more precisely, semi-automated schema
+ evolution. Specifically, ODB does fully-automatic schema
+ migration and provides facilities to help you with data
+ migration.</p>
+
+ <p>The topic of schema evolution is a complex and sensitive
+ issue since normally there would be valuable, production data at
+ stake. As a result, the approach taken by ODB is to provide simple
+ and bullet-proof elementary building blocks (or migration steps)
+ that we can understand and trust. Using these elementary blocks we
+ can then implement more complex migration scenarios. In particular,
+ ODB does not try to handle data migration automatically since in most
+ cases this requires understanding of application-specific semantics.
+ In other words, there is no magic.</p>
+
+ <p>There are two general approaches to working with older data: the
+ application can either convert it to correspond to the new format
+ or it can be made capable of working with multiple versions of this
+ format. There is also a hybrid approach where the application
+ may convert the data to the new format gradually as part of its
+ normal functionality. ODB is capable of handling all these
+ scenarios. That is, there is support for working with older
+ models without performing any migration (schema or data).
+ Alternatively, we can migrate the schema after
+ which we have the choice of either also immediately migrating the
+ data (<em>immediate data migration</em>) or doing it gradually
+ (<em>gradual data migration</em>).</p>
+
+ <p>Schema evolution is already a complex task and we should not
+ unnecessarily use a more complex approach where a simpler one
+ would be sufficient. From the above, the simplest approach is
+ the immediate schema migration that does not require any data
+ migration. An example of such a change would be adding a new
+ data member with the default value (<a href="#14.3.4">Section
+ 14.3.4, "<code>default</code>"</a>). This case ODB can handle
+ completely automatically.</p>
+
+ <p>If we do require data migration, then the next simplest approach
+ is the immediate schema and data migration. Here we have to write
+ custom migration code. However, it is separate from the rest of
+ the core application logic and is executed at a well defined point
+ (database migration). In other words, the core application logic
+ need not be aware of older model versions. The potential drawback
+ of this approach is performance. It may take a lot of resources
+ and/or time to convert all the data upfront.</p>
+
+ <p>If the immediate migration is not possible, then the next option
+ is the immediate schema migration followed by the gradual data
+ migration. With this approach, both old and new data must co-exist
+ in the new database. We also have to change the application
+ logic to both account for different sources of the same data (for
+ example, when either an old or new version of the object is loaded)
+ as well as migrate the data when appropriate (for example, when
+ the old version of the object is updated). At some point, usually
+ when the majority of the data has been converted, gradual migrations
+ are terminated with an immediate migration.</p>
+
+ <p>The most complex approach is working with multiple versions of
+ the database without performing any migrations, schema or data.
+ ODB does provide support for implementing this approach
+ (<a href="#13.4">Section 13.4, "Soft Object Model Changes"</a>),
+ however we will not cover it any further in this chapter.
+ Generally, this will require embedding knowledge about each
+ version into the core application logic which makes it hard
+ to maintain for any non-trivial object model.</p>
+
+ <p>Note also that when it comes to data migration, we can use
+ the immediate variant for some changes and gradual for others.
+ We will discuss various migration scenarios in greater detail
+ in section <a href="#13.3">Section 13.3, "Data Migration"</a>.</p>
+
+ <h2><a name="13.1">13.1 Object Model Version and Changelog</a></h2>
+
+ <p>To enable schema evolution support in ODB we need to specify
+ the object model version, or, more precisely, two versions.
+ The first is the base model version. It is the lowest
+ version from which we will be able to migrate. The second
+ version is the current model version. In ODB we can migrate
+ from multiple previous versions by successively migrating
+ from one to the next until we reach the current version.
+ We use the <code>db&nbsp;model&nbsp;version</code> pragma
+ to specify both the base and current versions.</p>
+
+ <p>When we enable schema evolution for the first time, our
+ base and current versions will be the same, for example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 1)
+ </pre>
+
+ <p>Once we release our application, its users may create databases
+ with the schema corresponding to this version of the object
+ model. This means that if we make any modifications to our
+ object model that also change the schema, then we will need
+ to be able to migrate the old databases to this new schema.
+ As a result, before making any new changes after a release,
+ we increment the current version, for example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+ </pre>
+
+ <p>To put this another way, we can stay on the same version
+ during development and keep adding new changes to it. But
+ once we release it, any new changes to the object model will
+ have to be done in a new version.</p>
+
+ <p>It is easy to forget to increment the version before
+ making new changes to the object model. To help solve this
+ problem, the <code>db&nbsp;model&nbsp;version</code> pragma
+ accepts a third optional argument that specify whether the
+ current version is open or closed for changes. For example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2, open) // Can add new changes to
+ // version 2.
+ </pre>
+
+ <pre class="cxx">
+#pragma db model version(1, 2, closed) // Can no longer add new
+ // changes to version 2.
+ </pre>
+
+ <p>If the current version is closed, ODB will refuse to accept
+ any new schema changes. In this situation you would
+ normally increment the current version and mark it as open
+ or you could re-open the existing version if, for example,
+ you need to fix something. Note, however, that re-opening
+ versions that have been released will most likely result
+ in migration malfunctions. By default the version is open.</p>
+
+ <p>Normally, an application will have a range of older database
+ versions from which it is able to migrate. When we change
+ this range by removing support for older versions, we also
+ need to adjust the base model version. This will make sure
+ that ODB does not keep unnecessary information around.</p>
+
+ <p>A model version (both base and current) is a 64-bit unsigned
+ integer (<code>unsigned&nbsp;long&nbsp;long</code>). <code>0</code>
+ is reserved to signify special situations, such as the lack of
+ schema in the database. Other than that, we can use any values
+ as versions as long as they are monotonically increasing. In
+ particular, we don't have to start with version <code>1</code>
+ and can increase the versions by any increment.</p>
+
+ <p>One versioning approach is to use an independent
+ object model version by starting from version <code>1</code>
+ and also incrementing by <code>1</code>. The alternative
+ is to make the model version correspond to the application
+ version. For example, if our application is using the
+ <code>X.Y.Z</code> version format, then we could encode it
+ as a hexadecimal number and use that as our model version,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db model version(0x020000, 0x020306) // 2.0.0-2.3.6
+ </pre>
+
+ <p>Most real-world object models will be spread over multiple
+ header files and it will be burdensome to repeat the
+ <code>db&nbsp;model&nbsp;version</code> pragma in each of
+ them. The recommended way to handle this situation is to
+ place the <code>version</code> pragma into a separate header
+ file and include it into the object model files. If your
+ project already has a header file that defines the
+ application version, then it is natural to place this
+ pragma there. For example:</p>
+
+ <pre class="cxx">
+// version.hxx
+//
+// Define the application version.
+//
+
+#define MYAPP_VERSION 0x020306 // 2.3.6
+
+#ifdef ODB_COMPILER
+#pragma db model version(1, 7)
+#endif
+ </pre>
+
+ <p>Note that we can also use macros in the <code>version</code>
+ pragma which allows us to specify all the versions in a single
+ place. For example:</p>
+
+ <pre class="cxx">
+#define MYAPP_VERSION 0x020306 // 2.3.6
+#define MYAPP_BASE_VERSION 0x020000 // 2.0.0
+
+#ifdef ODB_COMPILER
+#pragma db model version(MYAPP_BASE_VERSION, MYAPP_VERSION)
+#endif
+ </pre>
+
+ <p>It is also possible to have multiple object models within the
+ same application that have different versions. Such models
+ must be independent, that is, no headers from one model shall
+ include a header from another. You will also need to assign
+ different schema names to each model with the
+ <code>--schema-name</code> ODB compiler option.</p>
+
+ <p>Once we specify the object model version, the ODB compiler
+ starts tracking database schema changes in a changelog file.
+ Changelog has an XML-based, line-oriented format. It uses
+ XML in order to provide human readability while also
+ facilitating, if desired, processing and analysis with
+ custom tools. The line orientation makes it easy to review
+ with tools like <code>diff</code>.</p>
+
+ <p>The changelog is maintained by the ODB compiler. Specifically,
+ you do not need to make any manual changes to this file. You
+ will, however, need to keep it around from one invocation of
+ the ODB compiler to the next. In other words, the changelog
+ file is both the input and the output of the ODB compiler. This,
+ for example, means that if your project's source code is stored
+ in a version control repository, then you will most likely want
+ to store the changelog there as well. If you delete the changelog,
+ then any ability to do schema migration will be lost.</p>
+
+ <p>The only operation that you may want to perform with the
+ changelog is to review the database schema changes that resulted
+ from the C++ object model changes. For this you can use a tool
+ like <code>diff</code> or, better yet, the change review facilities
+ offered by your revision control system. For this purpose the
+ contents of a changelog will be self-explanatory.</p>
+
+ <p>As an example, consider the following initial object model:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#include &lt;string>
+
+#pragma db model version(1, 1)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+};
+ </pre>
+
+ <p>We then compile this header file with the ODB compiler (using the
+ PostgreSQL database as an example):</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema person.hxx
+ </pre>
+
+ <p>If we now look at the list of generated files, then in addition to
+ the now familiar <code>person-odb.?xx</code> and <code>person.sql</code>,
+ we will also see <code>person.xml</code> &mdash; the changelog file.
+ Just for illustration, below are the contents of this changelog.</p>
+
+ <pre class="xml">
+&lt;changelog database="pgsql">
+ &lt;model version="1">
+ &lt;table name="person" kind="object">
+ &lt;column name="id" type="BIGINT" null="false"/>
+ &lt;column name="first" type="TEXT" null="false"/>
+ &lt;column name="last" type="TEXT" null="false"/>
+ &lt;primary-key auto="true">
+ &lt;column name="id"/>
+ &lt;/primary-key>
+ &lt;/table>
+ &lt;/model>
+&lt;/changelog>
+ </pre>
+
+ <p>Let's say we now would like to add another data member to the
+ <code>person</code> class &mdash; the middle name. We increment
+ the version and make the change:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string middle_;
+ std::string last_;
+};
+ </pre>
+
+ <p>We use exactly the same command line to re-compile our file:</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema person.hxx
+ </pre>
+
+ <p>This time the ODB compiler will read the old changelog, update
+ it, and write out the new version. Again, for illustration only,
+ below are the updated changelog contents:</p>
+
+ <pre class="xml">
+&lt;changelog database="pgsql">
+ &lt;changeset version="2">
+ &lt;alter-table name="person">
+ &lt;add-column name="middle" type="TEXT" null="false"/>
+ &lt;/alter-table>
+ &lt;/changeset>
+
+ &lt;model version="1">
+ &lt;table name="person" kind="object">
+ &lt;column name="id" type="BIGINT" null="false"/>
+ &lt;column name="first" type="TEXT" null="false"/>
+ &lt;column name="last" type="TEXT" null="false"/>
+ &lt;primary-key auto="true">
+ &lt;column name="id"/>
+ &lt;/primary-key>
+ &lt;/table>
+ &lt;/model>
+&lt;/changelog>
+ </pre>
+
+ <p>Just to reiterate, while the changelog may look like it could
+ be written by hand, it is maintained completely automatically
+ by the ODB compiler and the only reason you may want to look
+ at its contents is to review the database schema changes. For
+ example, if we compare the above two changelogs with
+ <code>diff</code>, we will get the following summary of the
+ database schema changes:</p>
+
+ <pre class="xml">
+--- person.xml.orig
++++ person.xml
+@@ -1,4 +1,10 @@
+&lt;changelog database="pgsql">
+<span style="color: #009E00">+ &lt;changeset version="2">
++ &lt;alter-table name="person">
++ &lt;add-column name="middle" type="TEXT" null="false"/>
++ &lt;/alter-table>
++ &lt;/changeset>
++</span>
+ &lt;model version="1">
+ &lt;table name="person" kind="object">
+ &lt;column name="id" type="BIGINT" null="false"/>
+ </pre>
+
+ <p>The changelog is only written when we generate the database schema,
+ that is, the <code>--generate-schema</code> option is specified.
+ Invocations of the ODB compiler that only produce the database
+ support code (C++) do not read or update the changelog. To put it
+ another way, the changelog tracks changes in the resulting database
+ schema, not the C++ object model.</p>
+
+ <p>ODB ignores column order when comparing database schemas. This means
+ that we can re-order data members in a class without causing any
+ schema changes. Member renames, however, will result in schema
+ changes since the column name changes as well (unless we specified
+ the column name explicitly). From ODB's perspective such a rename
+ looks like the deletion of one data member and the addition of
+ another. If we don't want this to be treated as a schema change,
+ then we will need to keep the old column name by explicitly
+ specifying it with the <code>db&nbsp;column</code> pragma. For
+ example, here is how we can rename <code>middle_</code> to
+ <code>middle_name_</code> without causing any schema changes:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db column("middle") // Keep the original column name.
+ std::string middle_name_;
+
+ ...
+};
+ </pre>
+
+ <p>If your object model consists of a large number of header files and
+ you generate the database schema for each of them individually, then
+ a changelog will be created for each of your header files. This may
+ be what you want, however, the large number of changelogs can quickly
+ become unwieldy. In fact, if you are generating the database schema
+ as standalone SQL files, then you may have already experienced a
+ similar problem caused by a large number of <code>.sql</code> files,
+ one for each header.</p>
+
+ <p>The solution to both of these problems is to generate a combined
+ database schema file and a single changelog. For example, assume
+ we have three header files in our object model:
+ <code>person.hxx</code>, <code>employee.hxx</code>, and
+ <code>employer.hxx</code>. To generate the database support code
+ we compile them as usual but without specifying the
+ <code>--generate-schema</code> option. In this case no changelog
+ is created or updated:</p>
+
+ <pre class="terminal">
+odb --database pgsql person.hxx
+odb --database pgsql employee.hxx
+odb --database pgsql employer.hxx
+ </pre>
+
+ <p>To generate the database schema, we perform a separate invocation
+ of the ODB compiler. This time, however, we instruct it to only
+ generate the schema (<code>--generate-schema-only</code>) and
+ produce it combined (<code>--at-once</code>) for all the files
+ in our object model:</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema-only --at-once \
+--input-name company person.hxx employee.hxx employer.hxx
+ </pre>
+
+ <p>The result of the above command is a single <code>company.sql</code>
+ file (the name is derived from the <code>--input-name</code> value)
+ that contains the database schema for our entire object model. There
+ is also a single corresponding changelog file &mdash;
+ <code>company.xml</code>.</p>
+
+ <p>The same can be achieved for the embedded schema by instructing
+ the ODB compiler to generate the database creation code into a
+ separate C++ file (<code>--schema-format&nbsp;separate</code>):</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema-only --schema-format separate \
+--at-once --input-name company person.hxx employee.hxx employer.hxx
+ </pre>
+
+ <p>The result of this command is a single <code>company-schema.cxx</code>
+ file and, again, <code>company.xml</code>.</p>
+
+ <p>Note also that by default the changelog file is not placed into
+ the directory specified with the <code>--output-dir</code> option.
+ This is due to the changelog being both an input and an output file
+ at the same time. As a result, by default, the ODB compiler will
+ place it in the directory of the input header file.</p>
+
+ <p>There is, however, a number of command line options (including
+ <code>--changelog-dir</code>) that allow us to fine-tune the name and
+ location of the changelog file. For example, you can instruct the ODB
+ compiler to read the changelog from one file while writing it to
+ another. This, for example, can be useful if you want to review
+ the changes before discarding the old file. For more information
+ on these options, refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a> and search for "changelog".</p>
+
+ <p>When we were discussing version increments above, we used the
+ terms <em>development</em> and <em>release</em>. Specifically,
+ we talked about keeping the same object model versions during
+ development periods and incrementing them after releases.
+ What is a development period and a release in this context?
+ These definitions can vary from project to project.
+ Generally, during a development period we work on one or
+ more changes to the object model that result in the changes
+ to the database schema. A release is a point where we
+ make our changes available to someone else who may have an
+ older database to migrate from. In the traditional sense, a release
+ is a point where you make a new version of your application available
+ to its users. However, for schema evolution purposes, a release
+ could also mean simply making your schema-altering changes
+ available to other developers on your team. Let us consider
+ two common scenarios to illustrate how all this fits together.</p>
+
+ <p>One way to setup a project would be to re-use the application
+ development period and application release for schema evolution.
+ That is, during a new application version development we keep
+ a single object model version and when we release the application,
+ we increment the model version. In this case it makes sense to
+ also reuse the application version as a model version for
+ consistency. Here is a step-by-step guide for this setup:</p>
+
+ <ol>
+ <li>During development, keep the current object model version open.</li>
+
+ <li>Before the release (for example, when entering a "feature freeze")
+ close the version.</li>
+
+ <li>After the release, update the version and open it.</li>
+
+ <li>For each new feature, review the changeset at the top of the
+ changelog, for example, with <code>diff</code> or your
+ version control facilities. If you are using a version
+ control, then this is best done just before committing
+ your changes to the repository.</li>
+ </ol>
+
+ <p>An alternative way to setup schema versioning in a project would
+ be to define the development period as working on a single
+ feature and the release as making this feature available to
+ other people (developers, testers, etc.) on your team, for
+ example, by committing the changes to a public version control
+ repository. In this case, the object model version will be
+ independent of the application version and can simply be
+ a sequence that starts with <code>1</code> and is
+ incremented by <code>1</code>. Here is a step-by-step guide
+ for this setup:</p>
+
+ <ol>
+ <li>Keep the current model version closed. Once a change is made
+ that affects the database schema, the ODB compiler will refuse
+ to update the changelog.</li>
+
+ <li>If the change is legitimate, open a new version, that is,
+ increment the current version and make it open.</li>
+
+ <li>Once the feature is implemented and tested, review the final
+ set of database changes (with <code>diff</code> or your
+ version control facilities), close the version, and commit
+ the changes to the version control repository (if using).</li>
+ </ol>
+
+ <p>If you are using a version control repository that supports
+ pre-commit checks, then you may want to consider adding such
+ a check to make sure the committed version is always closed.</p>
+
+ <p>If we are just starting schema evolution in our project, which
+ approach should we choose? The two approaches will work better
+ in different situations since they have a different set of
+ advantages and disadvantages. The first approach, which we
+ can call version per application release, is best suited
+ for simpler projects with smaller releases since otherwise
+ a single migration will bundle a large number of unrelated
+ actions corresponding to different features. This can
+ become difficult to review and, if things go wrong, debug.</p>
+
+ <p>The second approach, which we can call version per feature,
+ is much more modular and provides a number of additional benefits.
+ We can perform migrations for each feature as a discreet step
+ which makes it easier to debug. We can also place each such
+ migration step into a separate transaction further improving
+ reliability. It also scales much better in larger teams
+ where multiple developers can work concurrently on features
+ that affect the database schema. For example, if you find
+ yourself in a situation where another developer on your
+ team used the same version as you and managed to commit his
+ changes before you (that is, you have a merge conflict),
+ then you can simply change the version to the next available
+ one, regenerate the changelog, and continue with your commit.</p>
+
+ <p>Overall, unless you have strong reasons to prefer the version
+ per application release approach, rather choose version per
+ feature even though it may seem more complex at the
+ beginning. Also, if you do select the first approach, consider
+ provisioning for switching to the second method by reserving
+ a sub-version number. For example, for an application version
+ in the form <code>2.3.4</code> you can make the object model
+ version to be in the form <code>0x0203040000</code>, reserving
+ the last two bytes for a sub-version. Later on you can use it to
+ switch to the version per feature approach.</p>
+
+ <h2><a name="13.2">13.2 Schema Migration</a></h2>
+
+ <p>Once we enable schema evolution by specifying the object model
+ version, in addition to the schema creation statements, the
+ ODB compiler starts generating schema migration statements
+ for each version all the way from the base to the current.
+ As with schema creation, schema migration can be generated
+ either as a set of SQL files or embedded into the generated
+ C++ code (<code>--schema-format</code> option).</p>
+
+ <p>For each migration step, that is from one version to the next,
+ ODB generates two sets of statements: pre-migration and
+ post-migration. The pre-migration statements <em>"relax"</em>
+ the database schema so that both old and new data can co-exist.
+ At this stage new columns and tables are added while old
+ constraints are dropped. The post-migration statements
+ <em>"tighten"</em> the database schema back so that only
+ data conforming to the new format can remain. At this stage
+ old columns and tables are dropped and new constraints are
+ added. Now you can probably guess where the data
+ migration fits into this &mdash; between the pre and post
+ schema migrations where we can both access the old data
+ and create the new one.</p>
+
+ <p>If the schema is being generated as standalone SQL files,
+ then we end up with a pair of files for each step: the pre-migration
+ file and the post-migration file. For the <code>person</code>
+ example we started in the previous section we will have the
+ <code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
+ files. Here <code>002</code> is the version <em>to</em> which
+ we are migrating while the <code>pre</code> and <code>post</code>
+ suffixes specify the migration stage. So if we wanted to migrate
+ a <code>person</code> database from version <code>1</code>
+ to <code>2</code>, then we would first execute
+ <code>person-002-pre.sql</code>, then migrate the data, if any
+ (discussed in more detail in the next section), and finally
+ execute <code>person-002-post.sql</code>. If our database is
+ several versions behind, for example the database has version
+ <code>1</code> while the current version is <code>5</code>,
+ then we simply perform this set of steps for each version
+ until we reach the current version.</p>
+
+ <p>If we look at the contents of the <code>person-002-pre.sql</code>
+ file, we will see the following (or equivalent, depending on the
+ database used) statement:</p>
+
+ <pre class="sql">
+ALTER TABLE "person"
+ ADD COLUMN "middle" TEXT NULL;
+ </pre>
+
+ <p>As we would expect, this statement adds a new column corresponding
+ to the new data member. An observant reader would notice,
+ however, that the column is added as <code>NULL</code>
+ even though we never requested this semantics in our object model.
+ Why is the column added as <code>NULL</code>? If during migration
+ the <code>person</code> table already contains rows (that is, existing
+ objects), then an attempt to add a non-<code>NULL</code> column that
+ doesn't have a default value will fail. As a result, ODB will initially
+ add a new column that doesn't have a default value as <code>NULL</code>
+ but then clean this up at the post-migration stage. This way your data
+ migration code is given a chance to assign some meaningful values for
+ the new data member for all the existing objects. Here are the contents
+ of the <code>person-002-post.sql</code> file:</p>
+
+ <pre class="sql">
+ALTER TABLE "person"
+ ALTER COLUMN "middle" SET NOT NULL;
+ </pre>
+
+ <p>Currently ODB directly supports the following elementary database
+ schema changes:</p>
+
+ <ul class="list">
+ <li>add table</li>
+ <li>drop table</li>
+ <li>add column</li>
+ <li>drop column</li>
+ <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
+ <li>add foreign key</li>
+ <li>drop foreign key</li>
+ <li>add index</li>
+ <li>drop index</li>
+ </ul>
+
+ <p>More complex changes can normally be implemented in terms of
+ these building blocks. For example, to change a type of a
+ data member (which leads to a change of a column type), we
+ can add a new data member with the desired type (add column),
+ migrate the data, and then delete the old data member (drop
+ column). ODB will issue diagnostics for cases that are
+ currently not supported directly. Note also that some database
+ systems (notably SQLite) have a number of limitations in their
+ support for schema changes. For more information on these
+ database-specific limitations, refer to the "Limitations" sections
+ in <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>How do we know what the current database version is? That is, the
+ version <em>from</em> which we need to migrate? We need to know this,
+ for example, in order to determine the set of migrations we have to
+ perform. By default, when schema evolution is enabled, ODB maintains
+ this information in a special table called <code>schema_version</code>
+ that has the following (or equivalent, depending on the database
+ used) definition:</p>
+
+ <pre class="sql">
+CREATE TABLE "schema_version" (
+ "name" TEXT NOT NULL PRIMARY KEY,
+ "version" BIGINT NOT NULL,
+ "migration" BOOLEAN NOT NULL);
+ </pre>
+
+ <p>The <code>name</code> column is the schema name as specified with
+ the <code>--schema-name</code> option. It is empty for the default
+ schema. The <code>version</code> column contains the current database
+ version. And, finally, the <code>migration</code> flag indicates
+ whether we are in the process of migrating the database, that is,
+ between the pre and post-migration stages.</p>
+
+ <p>The schema creation statements (<code>person.sql</code> in our case)
+ create this table and populate it with the initial model version. For
+ example, if we executed <code>person.sql</code> corresponding to
+ version <code>1</code> of our object model, then <code>name</code>
+ would have been empty (which signifies the default schema since we
+ didn't specify <code>--schema-name</code>), <code>version</code> will
+ be <code>1</code> and <code>migration</code> will be
+ <code>FALSE</code>.</p>
+
+ <p>The pre-migration statements update the version and set the migration
+ flag to <code>TRUE</code>. Continuing with our example, after executing
+ <code>person-002-pre.sql</code>, <code>version</code> will
+ become <code>2</code> and <code>migration</code> will be set to
+ <code>TRUE</code>. The post-migration statements simply clear the
+ migration flag. In our case, after running
+ <code>person-002-post.sql</code>, <code>version</code> will
+ remain <code>2</code> while <code>migration</code> will be reset
+ to <code>FALSE</code>.</p>
+
+ <p>Note also that above we mentioned that the schema creation statements
+ (<code>person.sql</code>) create the <code>schema_version</code> table.
+ This means that if we enable schema evolution support in the middle
+ of a project, then we could already have existing databases that
+ don't include this table. As a result, ODB will not be able to handle
+ migrations for such databases unless we manually add the
+ <code>schema_version</code> table and populate it with the correct
+ version information. For this reason, it is highly recommended that
+ you consider whether to use schema evolution and, if so, enable it
+ from the beginning of your project.</p>
+
+ <p>The <code>odb::database</code> class provides an API for accessing
+ and modifying the current database version:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ typedef unsigned long long schema_version;
+
+ struct LIBODB_EXPORT schema_version_migration
+ {
+ schema_version_migration (schema_version = 0,
+ bool migration = false);
+
+ schema_version version;
+ bool migration;
+
+ // This class also provides the ==, !=, &lt;, >, &lt;=, and >= operators.
+ // Version ordering is as follows: {1,f} &lt; {2,t} &lt; {2,f} &lt; {3,t}.
+ };
+
+ class database
+ {
+ public:
+ ...
+
+ schema_version
+ schema_version (const std::string&amp; name = "") const;
+
+ bool
+ schema_migration (const std::string&amp; name = "") const;
+
+ const schema_version_migration&amp;
+ schema_version_migration (const std::string&amp; name = "") const;
+
+ // Set schema version and migration state manually.
+ //
+ void
+ schema_version_migration (schema_version,
+ bool migration,
+ const std::string&amp; name = "");
+
+ void
+ schema_version_migration (const schema_version_migration&amp;,
+ const std::string&amp; name = "");
+
+ // Set default schema version table for all schemas.
+ //
+ void
+ schema_version_table (const std::string&amp; table_name);
+
+ // Set schema version table for a specific schema.
+ //
+ void
+ schema_version_table (const std::string&amp; table_name,
+ const std::string&amp; name);
+ };
+}
+ </pre>
+
+ <p>The <code>schema_version()</code> and <code>schema_migration()</code>
+ accessors return the current database version and migration flag,
+ respectively. The optional <code>name</code> argument is the schema
+ name. If the database schema hasn't been created (that is, there is
+ no corresponding entry in the <code>schema_version</code> table or
+ this table does not exist), then <code>schema_version()</code> returns
+ <code>0</code>. The <code>schema_version_migration()</code> accessor
+ returns both version and migration flag together in the
+ <code>schema_version_migration</code> <code>struct</code>.</p>
+
+ <p>You may already have a version table in your database or you (or your
+ database administrator) may prefer to keep track of versions your own
+ way. You can instruct ODB not to create the <code>schema_version</code>
+ table with the <code>--suppress-schema-version</code> option. However,
+ ODB still needs to know the current database version in order for certain
+ schema evolution mechanisms to function properly. As a result, in
+ this case, you will need to set the schema version on the database
+ instance manually using the schema_version_migration() modifier.
+ Note that the modifier API is not thread-safe. That is, you should
+ not modify the schema version while other threads may be accessing
+ or modifying the same information.</p>
+
+ <p>Note also that the accessors we discussed above will only query the
+ <code>schema_version</code> table once and, if the version could
+ be determined, cache the result. If, however, the version could
+ not be determined (that is, <code>schema_version()</code> returned
+ 0), then a subsequent call will re-query the table. While it is
+ probably a bad idea to modify the database schema while the
+ application is running (other than via the <code>schema_catalog</code>
+ API, as discussed below), if for some reason you need ODB to re-query
+ the version, then you can manually set it to 0 using the
+ <code>schema_version_migration()</code> modifier.</p>
+
+ <p>It is also possible to change the name of the table that stores
+ the schema version using the <code>--schema-version-table</code>
+ option. You will also need to specify this alternative name on
+ the <code>database</code> instance using the <code>schema_version_table()</code>
+ modifier. The first version specifies the default table that is
+ used for all the schema names. The second version specifies the
+ table for a specific schema. The table name should be
+ database-quoted, if necessary.</p>
+
+ <p>If we are generating our schema migrations as standalone SQL files,
+ then the migration workflow could look like this:</p>
+
+ <ol>
+ <li>The database administrator determines the current database version.
+ If migration is required, then for each migration step (that
+ is, from one version to the next), they perform the following:</li>
+
+ <li>Execute the pre-migration file.</li>
+
+ <li>Execute our application (or a separate migration program)
+ to perform data migration (discussed later). Our application
+ can determine that is is being executed in the "migration mode"
+ by calling <code>schema_migration()</code> and then which
+ migration code to run by calling <code>schema_version()</code>.</li>
+
+ <li>Execute the post-migration file.</li>
+ </ol>
+
+ <p>These steps become more integrated and automatic if we embed the
+ schema creation and migration code into the generated C++ code.
+ Now we can perform schema creation, schema migration, and data
+ migration as well as determine when each step is necessary
+ programmatically from within the application.</p>
+
+ <p>Schema evolution support adds the following extra functions to
+ the <code>odb::schema_catalog</code> class, which we first discussed
+ in <a href="#3.4">Section 3.4, "Database"</a>.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class schema_catalog
+ {
+ public:
+ ...
+
+
+ // Schema migration.
+ //
+ static void
+ migrate_schema_pre (database&amp;,
+ schema_version,
+ const std::string&amp; name = "");
+
+ static void
+ migrate_schema_post (database&amp;,
+ schema_version,
+ const std::string&amp; name = "");
+
+ static void
+ migrate_schema (database&amp;,
+ schema_version,
+ const std::string&amp; name = "");
+
+ // Data migration.
+ //
+ // Discussed in the next section.
+
+
+ // Combined schema and data migration.
+ //
+ static void
+ migrate (database&amp;,
+ schema_version = 0,
+ const std::string&amp; name = "");
+
+ // Schema version information.
+ //
+ static schema_version
+ base_version (const database&amp;,
+ const std::string&amp; name = "");
+
+ static schema_version
+ base_version (database_id,
+ const std::string&amp; name = "");
+
+ static schema_version
+ current_version (const database&amp;,
+ const std::string&amp; name = "");
+
+ static schema_version
+ current_version (database_id,
+ const std::string&amp; name = "");
+
+ static schema_version
+ next_version (const database&amp;,
+ schema_version = 0,
+ const std::string&amp; name = "");
+
+ static schema_version
+ next_version (database_id,
+ schema_version,
+ const std::string&amp; name = "");
+ };
+}
+ </pre>
+
+ <p>The <code>migrate_schema_pre()</code> and
+ <code>migrate_schema_post()</code> static functions perform
+ a single stage (that is, pre or post) of a single migration
+ step (that is, from one version to the next). The <code>version</code>
+ argument specifies the version we are migrating to. For
+ instance, in our <code>person</code> example, if we know that
+ the database version is <code>1</code> and the next version
+ is <code>2</code>, then we can execute code like this:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+schema_catalog::migrate_schema_pre (db, 2);
+
+// Data migration goes here.
+
+schema_catalog::migrate_schema_post (db, 2);
+
+t.commit ();
+ </pre>
+
+ <p>If you don't have any data migration code to run, then you can
+ perform both stages with a single call using the
+ <code>migrate_schema()</code> static function.</p>
+
+ <p>The <code>migrate()</code> static function perform both schema
+ and data migration (we discuss data migration in the next section).
+ It can also perform several migration steps at once. If we don't
+ specify its target version, then it will migrate (if necessary)
+ all the way to the current model version. As an extra convenience,
+ <code>migrate()</code> will also create the database schema if
+ none exists. As a result, if we don't have any data migration
+ code or we have registered it with <code>schema_catalog</code> (as
+ discussed later), then the database schema creation and migration,
+ whichever is necessary, if at all, can be performed with a single
+ function call:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+schema_catalog::migrate (db);
+t.commit ();
+ </pre>
+
+ <p>Note also that <code>schema_catalog</code> is integrated with the
+ <code>odb::database</code> schema version API. In particular,
+ <code>schema_catalog</code> functions will query and synchronize
+ the schema version on the <code>database</code> instance if and
+ when required.</p>
+
+ <p>The <code>schema_catalog</code> class also allows you to iterate
+ over known versions (remember, there could be "gaps" in version
+ numbers) with the <code>base_version()</code>,
+ <code>current_version()</code> and <code>next_version()</code>
+ static functions. The <code>base_version()</code> and
+ <code>current_version()</code> functions return the base and
+ current object model versions, respectively. That is, the
+ lowest version from which we can migrate and the version that
+ we ultimately want to migrate to. The <code>next_version()</code>
+ function returns the next known version. If the passed version is
+ greater or equal to the current version, then this function
+ will return the current version plus one (that is, one past
+ current). If we don't specify the version, then
+ <code>next_version()</code> will use the current database version
+ as the starting point. Note also that the schema version information
+ provided by these functions is only available if we embed the schema
+ migration code into the generated C++ code. For standalone SQL file
+ migrations this information is normally not needed since the migration
+ process is directed by an external entity, such as a database
+ administrator or a script.</p>
+
+ <p>Most <code>schema_catalog</code> functions presented above also accept
+ the optional schema name argument. If the passed schema name is not
+ found, then the <code>odb::unknown_schema</code> exception is
+ thrown. Similarly, functions that accept the schema version argument will
+ throw the <code>odb::unknown_schema_version</code> exception if the
+ passed or current version is invalid. Refer to <a href="#3.14">Section
+ 3.14, "ODB Exceptions"</a> for more information on these exceptions.</p>
+
+ <p>To illustrate how all these parts fit together, consider the
+ following more realistic database schema management example.
+ Here we want to handle the schema creation in a special way
+ and perform each migration step in its own transaction.</p>
+
+ <pre class="cxx">
+schema_version v (db.schema_version ());
+schema_version bv (schema_catalog::base_version (db));
+schema_version cv (schema_catalog::current_version (db));
+
+if (v == 0)
+{
+ // No schema in the database. Create the schema and
+ // initialize the database.
+ //
+ transaction t (db.begin ());
+ schema_catalog::create_schema (db);
+
+ // Populate the database with initial data, if any.
+
+ t.commit ();
+}
+else if (v &lt; cv)
+{
+ // Old schema (and data) in the database, migrate them.
+ //
+
+ if (v &lt; bv)
+ {
+ // Error: migration from this version is no longer supported.
+ }
+
+ for (v = schema_catalog::next_version (db, v);
+ v &lt;= cv;
+ v = schema_catalog::next_version (db, v))
+ {
+ transaction t (db.begin ());
+ schema_catalog::migrate_schema_pre (db, v);
+
+ // Data migration goes here.
+
+ schema_catalog::migrate_schema_post (db, v);
+ t.commit ();
+ }
+}
+else if (v > cv)
+{
+ // Error: old application trying to access new database.
+}
+ </pre>
+
+ <h2><a name="13.3">13.3 Data Migration</a></h2>
+
+ <p>In quite a few cases specifying the default value for new data
+ members will be all that's required to handle the existing objects.
+ For example, the natural default value for the new middle name
+ that we have added is an empty string. And we can handle
+ this case with the <code>db&nbsp;default</code> pragma and without
+ any extra C++ code:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+
+#pragma db object
+class person
+{
+ ...
+
+
+ #pragma db default("")
+ std::string middle_;
+};
+ </pre>
+
+ <p>However, there will be situations where we would need to perform
+ more elaborate data migrations, that is, convert old data to the
+ new format. As an example, suppose we want to add gender to our
+ <code>person</code> class. And, instead of leaving it unassigned
+ for all the existing objects, we will try to guess it from the
+ first name. This is not particularly accurate but it could be
+ sufficient for our hypothetical application:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 3)
+
+enum gender {male, female};
+
+#pragma db object
+class person
+{
+ ...
+
+ gender gender_;
+};
+ </pre>
+
+ <p>As we have discussed earlier, there are two ways to perform data
+ migration: immediate and gradual. To recap, with immediate
+ migration we migrate all the existing objects at once, normally
+ after the schema pre-migration statements but before the
+ post-migration statements. With gradual migration, we make sure
+ the new object model can accommodate both old and new data and
+ gradually migrate existing objects as the application runs and
+ the opportunities to do so arise, for example, an object is
+ updated.</p>
+
+ <p>There is also another option for data migration that is not
+ discussed further in this section. Instead of using our C++
+ object model we could execute ad-hoc SQL statements that
+ perform the necessary conversions and migrations directly
+ on the database server. While in certain cases this can be
+ a better option from the performance point of view, this
+ approach is often limited in terms of the migration logic
+ that we can handle.</p>
+
+ <h2><a name="13.3.1">13.3.1 Immediate Data Migration</a></h2>
+
+ <p>Let's first see how we can implement an immediate migration for the
+ new <code>gender_</code> data member we have added above. If we
+ are using standalone SQL files for migration, then we could add
+ code along these lines somewhere early in <code>main()</code>,
+ before the main application logic:</p>
+
+ <pre class="cxx">
+int
+main ()
+{
+ ...
+
+ odb::database&amp; db = ...
+
+ // Migrate data if necessary.
+ //
+ if (db.schema_migration ())
+ {
+ switch (db.schema_version ())
+ {
+ case 3:
+ {
+ // Assign gender to all the existing objects.
+ //
+ transaction t (db.begin ());
+
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+
+ t.commit ();
+ break;
+ }
+ }
+ }
+
+ ...
+}
+ </pre>
+
+ <p>If you have a large number of objects to migrate, it may also be
+ a good idea, from the performance point of view, to break one big
+ transaction that we now have into multiple smaller transactions
+ (<a href="#3.5">Section 3.5, "Transactions"</a>). For example:</p>
+
+ <pre class="cxx">
+case 3:
+ {
+ transaction t (db.begin ());
+
+ size_t n (0);
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+
+ // Commit the current transaction and start a new one after
+ // every 100 updates.
+ //
+ if (n++ % 100 == 0)
+ {
+ t.commit ();
+ t.reset (db.begin ());
+ }
+ }
+
+ t.commit ();
+ break;
+ }
+ </pre>
+
+ <p>While it looks straightforward enough, as we add more migration
+ snippets, this approach can quickly become unmaintainable. Instead
+ of having all the migrations in a single function and determining
+ when to run each piece ourselves, we can package each migration into
+ a separate function, register it with the <code>schema_catalog</code>
+ class, and let ODB figure out when to run which migration functions.
+ To support this functionality, <code>schema_catalog</code> provides
+ the following data migration API:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class schema_catalog
+ {
+ public:
+ ...
+
+ // Data migration.
+ //
+ static std::size_t
+ migrate_data (database&amp;,
+ schema_version = 0,
+ const std::string&amp; name = "");
+
+ typedef void data_migration_function_type (database&amp;);
+
+ // Common (for all the databases) data migration, C++98/03 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ // Common (for all the databases) data migration, C++11 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+
+ // Database-specific data migration, C++98/03 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database&amp;,
+ data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database_id,
+ data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ // Database-specific data migration, C++11 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database&amp;,
+ std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database_id,
+ std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+ };
+
+ // Static data migration function registration, C++98/03 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ struct data_migration_entry
+ {
+ data_migration_entry (data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ data_migration_entry (database_id,
+ data_migration_function_type*,
+ const std::string&amp; name = "");
+ };
+
+ // Static data migration function registration, C++11 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ struct data_migration_entry
+ {
+ data_migration_entry (std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+
+ data_migration_entry (database_id,
+ std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+ };
+}
+ </pre>
+
+ <p>The <code>migrate_data()</code> static function performs data
+ migration for the specified version. If no version is specified,
+ then it will use the current database version and also check
+ whether the database is in migration, that is,
+ <code>database::schema_migration()</code> returns <code>true</code>.
+ As a result, all we need to do in our <code>main()</code> is call
+ this function. It will check if migration is required and if so,
+ call all the migration functions registered for this version. For
+ example:</p>
+
+ <pre class="cxx">
+int
+main ()
+{
+ ...
+
+ database&amp; db = ...
+
+ // Check if we need to migrate any data and do so
+ // if that's the case.
+ //
+ schema_catalog::migrate_data (db);
+
+ ...
+}
+ </pre>
+
+ <p>The <code>migrate_data()</code> function returns the number of
+ migration functions called. You can use this value for debugging
+ or logging.</p>
+
+ <p>The only other step that we need to perform is register our data
+ migration functions with <code>schema_catalog</code>. At the
+ lower level we can call the <code>data_migration_function()</code>
+ static function for every migration function we have, for example,
+ at the beginning of <code>main()</code>. For each version, data
+ migration functions are called in the order of registration.</p>
+
+ <p>A more convenient approach, however, is to use the
+ <code>data_migration_entry</code> helper class template to register the
+ migration functions during static initialization. This way we
+ can keep the migration function and its registration code next
+ to each other. Here is how we can reimplement our <code>gender</code>
+ migration code to use this mechanism:</p>
+
+ <pre class="cxx">
+static void
+migrate_gender (odb::database&amp; db)
+{
+ transaction t (db.begin ());
+
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+
+ t.commit ();
+}
+
+static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
+migrate_gender_entry (&amp;migrate_gender);
+ </pre>
+
+ <p>The first template argument to the <code>data_migration_entry</code>
+ class template is the version we want this data migration function
+ to be called for. The second template argument is the base model
+ version. This second argument is necessary to detect the situation
+ where we no longer need this data migration function. Remember
+ that when we move the base model version forward, migrations from
+ any version below the new base are no longer possible. We, however,
+ may still have migration functions registered for those lower
+ versions. Since these functions will never be called, they are
+ effectively dead code and it would be useful to identify and
+ remove them. To assist with this, <code>data_migration_entry</code>
+ (and lower lever <code>data_migration_function()</code>) will
+ check at compile time (that is, <code>static_assert</code>) that
+ the registration version is greater than the base model version.</p>
+
+ <p>In the above example we use the <code>MYAPP_BASE_VERSION</code>
+ macro that is presumably defined in a central place, for example,
+ <code>version.hxx</code>. This is the recommended approach since
+ we can update the base version in a single place and have the
+ C++ compiler automatically identify all the data migration
+ functions that can be removed.</p>
+
+ <p>In C++11 we can also create a template alias so that we don't
+ have to repeat the base model macro in every registration, for
+ example:</p>
+
+ <pre class="cxx">
+template &lt;schema_version v>
+using migration_entry = odb::data_migration_entry&lt;v, MYAPP_BASE_VERSION>;
+
+static const migration_entry&lt;3>
+migrate_gender_entry (&amp;migrate_gender);
+ </pre>
+
+ <p>For cases where you need to by-pass the base version check, for
+ example, to implement your own registration helper, ODB also
+ provides "unsafe" versions of the <code>data_migration_function()</code>
+ functions that take the version as a function argument rather than
+ as a template parameter.</p>
+
+ <p>In C++11 we can also use lambdas as migration functions, which makes
+ the migration code more concise:</p>
+
+ <pre class="cxx">
+static const migration_entry&lt;3>
+migrate_gender_entry (
+ [] (odb::database&amp; db)
+ {
+ transaction t (db.begin ());
+
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+
+ t.commit ();
+ });
+ </pre>
+
+ <p>If we are using embedded schema migrations, then both schema and
+ data migration is integrated and can be performed with a single
+ call to the <code>schema_catalog::migrate()</code> function that
+ we discussed earlier. For example:</p>
+
+<pre class="cxx">
+int
+main ()
+{
+ ...
+
+ database&amp; db = ...
+
+ // Check if we need to migrate the database and do so
+ // if that's the case.
+ //
+ {
+ transaction t (db.begin ());
+ schema_catalog::migrate (db);
+ t.commit ();
+ }
+
+ ...
+}
+ </pre>
+
+ <p>Note, however, that in this case we call <code>migrate()</code>
+ within a transaction (for the schema migration part) which means
+ that our migration functions will also be called within this
+ transaction. As a result, we will need to adjust our migration
+ functions not to start their own transaction:</p>
+
+ <pre class="cxx">
+static void
+migrate_gender (odb::database&amp; db)
+{
+ // Assume we are already in a transaction.
+ //
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+}
+ </pre>
+
+ <p>If, however, we want more granular transactions, then we can
+ use the lower-level <code>schema_catalog</code> functions to
+ gain more control, as we have seen at the end of the previous
+ section. Here is the relevant part of that example with
+ an added data migration call:</p>
+
+ <pre class="cxx">
+ // Old schema (and data) in the database, migrate them.
+ //
+ for (v = schema_catalog::next_version (db, v);
+ v &lt;= cv;
+ v = schema_catalog::next_version (db, v))
+ {
+ transaction t (db.begin ());
+ schema_catalog::migrate_schema_pre (db, v);
+ schema_catalog::migrate_data (db, v);
+ schema_catalog::migrate_schema_post (db, v);
+ t.commit ();
+ }
+ </pre>
+
+ <h2><a name="13.3.2">13.3.2 Gradual Data Migration</a></h2>
+
+ <p>If the number of existing objects that require migration is large,
+ then an all-at-once, immediate migration, while simple, may not
+ be practical from a performance point of view. In this case,
+ we can perform a gradual migration as the application does
+ its normal functions.</p>
+
+ <p>With gradual migrations, the object model must be capable of
+ representing data that conforms to both old and new formats at
+ the same time since, in general, the database will contain a
+ mixture of old and new objects. For example, in case of our
+ <code>gender</code> data member, we need a special value that
+ represents the "no gender assigned yet" case (an old object).
+ We also need to assign this special value to all the existing
+ objects during the schema pre-migration stage. One way to do
+ this would be add a special value to our <code>gender</code>
+ enum and then make it the default value with the
+ <code>db&nbsp;default</code> pragma. A cleaner and easier approach,
+ however, is to use <code>NULL</code> as a special value. We
+ can add support for the <code>NULL</code> value semantics
+ to any existing type by wrapping it with
+ <code>odb::nullable</code>, <code>boost::optional</code>
+ or similar (<a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
+ Value Semantics"</a>). We also don't need to specify the default value
+ explicitly since <code>NULL</code> is used automatically. Here
+ is how we can use this approach in our <code>gender</code>
+ example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/nullable.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+ odb::nullable&lt;gender> gender_;
+};
+ </pre>
+
+ <p>A variety of strategies can be employed to implement gradual
+ migrations. For example, we can migrate the data when the object
+ is updated as part of the normal application logic. While there
+ is no migration cost associated with this approach (the object
+ is updated anyway), depending on how often objects are typically
+ updated, this strategy can take a long time to complete. An
+ alternative strategy would be to perform an update whenever
+ an old object is loaded. Yet another strategy is to have a
+ separate thread that slowly migrates all the old objects as
+ the application runs.</p>
+
+ <p>As an example, let us implement the first approach for our
+ <code>gender</code> migration. While we could have added
+ the necessary code throughout the application, from the
+ maintenance point of view, it is best to try and localize
+ the gradual migration logic to the persistent classes that
+ it affects. And for this database operation callbacks
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
+ are a very useful mechanism. In our case, all we have to do is handle
+ the <code>post_load</code> event where we guess the gender
+ if it is <code>NULL</code>:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx> // odb::database
+#include &lt;odb/callback.hxx> // odb::callback_event
+#include &lt;odb/nullable.hxx>
+
+#pragma db object callback(migrate)
+class person
+{
+ ...
+
+ void
+ migrate (odb::callback_event e, odb::database&amp;)
+ {
+ if (e == odb::callback_event::post_load)
+ {
+ // Guess gender if not assigned.
+ //
+ if (gender_.null ())
+ gender_ = guess_gender (first_);
+ }
+ }
+
+ odb::nullable&lt;gender> gender_;
+};
+ </pre>
+
+ <p>In particular, we don't have to touch any of the accessors
+ or modifiers or the application logic &mdash; all of them
+ can assume that the value can never be <code>NULL</code>.
+ And when the object is next updated, the new <code>gender</code>
+ value will be stored automatically.</p>
+
+ <p>All gradual migrations normally end up with a terminating
+ immediate migration some number of versions down the line,
+ when the bulk of the objects has presumably been converted.
+ This way we don't have to keep the gradual migration code
+ around forever. Here is how we could implement a terminating
+ migration for our example:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+ gender gender_;
+};
+
+// person.cxx
+//
+static void
+migrate_gender (odb::database&amp; db)
+{
+ typedef odb::query&lt;person> query;
+
+ for (person&amp; p: db.query&lt;person> (query::gender.is_null ()))
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
+migrate_gender_entry (&amp;migrate_gender);
+ </pre>
+
+ <p>A couple of points to note about this code. Firstly, we
+ removed all the gradual migration logic (the callback)
+ from the class and replaced it with the immediate migration
+ function. We also removed the <code>odb::nullable</code>
+ wrapper (and therefore disallowed the <code>NULL</code> values)
+ since after this migration all the objects will have been
+ converted. Finally, in the migration function, we only query
+ the database for objects that need migration, that is, have
+ <code>NULL</code> gender.</p>
+
+ <h2><a name="13.4">13.4 Soft Object Model Changes</a></h2>
+
+ <p>Let us consider another common kind of object model change:
+ we delete an old member, add a new one, and need to copy
+ the data from the old to the new, perhaps applying some
+ conversion. For example, we may realize that in our application
+ it is a better idea to store a person's name as a single string
+ rather than split it into three fields. So what we would like to do
+ is add a new data member, let's call it <code>name_</code>, convert
+ all the existing split names, and then delete the <code>first_</code>,
+ <code>middle_</code>, and <code>last_</code> data members.</p>
+
+ <p>While this sounds straightforward, there is a problem. If we
+ delete (that is, physically remove from the source code) the
+ old data members, then we won't be able to access the old
+ data. The data will still be available in the database between
+ the schema pre and post-migrations, it is just we will no longer
+ be able to access it through our object model. And if we keep
+ the old data members around, then the old data will remain
+ stored in the database even after the schema post-migration.</p>
+
+ <p>There is also a more subtle problem that has to do with existing
+ migrations for the previous versions. Remember, in version <code>3</code>
+ of our <code>person</code> example we added the <code>gender_</code>
+ data member. We also have a data migration function which guesses
+ the gender based on the first name. Deleting the <code>first_</code>
+ data member from our class will obviously break this code. But
+ even adding the new <code>name_</code> data member will cause
+ problems because when we try to update the object in order to
+ store the new gender, ODB will try to update <code>name_</code>
+ as well. But there is no corresponding column in the database
+ yet. When we run this migration function, we are still several
+ versions away from the point where the <code>name</code> column
+ will be added.</p>
+
+ <p>This is a very subtle but also very important implication to
+ understand. Unlike the main application logic, which only needs
+ to deal with the current model version, data migration code works
+ on databases that can be multiple versions behind the current
+ version.</p>
+
+ <p>How can we resolve this problem? It appears what we need is the
+ ability to add or delete data members starting from a specific
+ version. In ODB this mechanism is called soft member additions
+ and deletions. A soft-added member is only treated as persistent
+ starting from the addition version. A soft-deleted member is
+ persistent until the deletion version (but including the migration
+ stage). In its essence, soft model changes allow us to maintain
+ multiple versions of our object model all with a single set of
+ persistent classes. Let us now see how this functionality can
+ help implement our changes:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db deleted(4)
+ std::string first_;
+
+ #pragma db deleted(4)
+ std::string middle_;
+
+ #pragma db deleted(4)
+ std::string last_;
+
+ #pragma db added(4)
+ std::string name_;
+
+ gender gender_;
+};
+ </pre>
+
+ <p>The migration function for this change could then look like
+ this:</p>
+
+ <pre class="cxx">
+static void
+migrate_name (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.name (p.first () + " " +
+ p.middle () + (p.middle ().empty () ? "" : " ") +
+ p.last ());
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
+migrate_name_entry (&amp;migrate_name);
+ </pre>
+
+ <p>Note also that no changes are required to the gender migration
+ function.</p>
+
+ <p>As you may have noticed, in the code above we assumed that the
+ <code>person</code> class still provides public accessors for
+ the now deleted data members. This might not be ideal since now
+ they should not be used by the application logic. The only code
+ that may still need to access them is the migration functions. The
+ recommended way to resolve this is to remove the accessors/modifiers
+ corresponding to the deleted data member, make migration functions
+ static functions of the class being migrated, and then access
+ the deleted data members directly. For example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db deleted(4)
+ std::string first_;
+
+ #pragma db deleted(4)
+ std::string middle_;
+
+ #pragma db deleted(4)
+ std::string last_;
+
+ #pragma db added(4)
+ std::string name_;
+
+ gender gender_;
+
+private:
+ static void
+ migrate_gender (odb::database&amp;);
+
+ static void
+ migrate_name (odb::database&amp;);
+};
+
+void person::
+migrate_gender (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender_ = guess_gender (p.first_);
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
+migrate_name_entry (&amp;migrate_gender);
+
+void person::
+migrate_name (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.name_ = p.first_ + " " +
+ p.middle_ + (p.middle_.empty () ? "" : " ") +
+ p.last_;
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
+migrate_name_entry (&amp;migrate_name);
+ </pre>
+
+ <p>Another potential issue with the soft-deletion is the requirement
+ to keep the delete data members in the class. While they will not
+ be initialized in the normal operation of the application (that
+ is, not a migration), this can still be a problem if we need to
+ minimize the memory footprint of our classes. For example, we may
+ cache a large number of objects in memory and having three
+ <code>std::string</code> data members can be a significant
+ overhead.</p>
+
+ <p>The recommended way to resolve this issue is to place all the
+ deleted data members into a dynamically allocated composite
+ value type. For example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db added(4)
+ std::string name_;
+
+ gender gender_;
+
+ #pragma db value
+ struct deleted_data
+ {
+ #pragma db deleted(4)
+ std::string first_;
+
+ #pragma db deleted(4)
+ std::string middle_;
+
+ #pragma db deleted(4)
+ std::string last_;
+ };
+
+ #pragma db column("")
+ std::unique_ptr&lt;deleted_data> dd_;
+
+ ...
+};
+ </pre>
+
+ <p>ODB will then automatically allocate the deleted value type if
+ any of the deleted data members are being loaded. During the normal
+ operation, however, the pointer will stay <code>NULL</code> and
+ therefore reduce the common case overhead to a single pointer
+ per class. Note that we make the composite value column prefix
+ empty (the <code>db&nbsp;column("")</code> pragma) in order to
+ keep the same column names for the deleted data members.</p>
+
+ <p>Soft-added and deleted data members can be used in objects,
+ composite values, views, and container value types. We can
+ also soft-add and delete data members of simple, composite,
+ pointer to object, and container types. Only special data
+ members, such as the object id and the optimistic concurrency
+ version, cannot be soft-added or deleted.</p>
+
+ <p>It is also possible to soft-delete a persistent class. We
+ can still work with the existing objects of such a class,
+ however, no table is created in new databases for soft-deleted
+ classes. To put it another way, a soft-delete class is like an
+ abstract class (no table) but which can still be loaded, updated,
+ etc. Soft-added persistent classes do not make much sense and
+ are therefore not supported.</p>
+
+ <p>As an example of a soft-deleted class, suppose we want to
+ replace our <code>person</code> class with the new
+ <code>employee</code> object and migrate the data. Here is
+ how we could do this:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 5)
+
+#pragma db object deleted(5)
+class person
+{
+ ...
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string name_;
+ gender gender_;
+
+ static void
+ migrate_person (odb::database&amp;);
+};
+
+void employee::
+migrate_person (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ employee e (p.name (), p.gender ());
+ db.persist (e);
+ }
+}
+
+static const odb::data_migration_entry&lt;5, MYAPP_BASE_VERSION>
+migrate_person_entry (&amp;migrate_person);
+ </pre>
+
+ <p>As we have seen above, hard member additions and deletions can
+ (and most likely will) break existing data migration code. Why,
+ then, not treat all the changes, or at least additions, as soft?
+ ODB requires you to explicitly request this semantics because
+ support for soft-added and deleted data members incurs runtime
+ overhead. And there can be plenty of cases where there is no
+ existing data migration and therefore hard additions and deletions
+ are sufficient.</p>
+
+ <p>In some cases a hard addition or deletion will result in a
+ compile-time error. For example, one of the data migration
+ functions may reference the data member we just deleted. In
+ many cases, however, such errors can only be detected at
+ runtime, and, worse yet, only when the migration function
+ is executed. For example, we may hard-add a new data member
+ that an existing migration function will try to indirectly
+ store in the database as part of an object update. As a result,
+ it is highly recommended that you always test your application
+ with the database that starts at the base version so that every
+ data migration function is called and therefore ensured to
+ still work correctly.</p>
+
+ <p>To help with this problem you can also instruct ODB to warn
+ you about any hard additions or deletions with the
+ <code>--warn-hard-add</code>, <code>--warn-hard-delete</code>,
+ and <code>--warn-hard</code> command line options. ODB will
+ only warn you about hard changes in the current version and
+ only for as long as it is open, which makes this mechanism
+ fairly usable.</p>
+
+ <p>You may also be wondering why we have to specify the addition
+ and deletion versions explicitly. It may seem like the ODB compiler
+ should be able to figure this out automatically. While it is
+ theoretically possible, to achieve this, ODB would have to also
+ maintain a separate changelog of the C++ object model in
+ addition to the database schema changelog it already maintains.
+ While being a lot more complex, such an additional changelog
+ would also complicate the workflow significantly. In this light,
+ maintaining this change information as part of the original
+ source files appears to be a cleaner and simpler approach.</p>
+
+ <p>As we discussed before, when we move the base model version
+ forward we essentially drop support for migrations from
+ versions before the new base. As a result, it is no longer
+ necessary to maintain the soft semantics of additions and
+ deletions up to and including the new base version. ODB
+ will issue diagnostics for all such members and classes.
+ For soft deletions we can simply remove the data member or
+ class entirely. For soft additions we only need to remove the
+ <code>db&nbsp;added</code> pragma.</p>
+
+ <h2><a name="13.4.1">13.4.1 Reuse Inheritance Changes</a></h2>
+
+ <p>Besides adding and deleting data members, another way to alter
+ the object's table is using reuse-style inheritance. If we add
+ a new reuse base, then, from the database schema point of view,
+ this is equivalent to adding all its columns to the derived
+ object's table. Similarly, deleting reuse inheritance results in
+ all the base's columns being deleted from the derived's table.</p>
+
+ <p>In the future ODB may provide direct support for soft addition
+ and deletion of inheritance. Currently, however, this semantics
+ can be emulated with soft-added and deleted data members. The
+ following table describes the most common scenarios depending
+ on where columns are added or deleted, that is, base table,
+ derived table, or both.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="scenarios" border="1">
+ <tr>
+ <th>DELETE</th>
+ <th style="width: 40%">HARD</th>
+ <th style="width: 40%">SOFT</th>
+ </tr>
+
+ <tr>
+ <td>In both (delete inheritance and base)</td>
+ <td>Delete inheritance and base. Move object id to derived.</td>
+ <td>Soft-delete base. Mark all data members (except id) in
+ base as soft-deleted.</td>
+ </tr>
+
+ <tr>
+ <td>In base only (delete base)</td>
+ <td>Option 1: mark base as abstract.<br/><br/>
+ Option 2: move all the base member to derived, delete base.</td>
+ <td>Soft-delete base.</td>
+ </tr>
+
+ <tr>
+ <td>In derived only (delete inheritance)</td>
+ <td>Delete inheritance, add object id to derived.</td>
+ <td>Option 1: copy base to a new soft-deleted base, inherit
+ from it instead. Mark all the data members (expect id) in
+ this new base as soft-deleted. Note: we add the new base
+ as soft-deleted to get notified when we can remove it.<br/><br/>
+ Option 2: Copy all the data members from base to derived
+ and mark them as soft-deleted in derived.</td>
+ </tr>
+ </table>
+
+
+ <table class="scenarios" border="1">
+ <tr>
+ <th>ADD</th>
+ <th style="width: 40%">HARD</th>
+ <th style="width: 40%">SOFT</th>
+ </tr>
+
+ <tr>
+ <td>In both (add new base and inheritance)</td>
+ <td>Add new base and inheritance. Potentially move object id
+ member from derived to base.</td>
+ <td>Add new base and mark all its data members as soft-added.
+ Add inheritance. Move object id from derived to base.</td>
+ </tr>
+
+ <tr>
+ <td>In base only (refactor existing data to new base)</td>
+ <td>Add new base and move data members from derived to base.
+ Note: in most cases the new base will be made abstract
+ which make this scenario non-schema changing.</td>
+ <td>The same as HARD.</td>
+ </tr>
+
+ <tr>
+ <td>In derived only (add inheritance to existing base)</td>
+ <td>Add inheritance, delete object id in derived.</td>
+ <td>Copy existing base to a new abstract base and inherit
+ from it. Mark all the database members in the new base
+ as soft-added (except object id). When notified by the
+ ODB compiler that the soft addition of the data members
+ is no longer necessary, delete the copy and inherit from
+ the original base.</td>
+ </tr>
+ </table>
+
+ <h2><a name="13.4.2">13.4.2 Polymorphism Inheritance Changes</a></h2>
+
+ <p>Unlike reuse inheritance, adding or deleting a polymorphic base
+ does not result in the base's data members being added or deleted
+ from the derived object's table because each class in a polymorphic
+ hierarchy is stored in a separate table. There are, however, other
+ complications due to the presence of special columns (discriminator
+ in the root table and object id links in derived tables) which makes
+ altering the hierarchy structure difficult to handle automatically.
+ Adding or deleting (including soft-deleting) of leaf classes (or
+ leaf sub-hierarchies) in a polymorphic hierarchy is fully supported.
+ Any more complex changes, such as adding or deleting the root or
+ an intermediate base or getting an existing class into or out of
+ a polymorphic hierarchy can be handled by creating a new leaf class
+ (or leaf sub-hierarchy), soft-deleting the old class, and migrating
+ the data.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="14">14 ODB Pragma Language</a></h1>
+
+ <p>As we have already seen in previous chapters, ODB uses a pragma-based
+ language to capture database-specific information about C++ types.
+ This chapter describes the ODB pragma language in more detail. It
+ can be read together with other chapters in the manual to get a
+ sense of what kind of configurations and mapping fine-tuning are
+ possible. You can also use this chapter as a reference at a later
+ stage.</p>
+
+ <p>An ODB pragma has the following syntax:</p>
+
+ <p><code>#pragma db <i>qualifier</i> [<i>specifier</i> <i>specifier</i> ...]</code></p>
+
+ <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>,
+ <code>namespace</code>, <code>model</code>, <code>index</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 while the <code>model</code> qualifier describes
+ the whole C++ object model. The <code>index</code> qualifier defines
+ a database index. And, finally, 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
+ <code>id</code> member specifier tells the ODB compiler that this
+ member contains this object's identifier. Below is the declaration
+ of the <code>person</code> class that shows how we can use ODB
+ pragmas:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db member id
+ unsigned long id_;
+ ...
+};
+ </pre>
+
+ <p>In the above example we don't explicitly specify which C++ class or
+ data member the pragma belongs to. Rather, the pragma applies to
+ a C++ declaration that immediately follows the pragma. Such pragmas
+ are called <em>positioned pragmas</em>. In positioned pragmas that
+ apply to data members, the <code>member</code> qualifier can be
+ omitted for brevity, for example:</p>
+
+ <pre class="cxx">
+ #pragma db id
+ unsigned long id_;
+ </pre>
+
+ <p>Note also that if the C++ declaration immediately following a
+ position pragma is incompatible with the pragma qualifier, an
+ error will be issued. For example:</p>
+
+ <pre class="cxx">
+ #pragma db object // Error: expected class instead of data member.
+ unsigned long id_;
+ </pre>
+
+ <p>While keeping the C++ declarations and database declarations close
+ together eases maintenance and increases readability, we can also
+ place them in different parts of the same header file or even
+ factor them to a separate file. To achieve this we use the so called
+ <em>named pragmas</em>. Unlike positioned pragmas, named pragmas
+ explicitly specify the C++ declaration to which they apply by
+ adding the declaration name after the pragma qualifier. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+ ...
+private:
+ unsigned long id_;
+ ...
+};
+
+#pragma db object(person)
+#pragma db member(person::id_) id
+ </pre>
+
+ <p>Note that in the named pragmas for data members the <code>member</code>
+ qualifier is no longer optional. The C++ declaration name in the
+ named pragmas is resolved using the standard C++ name resolution
+ rules, for example:</p>
+
+ <pre class="cxx">
+namespace db
+{
+ class person
+ {
+ ...
+ private:
+ unsigned long id_;
+ ...
+ };
+}
+
+namespace db
+{
+ #pragma db object(person) // Resolves db::person.
+}
+
+#pragma db member(db::person::id_) id
+ </pre>
+
+ <p>As another example, the following code fragment shows how to use the
+ named value type pragma to map a C++ type to a native database type:</p>
+
+ <pre class="cxx">
+#pragma db value(bool) type("INT")
+
+#pragma db object
+class person
+{
+ ...
+private:
+ bool married_; // Mapped to INT NOT NULL database type.
+ ...
+};
+ </pre>
+
+ <p>If we would like to factor the ODB pragmas into a separate file,
+ we can include this file into the original header file (the one
+ that defines the persistent types) using the <code>#include</code>
+ directive, for example:</p>
+
+ <pre class="cxx">
+// person.hxx
+
+class person
+{
+ ...
+};
+
+#ifdef ODB_COMPILER
+# include "person-pragmas.hxx"
+#endif
+ </pre>
+
+ <p>Alternatively, instead of using the <code>#include</code> directive,
+ we can use the <code>--odb-epilogue</code> option to make the pragmas
+ known to the ODB compiler when compiling the original header file,
+ for example:</p>
+
+ <pre class="terminal">
+--odb-epilogue '#include "person-pragmas.hxx"'
+ </pre>
+
+ <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
+ the ODB compiler to generate the database support code and
+ 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="#14.9">Section 14.9,
+ "C++ Compiler Warnings"</a>.</p>
+
+ <h2><a name="14.1">14.1 Object Type Pragmas</a></h2>
+
+ <p>A pragma with the <code>object</code> qualifier declares a C++ class
+ as a persistent object type. The qualifier can be optionally followed,
+ in any order, by one or more specifiers summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table name for a persistent class</td>
+ <td><a href="#14.1.1">14.1.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>pointer</code></td>
+ <td>pointer type for a persistent class</td>
+ <td><a href="#14.1.2">14.1.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>abstract</code></td>
+ <td>persistent class is abstract</td>
+ <td><a href="#14.1.3">14.1.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>readonly</code></td>
+ <td>persistent class is read-only</td>
+ <td><a href="#14.1.4">14.1.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>optimistic</code></td>
+ <td>persistent class with the optimistic concurrency model</td>
+ <td><a href="#14.1.5">14.1.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>no_id</code></td>
+ <td>persistent class has no object id</td>
+ <td><a href="#14.1.6">14.1.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>callback</code></td>
+ <td>database operations callback</td>
+ <td><a href="#14.1.7">14.1.7</a></td>
+ </tr>
+
+ <tr>
+ <td><code>schema</code></td>
+ <td>database schema for a persistent class</td>
+ <td><a href="#14.1.8">14.1.8</a></td>
+ </tr>
+
+ <tr>
+ <td><code>polymorphic</code></td>
+ <td>persistent class is polymorphic</td>
+ <td><a href="#14.1.9">14.1.9</a></td>
+ </tr>
+
+ <tr>
+ <td><code>session</code></td>
+ <td>enable/disable session support for a persistent class</td>
+ <td><a href="#14.1.10">14.1.10</a></td>
+ </tr>
+
+ <tr>
+ <td><code>definition</code></td>
+ <td>definition location for a persistent class</td>
+ <td><a href="#14.1.11">14.1.11</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>all non-virtual data members in a persistent class are transient</td>
+ <td><a href="#14.1.12">14.1.12</a></td>
+ </tr>
+
+ <tr>
+ <td><code>sectionable</code></td>
+ <td>support addition of new sections in derived classes</td>
+ <td><a href="#14.1.13">14.1.13</a></td>
+ </tr>
+
+ <tr>
+ <td><code>deleted</code></td>
+ <td>persistent class is soft-deleted</td>
+ <td><a href="#14.1.14">14.1.14</a></td>
+ </tr>
+
+ <tr>
+ <td><code>bulk</code></td>
+ <td>enable bulk operations for a persistent class</td>
+ <td><a href="#14.1.15">14.1.15</a></td>
+ </tr>
+
+ <tr>
+ <td><code>options</code></td>
+ <td>database options for a persistent class</td>
+ <td><a href="#14.1.16">14.1.16</a></td>
+ </tr>
+
+ </table>
+
+ <h3><a name="14.1.1">14.1.1 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies the table name that should
+ be used to store objects of the persistent class in a relational
+ database. For example:</p>
+
+ <pre class="cxx">
+#pragma db object table("people")
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>If the table name is not specified, the class name is used as the
+ table name. The table name can be qualified with a database
+ schema, for example:</p>
+
+ <pre class="cxx">
+#pragma db object table("census.people")
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>For more information on database schemas and the format of the
+ qualified names, refer to <a href="#14.1.8">Section 14.1.8,
+ "<code>schema</code>"</a>.</p>
+
+ <h3><a name="14.1.2">14.1.2 <code>pointer</code></a></h3>
+
+ <p>The <code>pointer</code> specifier specifies the object pointer type
+ for the persistent class. The object pointer type is used to return,
+ pass, and cache dynamically allocated instances of a persistent
+ class. For example:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::tr1::shared_ptr&lt;person>)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>There are several ways to specify an object pointer with the
+ <code>pointer</code> specifier. We can use a complete pointer
+ type as shown in the example above. Alternatively, we can
+ specify only the template name of a smart pointer in which
+ case the ODB compiler will automatically append the class
+ name as a template argument. The following example is therefore
+ equivalent to the one above:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::tr1::shared_ptr)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>If you would like to use the raw pointer as an object pointer,
+ you can use <code>*</code> as a shortcut:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(*) // Same as pointer(person*)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>If a pointer type is not explicitly specified, the default pointer,
+ specified at the namespace level (<a href="#14.5.1">Section 14.5.1,
+ "<code>pointer</code>"</a>) or with the <code>--default-pointer</code>
+ ODB compiler option, is used. If neither of these two mechanisms is
+ used to specify the pointer, then the raw pointer is used by default.</p>
+
+ <p>For a more detailed discussion of object pointers, refer to
+ <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
+
+ <h3><a name="14.1.3">14.1.3 <code>abstract</code></a></h3>
+
+ <p>The <code>abstract</code> specifier specifies that the persistent class
+ is abstract. An instance of an abstract class cannot be stored in
+ the database and is normally used as a base for other persistent
+ classes. For example:</p>
+
+ <pre class="cxx">
+#pragma db object abstract
+class person
+{
+ ...
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+};
+
+#pragma db object
+class contractor: public person
+{
+ ...
+};
+ </pre>
+
+ <p>Persistent classes with pure virtual functions are automatically
+ treated as abstract by the ODB compiler. For a more detailed
+ discussion of persistent class inheritance, refer to
+ <a href="#8">Chapter 8, "Inheritance"</a>.</p>
+
+ <h3><a name="14.1.4">14.1.4 <code>readonly</code></a></h3>
+
+ <p>The <code>readonly</code> specifier specifies that the persistent class
+ is read-only. The database state of read-only objects cannot be
+ updated. In particular, this means that you cannot call the
+ <code>database::update()</code> function (<a href="#3.10">Section 3.10,
+ "Updating Persistent Objects"</a>) for such objects. For example:</p>
+
+ <pre class="cxx">
+#pragma db object readonly
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>Read-only and read-write objects can derive from each other without
+ any restrictions. When a read-only object derives from a read-write
+ object, the resulting whole object is read-only, including the part
+ corresponding to the read-write base. On the other hand, when a
+ read-write object derives from a read-only object, all the data
+ members that correspond to the read-only base are treated as
+ read-only while the rest is treated as read-write.</p>
+
+ <p>Note that it is also possible to declare individual data members
+ (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
+ as well as composite value types (<a href="#14.3.6">Section 14.3.6,
+ "<code>readonly</code>"</a>) as read-only.</p>
+
+ <h3><a name="14.1.5">14.1.5 <code>optimistic</code></a></h3>
+
+ <p>The <code>optimistic</code> specifier specifies that the persistent class
+ has the optimistic concurrency model. A class with the optimistic
+ concurrency model must also specify the data member that is used to
+ store the object version using the <code>version</code> pragma
+ (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long version_;
+};
+ </pre>
+
+ <p>If a base class has the optimistic concurrency model, then all its derived
+ classes will automatically have the optimistic concurrency model. The
+ current implementation also requires that in any given inheritance
+ hierarchy the object id and the version data members reside in the
+ same class.</p>
+
+ <p>For a more detailed discussion of optimistic concurrency, refer to
+ <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
+
+ <h3><a name="14.1.6">14.1.6 <code>no_id</code></a></h3>
+
+ <p>The <code>no_id</code> specifier specifies that the persistent class
+ has no object id. For example:</p>
+
+ <pre class="cxx">
+#pragma db object no_id
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>A persistent class without an object id has limited functionality.
+ Such a class cannot be loaded with the <code>database::load()</code>
+ or <code>database::find()</code> functions (<a href="#3.9">Section 3.9,
+ "Loading Persistent Objects"</a>), updated with the
+ <code>database::update()</code> function (<a href="#3.10">Section 3.10,
+ "Updating Persistent Objects"</a>), or deleted with the
+ <code>database::erase()</code> function (<a href="#3.11">Section 3.11,
+ "Deleting Persistent Objects"</a>). To load and delete
+ objects without ids you can use the <code>database::query()</code>
+ (<a href="#4">Chapter 4, "Querying the Database"</a>) and
+ <code>database::erase_query()</code> (<a href="#3.11">Section 3.11,
+ "Deleting Persistent Objects"</a>) functions, respectively.
+ There is no way to update such objects except by using native SQL
+ statements (<a href="#3.12">Section 3.12, "Executing Native SQL
+ Statements"</a>).</p>
+
+ <p>Furthermore, persistent classes without object ids cannot have container
+ data members nor can they be used in object relationships. Such objects
+ are not entered into the session object cache
+ (<a href="#11.1">Section 11.1, "Object Cache"</a>) either.</p>
+
+ <p>To declare a persistent class with an object id, use the data member
+ <code>id</code> specifier (<a href="#14.4.1">Section 14.4.1,
+ "<code>id</code>"</a>).</p>
+
+ <h3><a name="14.1.7">14.1.7 <code>callback</code></a></h3>
+
+ <p>The <code>callback</code> specifier specifies the persist class
+ member function that should be called before and after a
+ database operation is performed on an object of this class.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/callback.hxx>
+
+#pragma db object callback(init)
+class person
+{
+ ...
+
+ void
+ init (odb::callback_event, odb::database&amp;);
+};
+ </pre>
+
+ <p>The callback function has the following signature and can be
+ overloaded for constant objects:</p>
+
+ <pre class="cxx">
+void
+name (odb::callback_event, odb::database&amp;);
+
+void
+name (odb::callback_event, odb::database&amp;) const;
+ </pre>
+
+ <p>The first argument to the callback function is the event that
+ triggered this call. The <code>odb::callback_event</code>
+ enum-like type is defined in the <code>&lt;odb/callback.hxx></code>
+ header file and has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ struct callback_event
+ {
+ enum value
+ {
+ pre_persist,
+ post_persist,
+ pre_load,
+ post_load,
+ pre_update,
+ post_update,
+ pre_erase,
+ post_erase
+ };
+
+ callback_event (value v);
+ operator value () const;
+ };
+}
+ </pre>
+
+ <p>The second argument to the callback function is the database
+ on which the operation is about to be performed or has just
+ been performed. A callback function can be inline or virtual.</p>
+
+ <p>The callback function for the <code>*_persist</code>,
+ <code>*_update</code>, and <code>*_erase</code> events is always
+ called on the constant object reference while for the <code>*_load</code>
+ events &mdash; always on the unrestricted reference.</p>
+
+ <p>If only the non-<code>const</code> version of the callback function
+ is provided, then only the <code>*_load</code> events will be delivered.
+ If only the <code>const</code> version is provided, then all the
+ events will be delivered to this function. Finally, if both versions
+ are provided, then the <code>*_load</code> events will be delivered
+ to the non-<code>const</code> version while all others &mdash; to the
+ <code>const</code> version. If you need to modify the object in one
+ of the "<code>const</code>" events, then you can safely cast away
+ <code>const</code>-ness using the <code>const_cast</code> operator if
+ you know that none of the objects will be created const. Alternatively,
+ if you cannot make this assumption, then you can declare the data
+ members you wish to modify as <code>mutable</code>.</p>
+
+ <p>A database operations callback can be used to implement object-specific
+ pre and post initializations, registrations, and cleanups. As an example,
+ the following code fragment outlines an implementation of a
+ <code>person</code> class that maintains the transient <code>age</code>
+ data member in addition to the persistent date of birth. A callback
+ is used to calculate the value of the former from the latter every
+ time a <code>person</code> object is loaded from the database.</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+#include &lt;odb/callback.hxx>
+
+#pragma db object callback(init)
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+
+ date born_;
+
+ #pragma db transient
+ unsigned short age_;
+
+ void
+ init (odb::callback_event e, odb::database&amp;)
+ {
+ switch (e)
+ {
+ case odb::callback_event::post_load:
+ {
+ // Calculate age from the date of birth.
+ ...
+ break;
+ }
+ default:
+ break;
+ }
+ }
+};
+ </pre>
+
+ <h3><a name="14.1.8">14.1.8 <code>schema</code></a></h3>
+
+ <p>The <code>schema</code> specifier specifies a database schema
+ that should be used for the persistent class.</p>
+
+ <p>In relational databases the term schema can refer to two related
+ but ultimately different concepts. Normally it means a collection
+ of tables, indexes, sequences, etc., that are created in the
+ database or the actual DDL statements that create these
+ database objects. Some database implementations support what
+ would be more accurately called a <em>database namespace</em>
+ but is also called a schema. In this sense, a schema is a
+ separate namespace in which tables, indexes, sequences, etc.,
+ can be created. For example, two tables that have the same
+ name can coexist in the same database if they belong to
+ different schemas. In this section when we talk about a
+ schema, we refer to the <em>database namespace</em> meaning
+ of this term. </p>
+
+ <p>When schemas are in use, a database object name is qualified
+ with a schema. For example:</p>
+
+ <pre class="sql">
+CREATE TABLE accounting.employee (...)
+
+SELECT ... FROM accounting.employee WHERE ...
+ </pre>
+
+ <p>In the above example <code>accounting</code> is the schema
+ and the <code>employee</code> table belongs to this
+ schema.</p>
+
+ <p>Not all database implementations support schemas. Some
+ implementation that don't support schemas (for example,
+ MySQL, SQLite) allow the use of the above syntax to specify
+ the database name. Yet others may support several levels
+ of qualification. For example, Microsoft SQL Server has
+ three levels starting with the linked database server,
+ followed by the database, and then followed by
+ the schema:
+ <code>server1.company1.accounting.employee</code>.
+ While the actual meaning of the qualifier in a qualified name
+ vary from one database implementation to another, here we
+ refer to all of them collectively as a schema.</p>
+
+ <p>In ODB, a schema for a table of a persistent class can be
+ specified at the class level, C++ namespace level, or the
+ file level. To assign a schema to a specific persistent class
+ we can use the <code>schema</code> specifier, for example:</p>
+
+ <pre class="cxx">
+#pragma db object schema("accounting")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>If we are also assigning a table name, then we can use
+ a shorter notation by specifying both the schema and
+ the table name in the <code>table</code> specifier:</p>
+
+ <pre class="cxx">
+#pragma db object table("accounting.employee")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>If we want to assign a schema to all the persistent classes
+ in a C++ namespace, then, instead of specifying the schema
+ for each class, we can specify it once at the C++ namespace level.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace schema("accounting")
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>If we want to assign a schema to all the persistent classes in
+ a file, then we can use the <code>--schema</code> ODB compiler
+ option. For example:</p>
+
+ <pre class="terminal">
+odb ... --schema accounting ...
+ </pre>
+
+ <p>An alternative to this approach with the same effect is to
+ assign a schema to the global namespace:</p>
+
+ <pre class="cxx">
+#pragma db namespace() schema("accounting")
+ </pre>
+
+ <p>By default schema qualifications are accumulated starting from
+ the persistent class, continuing with the namespace hierarchy
+ to which this class belongs, and finishing with the schema
+ specified with the <code>--schema</code> option. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace schema("audit_db")
+namespace audit
+{
+ #pragma db namespace schema("accounting")
+ namespace accounting
+ {
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+ }
+}
+ </pre>
+
+ <p>If we compile the above code fragment with the
+ <code>--schema&nbsp;server1</code> option, then the
+ <code>employee</code> table will have the
+ <code>server1.audit_db.accounting.employee</code> qualified
+ name.</p>
+
+ <p>In some situations we may want to prevent such accumulation
+ of the qualifications. To accomplish this we can use the
+ so-called fully-qualified names, which have the empty leading
+ name component. This is analogous to the C++ fully-qualified
+ names in the <code>::accounting::employee</code> form. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace schema("accounting")
+namespace accounting
+{
+ #pragma db object schema(".hr")
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>In the above code fragment, the <code>employee</code> table will
+ have the <code>hr.employee</code> qualified name while the
+ <code>employer</code> &mdash; <code>accounting.employer</code>.
+ Note also that the empty leading name component is a special
+ ODB syntax and is not propagated to the actual database names
+ (using a name like <code>.hr.employee</code> to refer to a table
+ will most likely result in an error).</p>
+
+ <p>Auxiliary database objects for a persistent class, such as indexes,
+ sequences, triggers, etc., are all created in the same schema
+ as the class table. By default, this is also true for the
+ container tables. However, if you need to store a container
+ table in a different schema, then you can provide a qualified
+ name using the <code>table</code> specifier, for example:</p>
+
+ <pre class="cxx">
+#pragma db object table("accounting.employee")
+class employee
+{
+ ...
+
+ #pragma db object table("operations.projects")
+ std::vector&lt;std::string> projects_;
+};
+ </pre>
+
+ <p>The standard syntax for qualified names used in the
+ <code>schema</code> and <code>table</code> specifiers as well
+ as the view <code>column</code> specifier (<a href="#14.4.10">Section
+ 14.4.10, "<code>column</code> (view)"</a>) has the
+ <code>"</code><i>name</i><code>.</code><i>name</i>...<code>"</code>
+ form where, as discussed above, the leading name component
+ can be empty to denote a fully qualified name. This form, however,
+ doesn't work if one of the name components contains periods. To
+ support such cases the alternative form is available:
+ <code>"</code><i>name</i><code>"."</code><i>name</i><code>"</code>...
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object table("accounting_1.2"."employee")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>Finally, to specify an unqualified name that contains periods
+ we can use the following special syntax:</p>
+
+ <pre class="cxx">
+#pragma db object schema(."accounting_1.2") table("employee")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>Table prefixes (<a href="#14.5.2">Section 14.5.2, "<code>table</code>"</a>)
+ can be used as an alternative to database schemas if the target
+ database system does not support schemas.</p>
+
+ <h3><a name="14.1.9">14.1.9 <code>polymorphic</code></a></h3>
+
+ <p>The <code>polymorphic</code> specifier specifies that the persistent
+ class is polymorphic. For more information on polymorphism support,
+ refer to <a href="#8">Chapter 8, "Inheritance"</a>.</p>
+
+ <h3><a name="14.1.10">14.1.10 <code>session</code></a></h3>
+
+ <p>The <code>session</code> specifier specifies whether to enable
+ session support for the persistent class. For example:</p>
+
+ <pre class="cxx">
+#pragma db object session // Enable.
+class person
+{
+ ...
+};
+
+#pragma db object session(true) // Enable.
+class employee
+{
+ ...
+};
+
+#pragma db object session(false) // Disable.
+class employer
+{
+ ...
+};
+ </pre>
+
+ <p>Session support is disabled by default unless the
+ <code>--generate-session</code> ODB compiler option is specified
+ or session support is enabled at the namespace level
+ (<a href="#14.5.4">Section 14.5.4, "<code>session</code>"</a>).
+ For more information on sessions, refer to <a href="#11">Chapter
+ 11, "Session"</a>.</p>
+
+ <h3><a name="14.1.11">14.1.11 <code>definition</code></a></h3>
+
+ <p>The <code>definition</code> specifier specifies an alternative
+ <em>definition location</em> for the persistent class. By
+ default, the ODB compiler generates the database support code for
+ a persistent class when we compile the header file that
+ defines this class. However, if the <code>definition</code>
+ specifier is used, then the ODB compiler will instead generate
+ the database support code when we compile the header file
+ containing this pragma.</p>
+
+ <p>For more information on this functionality, refer to
+ <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
+
+ <h3><a name="14.1.12">14.1.12 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler to
+ treat all non-virtual data members in the persistent class as transient
+ (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
+ This specifier is primarily useful when declaring virtual data
+ members, as discussed in <a href="#14.4.13">Section 14.4.13,
+ "<code>virtual</code>"</a>.</p>
+
+ <h3><a name="14.1.13">14.1.13 <code>sectionable</code></a></h3>
+
+ <p>The <code>sectionable</code> specifier instructs the ODB compiler
+ to generate support for the addition of new object sections in
+ derived classes in a hierarchy with the optimistic concurrency
+ model. For more information on this functionality, refer to
+ <a href="#9.2">Section 9.2, "Sections and Optimistic
+ Concurrency"</a>.</p>
+
+ <h3><a name="14.1.14">14.1.14 <code>deleted</code></a></h3>
+
+ <p>The <code>deleted</code> specifier marks the persistent class as
+ soft-deleted. The single required argument to this specifier is
+ the deletion version. For more information on this functionality,
+ refer to <a href="#13.4">Section 13.4, "Soft Object Model
+ Changes"</a>.</p>
+
+ <h3><a name="14.1.15">14.1.15 <code>bulk</code></a></h3>
+
+ <p>The <code>bulk</code> specifier enables bulk operation support for
+ the persistent class. The single required argument to this specifier
+ is the batch size. For more information on this functionality, refer
+ to <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
+
+ <h3><a name="14.1.16">14.1.16 <code>options</code></a></h3>
+
+ <p>The <code>options</code> specifier specifies additional table
+ definition options that should be used for the persistent class. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object options("PARTITION BY RANGE (age)")
+class person
+{
+ ...
+
+ unsigned short age_;
+};
+ </pre>
+
+ <p>Table definition options for a container table can be specified with
+ the <code>options</code> data member specifier
+ (<a href="#14.4.8">Section 14.4.8, "<code>options</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db options("PARTITION BY RANGE (index)")
+ std::vector&lt;std::string> aliases_;
+};
+ </pre>
+
+
+ <h2><a name="14.2">14.2 View Type Pragmas</a></h2>
+
+ <p>A pragma with the <code>view</code> qualifier declares a C++ class
+ as a view type. The qualifier can be optionally followed,
+ in any order, by one or more specifiers summarized in the
+ table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>object</code></td>
+ <td>object associated with a view</td>
+ <td><a href="#14.2.1">14.2.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table associated with a view</td>
+ <td><a href="#14.2.2">14.2.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>query</code></td>
+ <td>view query condition</td>
+ <td><a href="#14.2.3">14.2.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>pointer</code></td>
+ <td>pointer type for a view</td>
+ <td><a href="#14.2.4">14.2.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>callback</code></td>
+ <td>database operations callback</td>
+ <td><a href="#14.2.5">14.2.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>definition</code></td>
+ <td>definition location for a view</td>
+ <td><a href="#14.2.6">14.2.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>all non-virtual data members in a view are transient</td>
+ <td><a href="#14.2.7">14.2.7</a></td>
+ </tr>
+
+ </table>
+
+ <p>For more information on view types refer to <a href="#10"> Chapter 10,
+ "Views"</a>.</p>
+
+ <h3><a name="14.2.1">14.2.1 <code>object</code></a></h3>
+
+ <p>The <code>object</code> specifier specifies a persistent class
+ that should be associated with the view. For more information
+ on object associations refer to <a href="#10.1">Section 10.1, "Object
+ Views"</a>.</p>
+
+ <h3><a name="14.2.2">14.2.2 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies a database table
+ that should be associated with the view. For more information
+ on table associations refer to <a href="#10.3">Section 10.3, "Table
+ Views"</a>.</p>
+
+ <h3><a name="14.2.3">14.2.3 <code>query</code></a></h3>
+
+ <p>The <code>query</code> specifier specifies a query condition
+ and, optionally, result modifiers for an object or table view
+ or a native SQL query for a native view. An empty <code>query</code>
+ specifier indicates that a native SQL query is provided at runtime.
+ For more information on query conditions refer to
+ <a href="#10.5">Section 10.5, "View Query Conditions"</a>. For
+ more information on native SQL queries, refer to
+ <a href="#10.6">Section 10.6, "Native Views"</a>.</p>
+
+ <h3><a name="14.2.4">14.2.4 <code>pointer</code></a></h3>
+
+ <p>The <code>pointer</code> specifier specifies the view pointer type
+ for the view class. Similar to objects, the view pointer type is used
+ to return dynamically allocated instances of a view class. The
+ semantics of the <code>pointer</code> specifier for a view are the
+ same as those of the <code>pointer</code> specifier for an object
+ (<a href="#14.1.2">Section 14.1.2, "<code>pointer</code>"</a>).</p>
+
+ <h3><a name="14.2.5">14.2.5 <code>callback</code></a></h3>
+
+ <p>The <code>callback</code> specifier specifies the view class
+ member function that should be called before and after an
+ instance of this view class is created as part of the query
+ result iteration. The semantics of the <code>callback</code>
+ specifier for a view are similar to those of the
+ <code>callback</code> specifier for an object
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
+ except that the only events that can trigger a callback
+ call in the case of a view are <code>pre_load</code> and
+ <code>post_load</code>.</p>
+
+ <h3><a name="14.2.6">14.2.6 <code>definition</code></a></h3>
+
+ <p>The <code>definition</code> specifier specifies an alternative
+ <em>definition location</em> for the view class. By
+ default, the ODB compiler generates the database support code for
+ a view class when we compile the header file that
+ defines this class. However, if the <code>definition</code>
+ specifier is used, then the ODB compiler will instead generate
+ the database support code when we compile the header file
+ containing this pragma.</p>
+
+ <p>For more information on this functionality, refer to
+ <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
+
+ <h3><a name="14.2.7">14.2.7 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler
+ to treat all non-virtual data members in the view class as transient
+ (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
+ This specifier is primarily useful when declaring virtual data
+ members, as discussed in <a href="#14.4.13">Section 14.4.13,
+ "<code>virtual</code>"</a>.</p>
+
+ <h2><a name="14.3">14.3 Value Type Pragmas</a></h2>
+
+ <p>A pragma with the <code>value</code> qualifier describes a value
+ type. It can be optionally followed, in any order, by one or more
+ specifiers summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>type</code></td>
+ <td>database type for a value type</td>
+ <td><a href="#14.3.1">14.3.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_type</code></td>
+ <td>database type for a value type when used as an object id</td>
+ <td><a href="#14.3.2">14.3.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>null</code>/<code>not_null</code></td>
+ <td>type can/cannot be <code>NULL</code></td>
+ <td><a href="#14.3.3">14.3.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>default</code></td>
+ <td>default value for a value type</td>
+ <td><a href="#14.3.4">14.3.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>options</code></td>
+ <td>database options for a value type</td>
+ <td><a href="#14.3.5">14.3.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>readonly</code></td>
+ <td>composite value type is read-only</td>
+ <td><a href="#14.3.6">14.3.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>definition</code></td>
+ <td>definition location for a composite value type</td>
+ <td><a href="#14.3.7">14.3.7</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>all non-virtual data members in a composite value are transient</td>
+ <td><a href="#14.3.8">14.3.8</a></td>
+ </tr>
+
+ <tr>
+ <td><code>unordered</code></td>
+ <td>ordered container should be stored unordered</td>
+ <td><a href="#14.3.9">14.3.9</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_type</code></td>
+ <td>database type for a container's index type</td>
+ <td><a href="#14.3.10">14.3.10</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_type</code></td>
+ <td>database type for a container's key type</td>
+ <td><a href="#14.3.11">14.3.11</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_type</code></td>
+ <td>database type for a container's value type</td>
+ <td><a href="#14.3.12">14.3.12</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_null</code>/<code>value_not_null</code></td>
+ <td>container's value can/cannot be <code>NULL</code></td>
+ <td><a href="#14.3.13">14.3.13</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_options</code></td>
+ <td>database options for a container's id column</td>
+ <td><a href="#14.3.14">14.3.14</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_options</code></td>
+ <td>database options for a container's index column</td>
+ <td><a href="#14.3.15">14.3.15</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_options</code></td>
+ <td>database options for a container's key column</td>
+ <td><a href="#14.3.16">14.3.16</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_options</code></td>
+ <td>database options for a container's value column</td>
+ <td><a href="#14.3.17">14.3.17</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_column</code></td>
+ <td>column name for a container's object id</td>
+ <td><a href="#14.3.18">14.3.18</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_column</code></td>
+ <td>column name for a container's index</td>
+ <td><a href="#14.3.19">14.3.19</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_column</code></td>
+ <td>column name for a container's key</td>
+ <td><a href="#14.3.20">14.3.20</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_column</code></td>
+ <td>column name for a container's value</td>
+ <td><a href="#14.3.21">14.3.21</a></td>
+ </tr>
+
+ </table>
+
+ <p>Many of the value type specifiers have corresponding member type
+ specifiers with the same names (<a href="#14.4">Section 14.4,
+ "Data Member Pragmas"</a>). The behavior of such specifiers
+ for members is similar to that for value types. The only difference
+ is the scope. A particular value type specifier applies to all the
+ members of this value type that don't have a pre-member version
+ of the specifier, while the member specifier always applies only
+ to a single member. Also, with a few exceptions, member specifiers
+ take precedence over and override parameters specified with value
+ specifiers.</p>
+
+ <h3><a name="14.3.1">14.3.1 <code>type</code></a></h3>
+
+ <p>The <code>type</code> specifier specifies the native database type
+ that should be used for data members of this type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(bool) type("INT")
+
+#pragma db object
+class person
+{
+ ...
+
+ bool married_; // Mapped to INT NOT NULL database type.
+};
+ </pre>
+
+ <p>The ODB compiler provides the default mapping between common C++
+ types, such as <code>bool</code>, <code>int</code>, and
+ <code>std::string</code> and the database types for each supported
+ database system. For more information on the default mapping,
+ refer to <a href="#II">Part II, "Database Systems"</a>. The
+ <code>null</code> and <code>not_null</code> (<a href="#14.3.3">Section
+ 14.3.3, "<code>null</code>/<code>not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a type.</p>
+
+ <p>In the above example we changed the mapping for the <code>bool</code>
+ type which is now mapped to the <code>INT</code> database type. In
+ this case, the <code>value</code> pragma is all that is necessary
+ since the ODB compiler will be able to figure out how to store
+ a boolean value as an integer in the database. However, there
+ could be situations where the ODB compiler will not know how to
+ handle the conversion between the C++ and database representations
+ of a value. Consider, as an example, a situation where the
+ boolean value is stored in the database as a string:</p>
+
+ <pre class="cxx">
+#pragma db value(bool) type("VARCHAR(5)")
+ </pre>
+
+ <p>The possible database values for the C++ <code>true</code> value could
+ be <code>"true"</code>, or <code>"TRUE"</code>, or <code>"True"</code>.
+ Or, maybe, all of the above could be valid. The ODB compiler has no way
+ of knowing how your application wants to convert <code>bool</code>
+ to a string and back. To support such custom value type mappings,
+ ODB allows you to provide your own database conversion functions
+ by specializing the <code>value_traits</code> class template. The
+ <code>mapping</code> example in the <code>odb-examples</code>
+ package shows how to do this for all the supported database systems.</p>
+
+ <h3><a name="14.3.2">14.3.2 <code>id_type</code></a></h3>
+
+ <p>The <code>id_type</code> specifier specifies the native database type
+ that should be used for data members of this type that are designated as
+ object identifiers (<a href="#14.4.1">Section 14.4.1,
+ "<code>id</code>"</a>). In combination with the <code>type</code>
+ specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>)
+ <code>id_type</code> allows you to map a C++ type differently depending
+ on whether it is used in an ordinary member or an object id. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("TEXT") id_type("VARCHAR(64)")
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ std::string email_; // Mapped to VARCHAR(64) NOT NULL.
+
+ std::string name_; // Mapped to TEXT NOT NULL.
+};
+ </pre>
+
+ <p>Note that there is no corresponding member type specifier for
+ <code>id_type</code> since the desired result can be achieved
+ with just the <code>type</code> specifier, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id type("VARCHAR(128)")
+ std::string email_;
+};
+ </pre>
+
+ <h3><a name="14.3.3">14.3.3 <code>null</code>/<code>not_null</code></a></h3>
+
+ <p>The <code>null</code> and <code>not_null</code> specifiers specify that
+ a value type or object pointer can or cannot be <code>NULL</code>,
+ respectively. By default, value types are assumed not to allow
+ <code>NULL</code> values while object pointers are assumed to
+ allow <code>NULL</code> values. Data members of types that allow
+ <code>NULL</code> values are mapped in a relational database to
+ columns that allow <code>NULL</code> values. For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+typedef shared_ptr&lt;std::string> string_ptr;
+#pragma db value(string_ptr) type("TEXT") null
+
+#pragma db object
+class person
+{
+ ...
+
+ string_ptr name_; // Mapped to TEXT NULL.
+};
+
+typedef shared_ptr&lt;person> person_ptr;
+#pragma db value(person_ptr) not_null
+ </pre>
+
+ <p>The <code>NULL</code> semantics can also be specified on the
+ per-member basis (<a href="#14.4.6">Section 14.4.6,
+ "<code>null</code>/<code>not_null</code>"</a>). If both a type and
+ a member have <code>null</code>/<code>not_null</code> specifiers,
+ then the member specifier takes precedence. If a member specifier
+ relaxes the <code>NULL</code> semantics (that is, if a member has
+ the <code>null</code> specifier and the type has the explicit
+ <code>not_null</code> specifier), then a warning is issued.</p>
+
+ <p>It is also possible to override a previously specified
+ <code>null</code>/<code>not_null</code> specifier. This is
+ primarily useful if a third-party type, for example,
+ one provided by a profile library (<a href="#III">Part III,
+ "Profiles"</a>), allows <code>NULL</code> values but in your
+ object model data members of this type should never be
+ <code>NULL</code>. In this case you can use the <code>not_null</code>
+ specifier to disable <code>NULL</code> values for this type for the
+ entire translation unit. For example:</p>
+
+ <pre class="cxx">
+// By default, null_string allows NULL values.
+//
+#include &lt;null-string.hxx>
+
+// Disable NULL values for all the null_string data members.
+//
+#pragma db value(null_string) not_null
+ </pre>
+
+ <p>For a more detailed discussion of the <code>NULL</code> semantics
+ for values, refer to <a href="#7.3">Section 7.3, "Pointers and
+ <code>NULL</code> Value Semantics"</a>. For a more detailed
+ discussion of the <code>NULL</code> semantics for object pointers,
+ refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
+
+ <h3><a name="14.3.4">14.3.4 <code>default</code></a></h3>
+
+ <p>The <code>default</code> specifier specifies the database default value
+ that should be used for data members of this type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) default("")
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string name_; // Mapped to TEXT NOT NULL DEFAULT ''.
+};
+ </pre>
+
+ <p>The semantics of the <code>default</code> specifier for a value type
+ are similar to those of the <code>default</code> specifier for a
+ data member (<a href="#14.4.7">Section 14.4.7,
+ "<code>default</code>"</a>).</p>
+
+ <h3><a name="14.3.5">14.3.5 <code>options</code></a></h3>
+
+ <p>The <code>options</code> specifier specifies additional column
+ definition options that should be used for data members of this
+ type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) options("COLLATE binary")
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string name_; // Mapped to TEXT NOT NULL COLLATE binary.
+};
+ </pre>
+
+ <p>The semantics of the <code>options</code> specifier for a value type
+ are similar to those of the <code>options</code> specifier for a
+ data member (<a href="#14.4.8">Section 14.4.8,
+ "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.3.6">14.3.6 <code>readonly</code></a></h3>
+
+ <p>The <code>readonly</code> specifier specifies that the composite
+ value type is read-only. Changes to data members of a read-only
+ composite value type are ignored when updating the database
+ state of an object (<a href="#3.10">Section 3.10, "Updating Persistent
+ Objects"</a>) containing such a value type. Note that this specifier
+ is only valid for composite value types. For example:</p>
+
+ <pre class="cxx">
+#pragma db value readonly
+class person_name
+{
+ ...
+};
+ </pre>
+
+ <p>Read-only and read-write composite values can derive from each other
+ without any restrictions. When a read-only value derives from a
+ read-write value, the resulting whole value is read-only, including
+ the part corresponding to the read-write base. On the other hand, when a
+ read-write value derives from a read-only value, all the data
+ members that correspond to the read-only base are treated as
+ read-only while the rest is treated as read-write.</p>
+
+ <p>Note that it is also possible to declare individual data members
+ (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
+ as well as whole objects (<a href="#14.1.4">Section 14.1.4,
+ "<code>readonly</code>"</a>) as read-only.</p>
+
+ <h3><a name="14.3.7">14.3.7 <code>definition</code></a></h3>
+
+ <p>The <code>definition</code> specifier specifies an alternative
+ <em>definition location</em> for the composite value type. By
+ default, the ODB compiler generates the database support code for
+ a composite value type when we compile the header file that
+ defines this value type. However, if the <code>definition</code>
+ specifier is used, then the ODB compiler will instead generate
+ the database support code when we compile the header file containing
+ this pragma.</p>
+
+ <p>This mechanism is primarily useful for converting third-party
+ types to ODB composite value types. In such cases we normally
+ cannot modify the header files to add the necessary pragmas.
+ It is also often inconvenient to compile these header files
+ with the ODB compiler. With the <code>definition</code>
+ specifier we can create a <em>wrapper header</em> that contains
+ the necessary pragmas and instructs the ODB compiler to generate
+ the database support code for a third-party type when we compile
+ the wrapper header. As an example, consider <code>struct timeval</code>
+ that is defined in the <code>&lt;sys/time.h></code> system header.
+ This type has the following (or similar) definition:</p>
+
+ <pre class="cxx">
+struct timeval
+{
+ long tv_sec;
+ long tv_usec;
+};
+ </pre>
+
+ <p>If we would like to make this type an ODB composite value type,
+ then we can create a wrapper header, for example
+ <code>time-mapping.hxx</code>, with the following content:</p>
+
+ <pre class="cxx">
+#ifndef TIME_MAPPING_HXX
+#define TIME_MAPPING_HXX
+
+#include &lt;sys/time.h>
+
+#pragma db value(timeval) definition
+#pragma db member(timeval::tv_sec) column("sec")
+#pragma db member(timeval::tv_usec) column("usec")
+
+#endif // TIME_MAPPING_HXX
+ </pre>
+
+ <p>If we now compile this header with the ODB compiler, the
+ resulting <code>time-mapping-odb.?xx</code> files will
+ contain the database support code for <code>struct timeval</code>.
+ To use <code>timeval</code> in our persistent classes, we simply
+ include the <code>time-mapping.hxx</code> header:</p>
+
+ <pre class="cxx">
+#include &lt;sys/time.h>
+#include "time-mapping.hxx"
+
+#pragma db object
+class object
+{
+ timeval timestamp;
+};
+ </pre>
+
+ <h3><a name="14.3.8">14.3.8 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler
+ to treat all non-virtual data members in the composite value type
+ as transient (<a href="#14.4.1">Section 14.4.1,
+ "<code>transient</code>"</a>). This specifier is primarily useful
+ when declaring virtual data members, as discussed in
+ <a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>.</p>
+
+ <h3><a name="14.3.9">14.3.9 <code>unordered</code></a></h3>
+
+ <p>The <code>unordered</code> specifier specifies that the ordered
+ container should be stored unordered in the database. The database
+ table for such a container will not contain the index column
+ and the order in which elements are retrieved from the database may
+ not be the same as the order in which they were stored. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) unordered
+ </pre>
+
+ <p>For a more detailed discussion of ordered containers and their
+ storage in the database, refer to <a href="#5.1">Section 5.1,
+ "Ordered Containers"</a>.</p>
+
+ <h3><a name="14.3.10">14.3.10 <code>index_type</code></a></h3>
+
+ <p>The <code>index_type</code> specifier specifies the native
+ database type that should be used for the ordered container's
+ index column. The semantics of <code>index_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). The native
+ database type is expected to be an integer type. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) index_type("SMALLINT UNSIGNED")
+ </pre>
+
+ <h3><a name="14.3.11">14.3.11 <code>key_type</code></a></h3>
+
+ <p>The <code>key_type</code> specifier specifies the native
+ database type that should be used for the map container's
+ key column. The semantics of <code>key_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;unsigned short, float> age_weight_map;
+#pragma db value(age_weight_map) key_type("INT UNSIGNED")
+ </pre>
+
+ <h3><a name="14.3.12">14.3.12 <code>value_type</code></a></h3>
+
+ <p>The <code>value_type</code> specifier specifies the native
+ database type that should be used for the container's
+ value column. The semantics of <code>value_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) value_type("VARCHAR(255)")
+ </pre>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code>
+ (<a href="#14.3.13">Section 14.3.13,
+ "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a value
+ column.</p>
+
+ <h3><a name="14.3.13">14.3.13 <code>value_null</code>/<code>value_not_null</code></a></h3>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
+ specify that the container type's element value can or cannot be
+ <code>NULL</code>, respectively. The semantics of <code>value_null</code>
+ and <code>value_not_null</code> are similar to those of the
+ <code>null</code> and <code>not_null</code> specifiers
+ (<a href="#14.3.3">Section 14.3.3, "<code>null</code>/<code>not_null</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+#pragma db object
+class account
+{
+ ...
+};
+
+typedef std::vector&lt;shared_ptr&lt;account> > accounts;
+#pragma db value(accounts) value_not_null
+ </pre>
+
+ <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
+ Multiset Containers"</a>) the element value is automatically treated
+ as not allowing a <code>NULL</code> value.</p>
+
+
+ <h3><a name="14.3.14">14.3.14 <code>id_options</code></a></h3>
+
+ <p>The <code>id_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ id column. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> nicknames;
+#pragma db value(nicknames) id_options("COLLATE binary")
+ </pre>
+
+ <p>The semantics of the <code>id_options</code> specifier for a container
+ type are similar to those of the <code>id_options</code> specifier for
+ a container data member (<a href="#14.4.29">Section 14.4.29,
+ "<code>id_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.15">14.3.15 <code>index_options</code></a></h3>
+
+ <p>The <code>index_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ index column. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> nicknames;
+#pragma db value(nicknames) index_options("ZEROFILL")
+ </pre>
+
+ <p>The semantics of the <code>index_options</code> specifier for a container
+ type are similar to those of the <code>index_options</code> specifier for
+ a container data member (<a href="#14.4.30">Section 14.4.30,
+ "<code>index_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.16">14.3.16 <code>key_options</code></a></h3>
+
+ <p>The <code>key_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ key column. For example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;std::string, std::string> properties;
+#pragma db value(properties) key_options("COLLATE binary")
+ </pre>
+
+ <p>The semantics of the <code>key_options</code> specifier for a container
+ type are similar to those of the <code>key_options</code> specifier for
+ a container data member (<a href="#14.4.31">Section 14.4.31,
+ "<code>key_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.17">14.3.17 <code>value_options</code></a></h3>
+
+ <p>The <code>value_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ value column. For example:</p>
+
+ <pre class="cxx">
+typedef std::set&lt;std::string> nicknames;
+#pragma db value(nicknames) value_options("COLLATE binary")
+ </pre>
+
+ <p>The semantics of the <code>value_options</code> specifier for a container
+ type are similar to those of the <code>value_options</code> specifier for
+ a container data member (<a href="#14.4.32">Section 14.4.32,
+ "<code>value_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.18">14.3.18 <code>id_column</code></a></h3>
+
+ <p>The <code>id_column</code> specifier specifies the column
+ name that should be used to store the object id in the
+ container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) id_column("id")
+ </pre>
+
+ <p>If the column name is not specified, then <code>object_id</code>
+ is used by default.</p>
+
+ <h3><a name="14.3.19">14.3.19 <code>index_column</code></a></h3>
+
+ <p>The <code>index_column</code> specifier specifies the column
+ name that should be used to store the element index in the
+ ordered container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) index_column("name_number")
+ </pre>
+
+ <p>If the column name is not specified, then <code>index</code>
+ is used by default.</p>
+
+ <h3><a name="14.3.20">14.3.20 <code>key_column</code></a></h3>
+
+ <p>The <code>key_column</code> specifier specifies the column
+ name that should be used to store the key in the map
+ container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;unsigned short, float> age_weight_map;
+#pragma db value(age_weight_map) key_column("age")
+ </pre>
+
+ <p>If the column name is not specified, then <code>key</code>
+ is used by default.</p>
+
+ <h3><a name="14.3.21">14.3.21 <code>value_column</code></a></h3>
+
+ <p>The <code>value_column</code> specifier specifies the column
+ name that should be used to store the element value in the
+ container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;unsigned short, float> age_weight_map;
+#pragma db value(age_weight_map) value_column("weight")
+ </pre>
+
+ <p>If the column name is not specified, then <code>value</code>
+ is used by default.</p>
+
+ <!-- Data Member Pragmas -->
+
+
+ <h2><a name="14.4">14.4 Data Member Pragmas</a></h2>
+
+ <p>A pragma with the <code>member</code> qualifier or a positioned
+ pragma without a qualifier describes a data member. It can
+ be optionally followed, in any order, by one or more specifiers
+ summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>id</code></td>
+ <td>member is an object id</td>
+ <td><a href="#14.4.1">14.4.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>auto</code></td>
+ <td>id is assigned by the database</td>
+ <td><a href="#14.4.2">14.4.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>type</code></td>
+ <td>database type for a member</td>
+ <td><a href="#14.4.3">14.4.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_type</code></td>
+ <td>database type for a member when used as an object id</td>
+ <td><a href="#14.4.4">14.4.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>get</code>/<code>set</code>/<code>access</code></td>
+ <td>member accessor/modifier expressions</td>
+ <td><a href="#14.4.5">14.4.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>null</code>/<code>not_null</code></td>
+ <td>member can/cannot be <code>NULL</code></td>
+ <td><a href="#14.4.6">14.4.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>default</code></td>
+ <td>default value for a member</td>
+ <td><a href="#14.4.7">14.4.7</a></td>
+ </tr>
+
+ <tr>
+ <td><code>options</code></td>
+ <td>database options for a member</td>
+ <td><a href="#14.4.8">14.4.8</a></td>
+ </tr>
+
+ <tr>
+ <td><code>column</code></td>
+ <td>column name for a member of an object or composite value</td>
+ <td><a href="#14.4.9">14.4.9</a></td>
+ </tr>
+
+ <tr>
+ <td><code>column</code></td>
+ <td>column name for a member of a view</td>
+ <td><a href="#14.4.10">14.4.10</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>member is not stored in the database</td>
+ <td><a href="#14.4.11">14.4.11</a></td>
+ </tr>
+
+ <tr>
+ <td><code>readonly</code></td>
+ <td>member is read-only</td>
+ <td><a href="#14.4.12">14.4.12</a></td>
+ </tr>
+
+ <tr>
+ <td><code>virtual</code></td>
+ <td>declare a virtual data member</td>
+ <td><a href="#14.4.13">14.4.13</a></td>
+ </tr>
+
+ <tr>
+ <td><code>inverse</code></td>
+ <td>member is an inverse side of a bidirectional relationship</td>
+ <td><a href="#14.4.14">14.4.14</a></td>
+ </tr>
+
+ <tr>
+ <td><code>on_delete</code></td>
+ <td><code>ON DELETE</code> clause for object pointer member</td>
+ <td><a href="#14.4.15">14.4.15</a></td>
+ </tr>
+
+ <tr>
+ <td><code>version</code></td>
+ <td>member stores object version</td>
+ <td><a href="#14.4.16">14.4.16</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index</code></td>
+ <td>define database index for a member</td>
+ <td><a href="#14.4.17">14.4.17</a></td>
+ </tr>
+
+ <tr>
+ <td><code>unique</code></td>
+ <td>define unique database index for a member</td>
+ <td><a href="#14.4.18">14.4.18</a></td>
+ </tr>
+
+ <tr>
+ <td><code>unordered</code></td>
+ <td>ordered container should be stored unordered</td>
+ <td><a href="#14.4.19">14.4.19</a></td>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table name for a container</td>
+ <td><a href="#14.4.20">14.4.20</a></td>
+ </tr>
+
+ <tr>
+ <td><code>load</code>/<code>update</code></td>
+ <td>loading/updating behavior for a section</td>
+ <td><a href="#14.4.21">14.4.21</a></td>
+ </tr>
+
+ <tr>
+ <td><code>section</code></td>
+ <td>member belongs to a section</td>
+ <td><a href="#14.4.22">14.4.22</a></td>
+ </tr>
+
+ <tr>
+ <td><code>added</code></td>
+ <td>member is soft-added</td>
+ <td><a href="#14.4.23">14.4.23</a></td>
+ </tr>
+
+ <tr>
+ <td><code>deleted</code></td>
+ <td>member is soft-deleted</td>
+ <td><a href="#14.4.24">14.4.24</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_type</code></td>
+ <td>database type for a container's index type</td>
+ <td><a href="#14.4.25">14.4.25</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_type</code></td>
+ <td>database type for a container's key type</td>
+ <td><a href="#14.4.26">14.4.26</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_type</code></td>
+ <td>database type for a container's value type</td>
+ <td><a href="#14.4.27">14.4.27</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_null</code>/<code>value_not_null</code></td>
+ <td>container's value can/cannot be <code>NULL</code></td>
+ <td><a href="#14.4.28">14.4.28</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_options</code></td>
+ <td>database options for a container's id column</td>
+ <td><a href="#14.4.29">14.4.29</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_options</code></td>
+ <td>database options for a container's index column</td>
+ <td><a href="#14.4.30">14.4.30</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_options</code></td>
+ <td>database options for a container's key column</td>
+ <td><a href="#14.4.31">14.4.31</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_options</code></td>
+ <td>database options for a container's value column</td>
+ <td><a href="#14.4.32">14.4.32</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_column</code></td>
+ <td>column name for a container's object id</td>
+ <td><a href="#14.4.33">14.4.33</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_column</code></td>
+ <td>column name for a container's index</td>
+ <td><a href="#14.4.34">14.4.34</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_column</code></td>
+ <td>column name for a container's key</td>
+ <td><a href="#14.4.35">14.4.35</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_column</code></td>
+ <td>column name for a container's value</td>
+ <td><a href="#14.4.36">14.4.36</a></td>
+ </tr>
+
+ </table>
+
+ <p>Many of the member specifiers have corresponding value type
+ specifiers with the same names (<a href="#14.3">Section 14.3,
+ "Value Type Pragmas"</a>). The behavior of such specifiers
+ for members is similar to that for value types. The only difference
+ is the scope. A particular value type specifier applies to all the
+ members of this value type that don't have a pre-member version
+ of the specifier, while the member specifier always applies only
+ to a single member. Also, with a few exceptions, member specifiers
+ take precedence over and override parameters specified with value
+ specifiers.</p>
+
+ <h3><a name="14.4.1">14.4.1 <code>id</code></a></h3>
+
+ <p>The <code>id</code> specifier specifies that the data member contains
+ the object id. In a relational database, an identifier member is
+ mapped to a primary key. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ std::string email_;
+};
+ </pre>
+
+ <p>Normally, every persistent class has a data member designated as an
+ object's identifier. However, it is possible to declare a
+ persistent class without an id using the object <code>no_id</code>
+ specifier (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
+
+ <p>Note also that the <code>id</code> specifier cannot be used for data
+ members of composite value types or views.</p>
+
+ <h3><a name="14.4.2">14.4.2 <code>auto</code></a></h3>
+
+ <p>The <code>auto</code> specifier specifies that the object's identifier
+ is automatically assigned by the database. Only a member that was
+ designated as an object id can have this specifier. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+};
+ </pre>
+
+ <p>Note that automatically-assigned object ids are not reused.
+ If you have a high object turnover (that is, objects are routinely
+ made persistent and then erased), then care must be taken not to
+ run out of object ids. In such situations, using
+ <code>unsigned&nbsp;long&nbsp;long</code> as the identifier type
+ is a safe choice.</p>
+
+ <p>For additional information on the automatic identifier assignment,
+ refer to <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>.</p>
+
+ <p>Note also that the <code>auto</code> specifier cannot be specified
+ for data members of composite value types or views.</p>
+
+ <h3><a name="14.4.3">14.4.3 <code>type</code></a></h3>
+
+ <p>The <code>type</code> specifier specifies the native database type
+ that should be used for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db type("INT")
+ bool married_;
+};
+ </pre>
+
+ <p>The <code>null</code> and <code>not_null</code> (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a data member.
+ It is also possible to specify the database type on the per-type instead
+ of the per-member basis using the value <code>type</code>
+ specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
+
+ <h3><a name="14.4.4">14.4.4 <code>id_type</code></a></h3>
+
+ <p>The <code>id_type</code> specifier specifies the native database type
+ that should be used for the data member when it is part of an
+ object identifier. This specifier only makes sense when applied to
+ a member of a composite value type that is used for both id and
+ non-id members. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+ std::string first_;
+
+ #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ name name_; // name_.first_, name_.last_ mapped to VARCHAR(64)
+
+ name alias_; // alias_.first_, alias_.last_ mapped to VARCHAR(256)
+};
+ </pre>
+
+ <h3><a name="14.4.5">14.4.5 <code>get</code>/<code>set</code>/<code>access</code></a></h3>
+
+ <p>The <code>get</code> and <code>set</code> specifiers specify the
+ data member accessor and modifier expressions, respectively. If
+ provided, the generated database support code will use these
+ expressions to access and modify the data member when performing
+ database operations. The <code>access</code> specifier can be used
+ as a shortcut to specify both the accessor and modifier if they
+ happen to be the same.</p>
+
+ <p>In its simplest form the accessor or modifier expression can be
+ just a name. Such a name should resolve either to another data
+ member of the same type or to a suitable accessor or modifier
+ member function. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+public:
+ const std::string&amp; name () const;
+ void name (const std::string&amp;);
+private:
+ #pragma db access(name)
+ std::string name_;
+};
+ </pre>
+
+ <p>A suitable accessor function is a <code>const</code> member function
+ that takes no arguments and whose return value can be implicitly
+ converted to the <code>const</code> reference to the member type
+ (<code>const&nbsp;std::string&amp;</code> in the example above).
+ An accessor function that returns a <code>const</code> reference
+ to the data member is called <em>by-reference accessor</em>.
+ Otherwise, it is called <em>by-value accessor</em>.</p>
+
+ <p>A suitable modifier function can be of two forms. It can be the
+ so called <em>by-reference modifier</em> which is a member function
+ that takes no arguments and returns a non-<code>const</code> reference
+ to the data member (<code>std::string&amp;</code> in the example above).
+ Alternatively, it can be the so called <em>by-value modifier</em> which
+ is a member function taking a single argument &mdash; the new value
+ &mdash; that can be implicitly initialized from a variable of the member
+ type (<code>std::string</code> in the example above). The return value
+ of a by-value modifier, if any, is ignored. If both by-reference and
+ by-value modifiers are available, then ODB prefers the by-reference
+ version since it is more efficient. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+public:
+ std::string get_name () const; // By-value accessor.
+ std::string&amp; set_name (); // By-reference modifier.
+ void set_name (std::string const&amp;); // By-value modifier.
+private:
+ #pragma db get(get_name) \ // Uses by-value accessor.
+ set(set_name) // Uses by-reference modifier.
+ std::string name_;
+};
+ </pre>
+
+ <p>Note that in many cases it is not necessary to specify accessor and
+ modifier functions explicitly since the ODB compiler will try to
+ discover them automatically in case the data member will be inaccessible
+ to the generated code. In particular, in both of the above examples
+ the ODB compiler would have successfully discovered the necessary
+ functions. For more information on this functionality, refer to
+ <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
+ Values"</a>.</p>
+
+ <p>Note also that by-value accessors and by-value modifiers cannot be
+ used for certain data members in certain situations. These limitations
+ are discussed in more detail later in this section.</p>
+
+ <p>Accessor and modifier expressions can be more elaborate than simple
+ names. An accessor expression is any C++ expression that can be
+ used to initialize a <code>const</code> reference to the member
+ type. Similar to accessor functions, which are just a special case
+ of accessor expressions, an accessor expression that evaluates to a
+ <code>const</code> reference to the data member is called
+ <em>by-reference accessor expression</em>. Otherwise, it is
+ called <em>by-value accessor expression</em>.</p>
+
+ <p>Modifier expressions can also be of two forms: <em>by-reference
+ modifier expression</em> and <em>by-value modifier expression</em>
+ (again, modifier functions are just a special case of modifier
+ expressions). A by-reference modifier expression is any C++
+ expression that evaluates to the non-<code>const</code> reference
+ to the member type. A by-value modifier expression can be a
+ single or multiple (separated by semicolon) C++ statements
+ with the effect of setting the new member value.</p>
+
+ <p>There are two special placeholders that are recognized by the
+ ODB compiler in accessor and modifier expressions. The first
+ is the <code>this</code> keyword which denotes a reference
+ (note: not a pointer) to the persistent object. In accessor
+ expressions this reference is <code>const</code> while in
+ modifier expressions it is non-<code>const</code>. If an
+ expression does not contain the <code>this</code> placeholder,
+ then the ODB compiler automatically prefixes it with <code>this.</code>
+ sequence.</p>
+
+ <p>The second placeholder, the <code>(?)</code> sequence, is used
+ to denote the new value in by-value modifier expressions. The
+ ODB compiler replaces the question mark with the variable name,
+ keeping the surrounding parenthesis. The following example shows
+ a few more interesting accessor and modifier expressions:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct point
+{
+ point (int, int);
+
+ int x;
+ int y;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ public:
+ const char* name () const;
+ void name (const char*);
+ private:
+ #pragma db get(std::string (this.name ())) \
+ set(name ((?).c_str ())) // The same as this.name (...).
+ std::string name_;
+
+ public:
+ const std::unique_ptr&lt;account>&amp; acc () const;
+ void acc (std::unique_ptr&lt;account>);
+ private:
+ #pragma db set(acc (std::move (?)))
+ std::unique_ptr&lt;account> acc_;
+
+ public:
+ int loc_x () const
+ int loc_y () const
+ void loc_x (int);
+ void loc_y (int);
+ private:
+ #pragma db get(point (this.loc_x (), this.loc_y ())) \
+ set(this.loc_x ((?).x); this.loc_y ((?).y))
+ point loc_;
+};
+ </pre>
+
+ <p>When the data member is of an array type, then the terms "reference"
+ and "member type" in the above discussion should be replaced with
+ "pointer" and "array element type", respectively. That is, the accessor
+ expression for an array member is any C++ expression that can be
+ used to initialize a <code>const</code> pointer to the array
+ element type, and so on. The following example shows common
+ accessor and modifier signatures for array members:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ public:
+ const char* id () const; // By-reference accessor.
+ void id (const char*); // By-value modifier.
+ private:
+ char id_[16];
+
+ public:
+ const char* pub_key () const; // By-reference accessor.
+ char* pub_key (); // By-reference modifier.
+ private:
+ char pub_key_[2048];
+};
+ </pre>
+
+ <p>Accessor and modifier expressions can be used with data members
+ of simple value, composite value, container, and object pointer
+ types. They can be used for data members in persistent classes,
+ composite value types, and views. There is also a mechanism
+ related to accessors and modifiers called virtual data members
+ and which is discussed in <a href="#14.4.13">Section 14.4.13,
+ "<code>virtual</code>"</a>.</p>
+
+ <p>There are, however, certain limitations when it comes to using
+ by-value accessor and modifier expressions. First of all, if a
+ by-value modifier is used, then the data member type should be
+ default-constructible. Furthermore, a composite value type that
+ has a container member cannot be modified with a by-value modifier.
+ Only a by-reference modifier expression can be used. The ODB
+ compiler will detect such cases and issue diagnostics. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct name
+{
+ std::string first_;
+ std::string last_;
+ std::vector&lt;std::string> aliases_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+public:
+ const name&amp; name () const;
+ void name (const name&amp;);
+private:
+ #pragma db access(name) // Error: by-value modifier.
+ name name_;
+};
+ </pre>
+
+ <p>In certain database systems it is also not possible to use by-value
+ accessor and modifier expression with certain database types.
+ The ODB compiler is only able to detect such cases and issue diagnostics
+ if you specified accessor/modifier function names as opposed to custom
+ expressions. For more information on these database and type-specific
+ limitations, refer to the "Limitations" sections in <a href="#II">Part
+ II, "Database Systems"</a>.</p>
+
+ <h3><a name="14.4.6">14.4.6 <code>null</code>/<code>not_null</code></a></h3>
+
+ <p>The <code>null</code> and <code>not_null</code> specifiers specify that
+ the data member can or cannot be <code>NULL</code>, respectively.
+ By default, data members of basic value types for which database
+ mapping is provided by the ODB compiler do not allow <code>NULL</code>
+ values while data members of object pointers allow <code>NULL</code>
+ values. Other value types, such as those provided by the profile
+ libraries (<a href="#III">Part III, "Profiles"</a>), may or may
+ not allow <code>NULL</code> values, depending on the semantics
+ of each value type. Consult the relevant documentation to find
+ out more about the <code>NULL</code> semantics for such value
+ types. A data member containing the object id (<a href="#14.4.1">Section
+ 14.4.1, "<code>id</code>"</a>) is automatically treated as not
+ allowing a <code>NULL</code> value. Data members that
+ allow <code>NULL</code> values are mapped in a relational database
+ to columns that allow <code>NULL</code> values. For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db null
+ std::string name_;
+};
+
+#pragma db object
+class account
+{
+ ...
+
+ #pragma db not_null
+ shared_ptr&lt;person> holder_;
+};
+ </pre>
+
+ <p>The <code>NULL</code> semantics can also be specified on the
+ per-type basis (<a href="#14.3.3">Section 14.3.3,
+ "<code>null</code>/<code>not_null</code>"</a>). If both a type and
+ a member have <code>null</code>/<code>not_null</code> specifiers,
+ then the member specifier takes precedence. If a member specifier
+ relaxes the <code>NULL</code> semantics (that is, if a member has
+ the <code>null</code> specifier and the type has the explicit
+ <code>not_null</code> specifier), then a warning is issued.</p>
+
+ <p>For a more detailed discussion of the <code>NULL</code> semantics
+ for values, refer to <a href="#7.3">Section 7.3, "Pointers and
+ <code>NULL</code> Value Semantics"</a>. For a more detailed
+ discussion of the <code>NULL</code> semantics for object pointers,
+ refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
+
+ <h3><a name="14.4.7">14.4.7 <code>default</code></a></h3>
+
+ <p>The <code>default</code> specifier specifies the database default value
+ that should be used for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default(-1)
+ int age_; // Mapped to INT NOT NULL DEFAULT -1.
+};
+ </pre>
+
+ <p>A default value can be the special <code>null</code> keyword,
+ a <code>bool</code> literal (<code>true</code> or <code>false</code>),
+ an integer literal, a floating point literal, a string literal, or
+ an enumerator name. If you need to specify a default value that is
+ an expression, for example an SQL function call, then you can use
+ the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>) instead. For example:</p>
+
+ <pre class="cxx">
+enum gender {male, female, undisclosed};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default(null)
+ odb::nullable&lt;std::string> middle_; // DEFAULT NULL
+
+ #pragma db default(false)
+ bool married_; // DEFAULT 0/FALSE
+
+ #pragma db default(0.0)
+ float weight_; // DEFAULT 0.0
+
+ #pragma db default("Mr")
+ string title_; // DEFAULT 'Mr'
+
+ #pragma db default(undisclosed)
+ gender gender_; // DEFAULT 2/'undisclosed'
+
+ #pragma db options("DEFAULT CURRENT_TIMESTAMP()")
+ date timestamp_; // DEFAULT CURRENT_TIMESTAMP()
+};
+ </pre>
+
+ <p>Default values specified as enumerators are only supported for
+ members that are mapped to an <code>ENUM</code> or an integer
+ type in the database, which is the case for the automatic
+ mapping of C++ enums and enum classes to suitable database
+ types as performed by the ODB compiler. If you have mapped
+ a C++ enum or enum class to another database type, then you
+ should use a literal corresponding to that type to specify
+ the default value. For example:</p>
+
+ <pre class="cxx">
+enum gender {male, female, undisclosed};
+#pragma db value(gender) type("VARCHAR(11)")
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default("undisclosed")
+ gender gender_; // DEFAULT 'undisclosed'
+};
+ </pre>
+
+ <p>A default value can also be specified on the per-type basis
+ (<a href="#14.3.4">Section 14.3.4, "<code>default</code>"</a>).
+ An empty <code>default</code> specifier can be used to reset
+ a default value that was previously specified on the per-type
+ basis. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) default("")
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default()
+ std::string name_; // No default value.
+};
+ </pre>
+
+ <p>A data member containing the object id (<a href="#14.4.1">Section
+ 14.4.1, "<code>id</code>"</a> ) is automatically treated as not
+ having a default value even if its type specifies a default value.</p>
+
+ <p>Note also that default values do not affect the generated C++ code
+ in any way. In particular, no automatic initialization of data members
+ with their default values is performed at any point. If you need such
+ an initialization, you will need to implement it yourself, for example,
+ in your persistent class constructors. The default values only
+ affect the generated database schemas and, in the context of ODB,
+ are primarily useful for schema evolution.</p>
+
+ <p>Additionally, the <code>default</code> specifier cannot be specified
+ for view data members.</p>
+
+ <h3><a name="14.4.8">14.4.8 <code>options</code></a></h3>
+
+ <p>The <code>options</code> specifier specifies additional column
+ definition options that should be used for the data member. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db options("CHECK(email != '')")
+ std::string email_; // Mapped to TEXT NOT NULL CHECK(email != '').
+};
+ </pre>
+
+ <p>Note that if specified for the container member, then instead of the
+ column definition options it specifies the table definition options for
+ the container table (<a href="#14.1.16">Section 14.1.16,
+ "<code>options</code>"</a>).</p>
+
+ <p>Options can also be specified on the per-type basis
+ (<a href="#14.3.5">Section 14.3.5, "<code>options</code>"</a>).
+ By default, options are accumulating. That is, the ODB compiler
+ first adds all the options specified for a value type followed
+ by all the options specified for a data member. To clear the
+ accumulated options at any point in this sequence you can use
+ an empty <code>options</code> specifier. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) options("COLLATE binary")
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_; // TEXT NOT NULL COLLATE binary
+
+ #pragma db options("CHECK(last != '')")
+ std::string last_; // TEXT NOT NULL COLLATE binary CHECK(last != '')
+
+ #pragma db options()
+ std::string title_; // TEXT NOT NULL
+
+ #pragma db options() options("CHECK(email != '')")
+ std::string email_; // TEXT NOT NULL CHECK(email != '')
+};
+ </pre>
+
+ <p>ODB provides dedicated specifiers for specifying column types
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
+ <code>NULL</code> constraints (<a href="#14.4.6">Section 14.4.6,
+ "<code>null</code>/<code>not_null</code>"</a>), and default
+ values (<a href="#14.4.7">Section 14.4.7, "<code>default</code>"</a>).
+ For ODB to function correctly these specifiers should always be
+ used instead of the opaque <code>options</code> specifier for
+ these components of a column definition.</p>
+
+ <p>Note also that the <code>options</code> specifier cannot be specified
+ for view data members.</p>
+
+ <h3><a name="14.4.9">14.4.9 <code>column</code> (object, composite value)</a></h3>
+
+ <p>The <code>column</code> specifier specifies the column name
+ that should be used to store the data member of a persistent class
+ or composite value type in a relational database. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id column("person_id")
+ unsigned long id_;
+};
+ </pre>
+
+ <p>For a member of a composite value type, the <code>column</code> specifier
+ specifies the column name prefix. Refer to <a href="#7.2.2">Section 7.2.2,
+ "Composite Value Column and Table Names"</a> for details.</p>
+
+ <p>If the column name is not specified, it is derived from the member's
+ so-called public name. A public member name is obtained by removing
+ the common data member name decorations, such as leading and trailing
+ underscores, the <code>m_</code> prefix, etc.</p>
+
+ <h3><a name="14.4.10">14.4.10 <code>column</code> (view)</a></h3>
+
+ <p>The <code>column</code> specifier can be used to specify the associated
+ object data member, the potentially qualified column name, or the column
+ expression for the data member of a view class. For more information,
+ refer to <a href="#10.1">Section 10.1, "Object Views"</a> and
+ <a href="#10.3">Section 10.3, "Table Views"</a>.</p>
+
+ <h3><a name="14.4.11">14.4.11 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler
+ not to store the data member in the database. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ date born_;
+
+ #pragma db transient
+ unsigned short age_; // Computed from born_.
+};
+ </pre>
+
+ <p>This pragma is usually used on computed members, pointers and
+ references that are only meaningful in the application's
+ memory, as well as utility members such as mutexes, etc.</p>
+
+ <h3><a name="14.4.12">14.4.12 <code>readonly</code></a></h3>
+
+ <p>The <code>readonly</code> specifier specifies that the data member of
+ an object or composite value type is read-only. Changes to a read-only
+ data member are ignored when updating the database state of an object
+ (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>)
+ containing such a member. Since views are read-only, it is not
+ necessary to use this specifier for view data members. Object id
+ (<a href="#14.4.1">Section 14.4.1, "<code>id</code>"</a>)
+ and inverse (<a href="#14.4.14">Section 14.4.14,
+ "<code>inverse</code>"</a>) data members are automatically treated
+ as read-only and must not be explicitly declared as such. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db readonly
+ date born_;
+};
+ </pre>
+
+ <p>Besides simple value members, object pointer, container, and composite
+ value members can also be declared read-only. A change of a pointed-to
+ object is ignored when updating the state of a read-only object
+ pointer. Similarly, any changes to the number or order of
+ elements or to the element values themselves are ignored when
+ updating the state of a read-only container. Finally, any changes
+ to the members of a read-only composite value type are also ignored
+ when updating the state of such a composite value.</p>
+
+ <p>ODB automatically treats <code>const</code> data members as read-only.
+ For example, the following <code>person</code> object is equivalent
+ to the above declaration for the database persistence purposes:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ const date born_; // Automatically read-only.
+};
+ </pre>
+
+ <p>When declaring an object pointer <code>const</code>, make sure to
+ declare the pointer as <code>const</code> rather than (or in addition
+ to) the object itself. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ const person* father_; // Read-write pointer to a read-only object.
+ person* const mother_; // Read-only pointer to a read-write object.
+};
+ </pre>
+
+ <p>Note that in case of a wrapper type (<a href="#7.3">Section 7.3,
+ "Pointers and <code>NULL</code> Value Semantics"</a>), both the
+ wrapper and the wrapped type must be <code>const</code> in
+ order for the ODB compiler to automatically treat the data
+ member as read-only. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ const std::auto_ptr&lt;const date> born_;
+};
+ </pre>
+
+ <p>Read-only members are useful when dealing with
+ asynchronous changes to the state of a data member in the
+ database which should not be overwritten. In other cases,
+ where the state of a data member never changes, declaring such a member
+ read-only allows ODB to perform more efficient object updates.
+ In such cases, however, it is conceptually more correct to
+ declare such a data member as <code>const</code> rather than
+ as read-only.</p>
+
+ <p>Note that it is also possible to declare composite value types
+ (<a href="#14.3.6">Section 14.3.6, "<code>readonly</code>"</a>)
+ as well as whole objects (<a href="#14.1.4">Section 14.1.4,
+ "<code>readonly</code>"</a>) as read-only.</p>
+
+ <h3><a name="14.4.13">14.4.13 <code>virtual</code></a></h3>
+
+ <p>The <code>virtual</code> specifier is used to declare a virtual
+ data member in an object, view, or composite value type. A virtual
+ data member is an <em>imaginary</em> data member that is only
+ used for the purpose of database persistence. A virtual data
+ member does not actually exist (that is, occupy space) in the
+ C++ class. Note also that virtual data members have nothing to
+ do with C++ virtual functions or virtual inheritance. Specifically,
+ no virtual function call overhead is incurred when using virtual
+ data members.</p>
+
+ <p>To declare a virtual data member we must specify the data
+ member name using the <code>member</code> specifier. We must
+ also specify the data member type with the <code>virtual</code>
+ specifier. Finally, the virtual data member declaration must
+ also specify the accessor and modifier expressions, unless
+ suitable accessor and modifier functions can automatically be
+ found by the ODB compiler (<a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ // Transient real data member that actually stores the data.
+ //
+ #pragma db transient
+ std::string name_;
+
+ // Virtual data member.
+ //
+ #pragma db member(name) virtual(std::string) access(name_)
+};
+ </pre>
+
+ <p>From the pragma language point of view, a virtual data member
+ behaves exactly like a normal data member. Specifically, we
+ can reference the virtual data member after it has been
+ declared and use positioned pragmas before its declaration.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db transient
+ std::string name_;
+
+ #pragma db access(name_)
+ #pragma db member(name) virtual(std::string)
+};
+
+#pragma db member(person::name) column("person_name")
+#pragma db index member(person::name)
+ </pre>
+
+ <p>We can also declare a virtual data member outside the class
+ scope:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ std::string name_;
+};
+
+#pragma db member(person::name_) transient
+#pragma db member(person::name) virtual(std::string) access(name_)
+ </pre>
+
+ <p>While in the above examples using virtual data members doesn't
+ seem to yield any benefits, this mechanism can be useful in a
+ number of situations. As one example, consider the need to
+ aggregate or dis-aggregate a data member:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db transient
+ std::pair&lt;std::string, std::string> name_;
+
+ #pragma db member(first) virtual(std::string) access(name_.first)
+ #pragma db member(last) virtual(std::string) access(name_.second)
+};
+ </pre>
+
+ <p>We can also use virtual data members to implement composite
+ object ids that are spread over multiple data members:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct name
+{
+ name () {}
+ name (std::string const&amp; f, std::string const&amp; l)
+ : first (f), last(l) {}
+
+ std::string first;
+ std::string last;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db transient
+ std::string first_;
+
+ #pragma db transient
+ std::string last_;
+
+ #pragma db member(name) virtual(name) id \
+ get(::name (this.first_, this.last_)) \
+ set(this.first_ = (?).first; this.last_ = (?).last)
+};
+ </pre>
+
+ <p>Another common situation that calls for virtual data members is
+ a class that uses the pimpl idiom. While the following code
+ fragment outlines the idea, for details refer to the
+ <code>pimpl</code> example in the <code>odb-examples</code>
+ package.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ std::string const&amp; name () const;
+ void name (std::string const&amp;);
+
+ unsigned short age () const;
+ void age (unsigned short);
+
+ ...
+
+private:
+ class impl;
+
+ #pragma db transient
+ impl* pimpl_;
+
+ #pragma db member(name) virtual(std::string) // Uses name().
+ #pragma db member(age) virtual(unsigned short) // Uses age().
+};
+ </pre>
+
+ <p>The above example also shows that names used for virtual data
+ members (<code>name</code> and <code>age</code> in our case) can
+ be the same as the names of accessor/modifier functions. The only
+ names that virtual data members cannot clash with are those of
+ other data members, virtual or real.</p>
+
+ <p>A common pattern in the above examples is the need to
+ declare the real data member that actually stores the
+ data as transient. If all the real data members in a
+ class are treated as transient, then we can use the
+ class-level <code>transient</code> specifier
+ (<a href="#14.1.12">Section 14.1.12, "<code>transient</code>
+ (object)"</a>,
+ <a href="#14.3.8">Section 14.3.8, "<code>transient</code>
+ (composite value)"</a>,
+ <a href="#14.2.7">Section 14.2.7, "<code>transient</code>
+ (view)"</a>)
+ instead of doing it for each individual member. For example: </p>
+
+ <pre class="cxx">
+#pragma db object transient
+class person
+{
+ ...
+
+ std::string first_; // Transient.
+ std::string last_; // Transient.
+
+ #pragma db member(name) virtual(name) ...
+};
+ </pre>
+
+ <p>The ability to treat all the real data members as transient
+ becomes more important if we don't know the names of these
+ data members. This is often the case when we are working
+ with third-party types that document the accessor and
+ modifier functions but not the names of their private data
+ members. As an example, consider the <code>point</code> class
+ defined in a third-party <code>&lt;point></code> header file:</p>
+
+ <pre class="cxx">
+class point
+{
+public:
+ point ();
+ point (int x, int y);
+
+ int x () const;
+ int y () const;
+
+ void x (int);
+ void y (int);
+
+private:
+ ...
+};
+ </pre>
+
+ <p>To convert this class to an ODB composite value type we could
+ create the <code>point-mapping.hxx</code> file with the following
+ content:</p>
+
+ <pre class="cxx">
+#include &lt;point>
+
+#pragma db value(point) transient definition
+#pragma db member(point::x) virtual(int)
+#pragma db member(point::y) virtual(int)
+ </pre>
+
+ <p>Virtual data members can be of simple value, composite value,
+ container, or object pointer types. They can be used in persistent
+ classes, composite value types, and views.</p>
+
+ <h3><a name="14.4.14">14.4.14 <code>inverse</code></a></h3>
+
+ <p>The <code>inverse</code> specifier specifies that the data member of
+ an object pointer or a container of object pointers type is an
+ inverse side of a bidirectional object relationship. The single
+ required argument to this specifier is the corresponding data
+ member name in the referenced object. For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+using std::tr1::weak_ptr;
+
+class person;
+
+#pragma db object pointer(shared_ptr)
+class employer
+{
+ ...
+
+ std::vector&lt;shared_ptr&lt;person> > employees_;
+};
+
+#pragma db object pointer(shared_ptr)
+class person
+{
+ ...
+
+ #pragma db inverse(employee_)
+ weak_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>An inverse member does not have a corresponding column or, in case
+ of a container, table in the resulting database schema. Instead, the
+ column or table from the referenced object is used to retrieve the
+ relationship information. Only ordered and set containers can be used
+ for inverse members. If an inverse member is of an ordered container
+ type, it is automatically marked as unordered
+ (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>).</p>
+
+ <p>For a more detailed discussion of inverse members, refer to
+ <a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>.</p>
+
+ <h3><a name="14.4.15">14.4.15 <code>on_delete</code></a></h3>
+
+ <p>The <code>on_delete</code> specifier specifies the on-delete semantics
+ for a data member of an object pointer or a container of object
+ pointers type. The single required argument to this specifier must
+ be either <code>cascade</code> or <code>set_null</code>.</p>
+
+ <p>The <code>on_delete</code> specifier is translated directly to the
+ corresponding <code>ON DELETE</code> SQL clause. That is, if
+ <code>cascade</code> is specified, then when a pointed-to object
+ is erased from the database, the database state of the pointing
+ object is automatically erased as well. If <code>set_null</code> is
+ specified, then when a pointed-to object is erased from the database,
+ the database state of the pointing object is automatically updated
+ to set the pointer column to <code>NULL</code>. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db on_delete(cascade)
+ employer* employer_;
+};
+
+unsigned long id;
+
+{
+ employer e;
+ person p;
+ p.employer_ = &amp;e;
+
+ transaction t (db.begin ());
+
+ id = db.persist (e);
+ db.persist (p);
+
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ // Database state of the person object is erased as well.
+ //
+ db.erase&lt;employer> (id);
+
+ t.commit ();
+}
+ </pre>
+
+
+ <p>Note that this is a database-level functionality and care must be
+ taken in order not to end up with inconsistent object states in the
+ application's memory and database. The following example illustrates
+ the kind of problems one may encounter:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db on_delete(set_null)
+ employer* employer_;
+};
+
+employer e;
+person p;
+p.employer_ = &amp;e;
+
+{
+ transaction t (db.begin ());
+ db.persist (e);
+ db.persist (p);
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ // The employer column is set to NULL in the database but
+ // not the p.employer_ data member in the application.
+ //
+ db.erase (e);
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ // Override the employer column with an invalid pointer.
+ //
+ db.update (p);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>Note that even optimistic concurrency will not resolve such
+ issues unless you are using database-level support for optimistic
+ concurrency as well (for example, <code>ROWVERSION</code> in SQL
+ Server).</p>
+
+ <p>The <code>on_delete</code> specifier is only valid for non-inverse
+ object pointer data members. If the <code>set_null</code> semantics
+ is used, then the pointer must allow the <code>NULL</code> value.</p>
+
+ <h3><a name="14.4.16">14.4.16 <code>version</code></a></h3>
+
+ <p>The <code>version</code> specifier specifies that the data member stores
+ the object version used to support optimistic concurrency. If a class
+ has a version data member, then it must also be declared as having the
+ optimistic concurrency model using the <code>optimistic</code> pragma
+ (<a href="#14.1.5">Section 14.1.5, "<code>optimistic</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long version_;
+};
+ </pre>
+
+ <p>A version member must be of an integral C++ type and must map to
+ an integer or similar database type. Note also that object versions
+ are not reused. If you have a high update frequency, then care must
+ be taken not to run out of versions. In such situations, using
+ <code>unsigned&nbsp;long&nbsp;long</code> as the version type is a safe
+ choice.</p>
+
+ <p>For a more detailed discussion of optimistic concurrency, refer to
+ <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
+
+ <h3><a name="14.4.17">14.4.17 <code>index</code></a></h3>
+
+ <p>The <code>index</code> specifier instructs the ODB compiler to define
+ a database index for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index
+ std::string name_;
+};
+ </pre>
+
+ <p>For more information on defining database indexes, refer to
+ <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
+
+ <h3><a name="14.4.18">14.4.18 <code>unique</code></a></h3>
+
+ <p>The <code>index</code> specifier instructs the ODB compiler to define
+ a unique database index for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db unique
+ std::string name_;
+};
+ </pre>
+
+ <p>For more information on defining database indexes, refer to
+ <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
+
+ <h3><a name="14.4.19">14.4.19 <code>unordered</code></a></h3>
+
+ <p>The <code>unordered</code> specifier specifies that the member of
+ an ordered container type should be stored unordered in the database.
+ The database table for such a member will not contain the index column
+ and the order in which elements are retrieved from the database may
+ not be the same as the order in which they were stored. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db unordered
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>For a more detailed discussion of ordered containers and their
+ storage in the database, refer to <a href="#5.1">Section 5.1,
+ "Ordered Containers"</a>.</p>
+
+ <h3><a name="14.4.20">14.4.20 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies the table name that should
+ be used to store the contents of the container member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db table("nicknames")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>If the table name is not specified, then the container table name
+ is constructed by concatenating the object's table name, underscore,
+ and the public member name. The public member name is obtained
+ by removing the common member name decorations, such as leading and
+ trailing underscores, the <code>m_</code> prefix, etc. In the example
+ above, without the <code>table</code> specifier, the container's
+ table name would have been <code>person_nicknames</code>.</p>
+
+ <p>The <code>table</code> specifier can also be used for members of
+ composite value types. In this case it specifies the table name
+ prefix for container members inside the composite value type. Refer
+ to <a href="#7.2.2">Section 7.2.2, "Composite Value Column and Table
+ Names"</a> for details.</p>
+
+ <p>The container table name can be qualified with a database
+ schema, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db table("extras.nicknames")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>For more information on database schemas and the format of the
+ qualified names, refer to <a href="#14.1.8">Section 14.1.8,
+ "<code>schema</code>"</a>.</p>
+
+ <h3><a name="14.4.21">14.4.21 <code>load</code>/<code>update</code></a></h3>
+
+ <p>The <code>load</code> and <code>update</code> specifiers specify the
+ loading and updating behavior for an object section, respectively.
+ Valid values for the <code>load</code> specifier are
+ <code>eager</code> (default) and <code>lazy</code>. Valid values for
+ the <code>update</code> specifier are <code>always</code> (default),
+ <code>change</code>, and <code>manual</code>. For more information
+ on object sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
+
+ <h3><a name="14.4.22">14.4.22 <code>section</code></a></h3>
+
+ <p>The <code>section</code> specifier indicates that a data member
+ of a persistent class belongs to an object section. The single
+ required argument to this specifier is the name of the section
+ data member. This specifier can only be used on direct data
+ members of a persistent class. For more information on object
+ sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
+
+ <h3><a name="14.4.23">14.4.23 <code>added</code></a></h3>
+
+ <p>The <code>added</code> specifier marks the data member as
+ soft-added. The single required argument to this specifier is
+ the addition version. For more information on this functionality,
+ refer to <a href="#13.4">Section 13.4, "Soft Object Model
+ Changes"</a>.</p>
+
+ <h3><a name="14.4.24">14.4.24 <code>deleted</code></a></h3>
+
+ <p>The <code>deleted</code> specifier marks the data member as
+ soft-deleted. The single required argument to this specifier is
+ the deletion version. For more information on this functionality,
+ refer to <a href="#13.4">Section 13.4, "Soft Object Model
+ Changes"</a>.</p>
+
+ <h3><a name="14.4.25">14.4.25 <code>index_type</code></a></h3>
+
+ <p>The <code>index_type</code> specifier specifies the native
+ database type that should be used for an ordered container's
+ index column of the data member. The semantics of <code>index_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). The native
+ database type is expected to be an integer type. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index_type("SMALLINT UNSIGNED")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <h3><a name="14.4.26">14.4.26 <code>key_type</code></a></h3>
+
+ <p>The <code>key_type</code> specifier specifies the native
+ database type that should be used for a map container's
+ key column of the data member. The semantics of <code>key_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db key_type("INT UNSIGNED")
+ std::map&lt;unsigned short, float> age_weight_map_;
+};
+ </pre>
+
+ <h3><a name="14.4.27">14.4.27 <code>value_type</code></a></h3>
+
+ <p>The <code>value_type</code> specifier specifies the native
+ database type that should be used for a container's
+ value column of the data member. The semantics of <code>value_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value_type("VARCHAR(255)")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code>
+ (<a href="#14.4.28">Section 14.4.28,
+ "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a value
+ column.</p>
+
+ <h3><a name="14.4.28">14.4.28 <code>value_null</code>/<code>value_not_null</code></a></h3>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
+ specify that a container's element value for the data member can or
+ cannot be <code>NULL</code>, respectively. The semantics of
+ <code>value_null</code> and <code>value_not_null</code> are similar
+ to those of the <code>null</code> and <code>not_null</code> specifiers
+ (<a href="#14.4.6">Section 14.4.6, "<code>null</code>/<code>not_null</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+#pragma db object
+class person
+{
+ ...
+};
+
+#pragma db object
+class account
+{
+ ...
+
+ #pragma db value_not_null
+ std::vector&lt;shared_ptr&lt;person> > holders_;
+};
+ </pre>
+
+ <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
+ Multiset Containers"</a>) the element value is automatically treated
+ as not allowing a <code>NULL</code> value.</p>
+
+ <h3><a name="14.4.29">14.4.29 <code>id_options</code></a></h3>
+
+ <p>The <code>id_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ id column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id options("COLLATE binary")
+ std::string name_;
+
+ #pragma db id_options("COLLATE binary")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The semantics of <code>id_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.30">14.4.30 <code>index_options</code></a></h3>
+
+ <p>The <code>index_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ index column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index_options("ZEROFILL")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The semantics of <code>index_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.31">14.4.31 <code>key_options</code></a></h3>
+
+ <p>The <code>key_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ key column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db key_options("COLLATE binary")
+ std::map&lt;std::string, std::string> properties_;
+};
+ </pre>
+
+ <p>The semantics of <code>key_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.32">14.4.32 <code>value_options</code></a></h3>
+
+ <p>The <code>value_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ value column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value_options("COLLATE binary")
+ std::set&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The semantics of <code>value_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.33">14.4.33 <code>id_column</code></a></h3>
+
+ <p>The <code>id_column</code> specifier specifies the column
+ name that should be used to store the object id in a
+ container's table for the data member. The semantics of
+ <code>id_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id_column("person_id")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>object_id</code>
+ is used by default.</p>
+
+ <h3><a name="14.4.34">14.4.34 <code>index_column</code></a></h3>
+
+ <p>The <code>index_column</code> specifier specifies the column
+ name that should be used to store the element index in an
+ ordered container's table for the data member. The semantics of
+ <code>index_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index_column("nickname_number")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>index</code>
+ is used by default.</p>
+
+ <h3><a name="14.4.35">14.4.35 <code>key_column</code></a></h3>
+
+ <p>The <code>key_column</code> specifier specifies the column
+ name that should be used to store the key in a map
+ container's table for the data member. The semantics of
+ <code>key_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db key_column("age")
+ std::map&lt;unsigned short, float> age_weight_map_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>key</code>
+ is used by default.</p>
+
+ <h3><a name="14.4.36">14.4.36 <code>value_column</code></a></h3>
+
+ <p>The <code>value_column</code> specifier specifies the column
+ name that should be used to store the element value in a
+ container's table for the data member. The semantics of
+ <code>value_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value_column("weight")
+ std::map&lt;unsigned short, float> age_weight_map_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>value</code>
+ is used by default.</p>
+
+ <h2><a name="14.5">14.5 Namespace Pragmas</a></h2>
+
+ <p>A pragma with the <code>namespace</code> qualifier describes a
+ C++ namespace. Similar to other qualifiers, <code>namespace</code>
+ can also refer to a named C++ namespace, for example:</p>
+
+ <pre class="cxx">
+namespace test
+{
+ ...
+}
+
+#pragma db namespace(test) ...
+ </pre>
+
+ <p>To refer to the global namespace in the <code>namespace</code>
+ qualifier the following special syntax is used:</p>
+
+ <pre class="cxx">
+#pragma db namespace() ....
+ </pre>
+
+ <p>The <code>namespace</code> qualifier can be optionally followed,
+ in any order, by one or more specifiers summarized in the
+ table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>pointer</code></td>
+ <td>pointer type for persistent classes and views inside a namespace</td>
+ <td><a href="#14.5.1">14.5.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table name prefix for persistent classes inside a namespace</td>
+ <td><a href="#14.5.2">14.5.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>schema</code></td>
+ <td>database schema for persistent classes inside a namespace</td>
+ <td><a href="#14.5.3">14.5.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>session</code></td>
+ <td>enable/disable session support for persistent classes inside a namespace</td>
+ <td><a href="#14.5.4">14.5.4</a></td>
+ </tr>
+
+ </table>
+
+ <h3><a name="14.5.1">14.5.1 <code>pointer</code></a></h3>
+
+ <p>The <code>pointer</code> specifier specifies the default pointer
+ type for persistent classes and views inside the namespace. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace pointer(std::tr1::shared_ptr)
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>There are only two valid ways to specify a pointer with the
+ <code>pointer</code> specifier at the namespace level. We can
+ specify the template name of a smart pointer in which
+ case the ODB compiler will automatically append the class
+ name as a template argument. Or we can use <code>*</code>
+ to denote a raw pointer.</p>
+
+ <p>Note also that we can always override the default pointer
+ specified at the namespace level for any persistent class
+ or view inside this namespace. For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace pointer(std::unique_ptr)
+namespace accounting
+{
+ #pragma db object pointer(std::shared_ptr)
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>For a more detailed discussion of object and view pointers, refer
+ to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
+
+ <h3><a name="14.5.2">14.5.2 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies a table prefix
+ that should be added to table names of persistent classes inside
+ the namespace. For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace table("acc_")
+namespace accounting
+{
+ #pragma db object table("employees")
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object table("employers")
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>In the above example the resulting table names will be
+ <code>acc_employees</code> and <code>acc_employers</code>.</p>
+
+ <p>The table name prefix can also be specified with the
+ <code>--table-prefix</code> ODB compiler option. Note
+ that table prefixes specified at the namespace level as well
+ as with the command line option are accumulated. For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace() table("audit_")
+
+#pragma db namespace table("hr_")
+namespace hr
+{
+ #pragma db object table("employees")
+ class employee
+ {
+ ...
+ };
+}
+
+#pragma db object table("employers")
+class employer
+{
+ ...
+};
+ </pre>
+
+ <p>If we compile the above example with the
+ <code>--table-prefix&nbsp;test_</code> option, then the
+ <code>employee</code> class table will be called
+ <code>test_audit_hr_employees</code> and <code>employer</code> &mdash;
+ <code>test_audit_employers</code>.</p>
+
+ <p>Table prefixes can be used as an alternative to database schemas
+ (<a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>) if
+ the target database system does not support schemas.</p>
+
+ <h3><a name="14.5.3">14.5.3 <code>schema</code></a></h3>
+
+ <p>The <code>schema</code> specifier specifies a database schema
+ that should be used for persistent classes inside the namespace.
+ For more information on specifying a database schema refer to
+ <a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>.</p>
+
+ <h3><a name="14.5.4">14.5.4 <code>session</code></a></h3>
+
+ <p>The <code>session</code> specifier specifies whether to enable
+ session support for persistent classes inside the namespace. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace session
+namespace hr
+{
+ #pragma db object // Enabled.
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object session(false) // Disabled.
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Session support is disabled by default unless the
+ <code>--generate-session</code> ODB compiler option is specified.
+ Session support specified at the namespace level can be overridden
+ on the per object basis (<a href="#14.1.10">Section 14.1.10,
+ "<code>session</code>"</a>). For more information on sessions,
+ refer to <a href="#11">Chapter 11, "Session"</a>.</p>
+
+<h2><a name="14.6">14.6 Object Model Pragmas</a></h2>
+
+ <p>A pragma with the <code>model</code> qualifier describes the
+ whole C++ object model. For example:</p>
+
+ <pre class="cxx">
+#pragma db model ...
+ </pre>
+
+ <p>The <code>model</code> qualifier can be followed, in any order,
+ by one or more specifiers summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>version</code></td>
+ <td>object model version</td>
+ <td><a href="#14.6.1">14.6.1</a></td>
+ </tr>
+
+ </table>
+
+ <h3><a name="14.6.1">14.6.1 <code>version</code></a></h3>
+
+ <p>The <code>version</code> specifier specifies the object model
+ version when schema evolution support is used. The first two
+ required arguments to this specifier are the base and current
+ model versions, respectively. The third optional argument
+ specifies whether the current version is open for changes.
+ Valid values for this argument are <code>open</code> (the
+ default) and <code>closed</code>. For more information on
+ this functionality, refer to <a href="#13">Chapter 13,
+ "Database Schema Evolution"</a>.</p>
+
+
+ <h2><a name="14.7">14.7 Index Definition Pragmas</a></h2>
+
+ <p>While it is possible to manually add indexes to the generated
+ database schema, it is more convenient to do this as part of
+ the persistent class definitions. A pragma with the <code>index</code>
+ qualifier describes a database index. It has the following
+ general format:</p>
+
+<pre class="cxx">
+#pragma db index[("&lt;name>")] \
+ [unique|type("&lt;type>")] \
+ [method("&lt;method>")] \
+ [options("&lt;index-options>")] \
+ member(&lt;name>[, "&lt;column-options>"])... \
+ members(&lt;name>[,&lt;name>...])...
+</pre>
+
+ <p>The <code>index</code> qualifier can optionally specify the
+ index name. If the index name is not specified, then one is
+ automatically derived by appending the <code>_i</code> suffix
+ to the column name of the index member. If the name is not
+ specified and the index contains multiple members, then the
+ index definition is invalid.</p>
+
+ <p>The optional <code>type</code>, <code>method</code>, and
+ <code>options</code> clauses specify the index type, for
+ example <code>UNIQUE</code>, index method, for example
+ <code>BTREE</code>, and index options, respectively. The
+ <code>unique</code> clause is a shortcut for
+ <code>type("UNIQUE")</code>. Note that not all database
+ systems support specifying an index method or options.
+ For more information on the database system-specific index
+ types, methods, and options, refer to <a href="#II">Part II,
+ "Database Systems"</a>.</p>
+
+ <p>To specify index members we can use the <code>member</code>
+ or <code>members</code> clauses, or a mix of the two. The
+ <code>member</code> clause allows us to specify a single
+ index member with optional column options, for example,
+ <code>"ASC"</code>. If we need to create a composite
+ index that contains multiple members, then we can repeat
+ the <code>member</code> clause several times or, if the
+ members don't have any column options, we can use a single
+ <code>members</code> clause instead. Similar to the index
+ type, method, and options, the format of column options is
+ database system-specific. For more details, refer to
+ <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>The following code fragment shows some typical examples
+ of index definitions:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ int x;
+ int y;
+ int z1;
+ int z2;
+
+ // An index for member x with automatically-assigned name x_i.
+ //
+ #pragma db index member(x)
+
+ // A unique index named y_index for member y which is sorted in
+ // the descending order. The index is using the BTREE method.
+ //
+ #pragma db index("y_index") unique method("BTREE") member(y, "DESC")
+
+ // A composite BITMAP index named z_i for members z1 and z2.
+ //
+ #pragma db index("z_i") type("BITMAP") members(z1, z2)
+};
+</pre>
+
+ <p>ODB also offers a shortcut for defining an index with the default
+ method and options for a single data member. Such an index can
+ be defined using the <code>index</code> (<a href="#14.4.17">Section
+ 14.4.17, "<code>index</code>"</a>) or <code>unique</code>
+ (<a href="#14.4.18">Section 14.4.18, "<code>unique</code>"</a>)
+ member specifier. For example:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db index
+ int x;
+
+ #pragma db type("INT") unique
+ int y;
+};
+</pre>
+
+ <p>The above example is semantically equivalent to the following
+ more verbose version:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ int x;
+
+ #pragma db type("INT")
+ int y;
+
+ #pragma db index member(x)
+ #pragma db index unique member(y)
+};
+</pre>
+
+ <p>While it is convenient to define an index inside a persistent
+ class, it is also possible to do that out of the class body. In this
+ case, the index name, if specified, must be prefixed with the
+ potentially-qualified class name. For example:</p>
+
+<pre class="cxx">
+namespace n
+{
+ #pragma db object
+ class object
+ {
+ ...
+
+ int x;
+ int y;
+ };
+
+ // An index for member x in persistent class object with automatically-
+ // assigned name x_i.
+ //
+ #pragma db index(object) member(x)
+}
+
+// An index named y_index for member y in persistent class n::object.
+//
+#pragma db index(n::object::"y_index") member(y)
+</pre>
+
+ <p>It is possible to define an index on a member that is of a
+ composite value type or on some of its nested members. For
+ example:</p>
+
+<pre class="cxx">
+#pragma db value
+struct point
+{
+ int x;
+ int y;
+ int z;
+};
+
+#pragma db object
+class object
+{
+ // An index that includes all of the p1's nested members.
+ //
+ #pragma db index
+ point p1;
+
+ point p2;
+
+ // An index that includes only p2.x and p2.y.
+ //
+ #pragma db index("p2_xy_i") members(p2.x, p2.y)
+};
+</pre>
+
+ <p>When generating a schema for a container member (<a href="#5">Chapter 5,
+ "Containers"</a>), ODB automatically defines two indexes on the container
+ table. One is for the object id that references the object table and the
+ other is for the index column in case the container is ordered
+ (<a href="#5.1">Section 5.1, "Ordered Containers"</a>). By default these
+ indexes use the default index name, type, method, and options. The
+ <code>index</code> pragma allows us to customize these two indexes by
+ recognizing the special <code>id</code> and <code>index</code> nested
+ member names when specified after a container member. For example:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ std::vector&lt;int> v;
+
+ // Change the container id index name.
+ //
+ #pragma db index("id_index") member(v.id)
+
+ // Change the container index index method.
+ //
+ #pragma db index method("BTREE") member(v.index)
+};
+</pre>
+
+ <h2><a name="14.8">14.8 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 extended types such as geospatial types,
+ user-defined types, and collections (arrays, table types,
+ key-value stores, etc). In order to support such extended 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 extended 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.13">Section 3.13, "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 extended types.</p>
+
+ <h2><a name="14.9">14.9 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
+ issue warnings about pragmas that it doesn't recognize. There
+ are several ways to deal with this problem. The easiest is to
+ disable such warnings using one of the compiler-specific command
+ line options or warning control pragmas. This method is described
+ in the following sub-section for popular C++ compilers.</p>
+
+ <p>There are also several C++ compiler-independent methods that we
+ can employ. The first is to use the <code>PRAGMA_DB</code> macro,
+ defined in <code>&lt;odb/core.hxx></code>, instead of using
+ <code>#pragma&nbsp;db</code> directly. This macro expands to the
+ ODB pragma when compiled with the ODB compiler and to an empty
+ declaration when compiled with other compilers. The following example
+ shows how we can use this macro:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+PRAGMA_DB(object)
+class person
+{
+ ...
+
+ PRAGMA_DB(id)
+ unsigned long id_;
+};
+ </pre>
+
+ <p>An alternative to using the <code>PRAGMA_DB</code> macro is to
+ group the <code>#pragma&nbsp;db</code> directives in blocks that are
+ conditionally included into compilation only when compiled with the
+ ODB compiler. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+ ...
+
+ unsigned long id_;
+};
+
+#ifdef ODB_COMPILER
+# pragma db object(person)
+# pragma db member(person::id_) id
+#endif
+ </pre>
+
+ <p>The disadvantage of this approach is that it can quickly become
+ overly verbose when positioned pragmas are used.</p>
+
+ <h3><a name="14.9.1">14.9.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.
+ To disable only the unknown pragma warning, we can add the
+ <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
+ for example:</p>
+
+ <pre class="terminal">
+g++ -Wall -Wno-unknown-pragmas ...
+ </pre>
+
+ <h3><a name="14.9.2">14.9.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
+ the warnings altogether (level 0), we will see this warning.</p>
+
+ <p>To disable this warning via the compiler command line, we can add
+ the <code>/wd4068</code> C++ compiler option in Visual Studio 2008
+ and earlier. In Visual Studio 2010 and later there is now a special
+ GUI field where we can enter warning numbers that should be disabled.
+ Simply enter 4068 into this field.</p>
+
+ <p>We can also disable this warning for only a specific header or
+ a fragment of a header using the warning control pragma. For
+ example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma warning (push)
+#pragma warning (disable:4068)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#pragma warning (pop)
+ </pre>
+
+ <h3><a name="14.9.3">14.9.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.
+ To disable only the unknown pragma warning we can add the
+ <code>-erroff=unknownpragma</code> option anywhere on the
+ command line, for example:</p>
+
+ <pre class="terminal">
+CC +w -erroff=unknownpragma ...
+ </pre>
+
+ <h3><a name="14.9.4">14.9.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>
+ command line option, for example:</p>
+
+ <pre class="terminal">
+xlC -qsuppress=1540-1401 ...
+ </pre>
+
+ <h3><a name="14.9.5">14.9.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>
+ command line option, for example:</p>
+
+ <pre class="terminal">
+aCC +W2161 ...
+ </pre>
+
+ <h3><a name="14.9.6">14.9.6 Clang</a></h3>
+
+ <p>Clang does not issue warnings about unknown pragmas
+ unless requested with the <code>-Wall</code> command line option.
+ To disable only the unknown pragma warning, we can add the
+ <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
+ for example:</p>
+
+ <pre class="terminal">
+clang++ -Wall -Wno-unknown-pragmas ...
+ </pre>
+
+ <p>We can also disable this warning for only a specific header or
+ a fragment of a header using the warning control pragma. For
+ example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#pragma clang diagnostic pop
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="15">15 Advanced Techniques and Mechanisms</a></h1>
+
+ <p>This chapter covers more advanced techniques and mechanisms
+ provided by ODB that may be useful in certain situations.</p>
+
+ <h2><a name="15.1">15.1 Transaction Callbacks</a></h2>
+
+ <p>The ODB transaction class (<code>odb::transaction</code>) allows
+ an application to register a callback that will be called after
+ the transaction is finalized, that is, committed or rolled back.
+ This mechanism can be used, for example, to restore values that
+ were updated during the transaction execution to their original
+ states if the transaction is rolled back.</p>
+
+ <p>The callback management interface of the <code>transaction</code>
+ class is shown below.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class transaction
+ {
+ ...
+
+ public:
+ static const unsigned short event_commit = 0x01;
+ static const unsigned short event_rollback = 0x02;
+ static const unsigned short event_all = event_commit | event_rollback;
+
+ typedef void (*callback_type) (
+ unsigned short event, void* key, unsigned long long data);
+
+ void
+ callback_register (callback_type callback,
+ void* key,
+ unsigned short event = event_all,
+ unsigned long long data = 0,
+ transaction** state = 0);
+
+
+ void
+ callback_unregister (void* key);
+
+ void
+ callback_update (void* key,
+ unsigned short event,
+ unsigned long long data = 0,
+ transaction** state = 0);
+ }
+}
+ </pre>
+
+ <p>The <code>callback_register()</code> function registers a
+ post-commit/rollback callback. The <code>callback</code>
+ argument is the function that should be called. The
+ <code>key</code> argument is used by the transaction
+ to identify this callback. It is also normally used
+ to pass an address of the data object on which the
+ callback function will work. The <code>event</code>
+ argument is the bitwise-or of the events that should
+ trigger the callback.</p>
+
+ <p>The optional data argument can be used to store any POD
+ user data that doesn't exceed 8 bytes in size and doesn't require
+ alignment greater than <code>unsigned long long</code>. For
+ example, we could store an old value of a flag or a counter
+ that needs to be restored in case of a roll back.</p>
+
+ <p>The optional <code>state</code> argument can be used to
+ indicate that the callback has been unregistered because
+ the transaction was finalized. In this case the transaction
+ automatically resets the passed pointer to 0. This is
+ primarily useful if we are interested in only one of
+ the events (commit or rollback).</p>
+
+ <p>The <code>callback_unregister()</code> function unregisters a previously
+ registered callback. If the number of registered callbacks is
+ large, then this can be a slow operation. Generally, the callback
+ mechanism is optimized for cases where the callbacks stay
+ registered until the transaction is finalized.</p>
+
+ <p>Note also that you don't need to unregister a callback that has
+ been called or auto-reset using the <code>state</code> argument
+ passed to <code>callback_register()</code>. This function does nothing
+ if the key is not found.</p>
+
+ <p>The <code>callback_update()</code> function can be used to update
+ the <code>event</code>, <code>data</code>, and <code>state</code>
+ values of a previously registered callback. Similar to
+ <code>callback_unregister()</code>, this is a potentially slow
+ operation.</p>
+
+ <p>When the callback is called, it is passed the event that
+ triggered it, as well as the <code>key</code> and
+ <code>data</code> values that were passed to the
+ <code>callback_register()</code> function. Note also that the order
+ in which the callbacks are called is unspecified. The rollback
+ event can be triggered by an exception. In this case, if the
+ callback throws, the program will be terminated.</p>
+
+ <p>The following example shows how we can use transaction
+ callbacks together with database operation callbacks
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
+ to manage the object's "dirty" flag.</p>
+
+ <pre class="cxx">
+#include &lt;odb/callback.hxx>
+#include &lt;odb/transaction.hxx>
+
+#pragma db object callback(update)
+class object
+{
+ ...
+
+ #pragma db transient
+ mutable bool dirty_;
+
+ // Non-NULL value indicates that we are registered
+ // with this transaction.
+ //
+ #pragma db transient
+ mutable odb::transaction* tran_;
+
+ void
+ update (odb::callback_event e, odb::database&amp;) const
+ {
+ using namespace odb::core;
+
+ if (e == callback_event::post_update)
+ return;
+
+ // Mark the object as clean again but register a
+ // transaction callback in case the update is rolled
+ // back.
+ //
+ tran_ = &amp;transaction::current ();
+ tran_->callback_register (&amp;rollback,
+ const_cast&lt;object*> (this),
+ transaction::event_rollback,
+ 0,
+ &amp;tran_);
+ dirty_ = false;
+ }
+
+ static void
+ rollback (unsigned short, void* key, unsigned long long)
+ {
+ // Restore the dirty flag since the changes have been
+ // rolled back.
+ //
+ object&amp; o (*static_cast&lt;object*> (key));
+ o.dirty_ = true;
+ }
+
+ ~object ()
+ {
+ // Unregister the callback if we are going away before
+ // the transaction.
+ //
+ if (tran_ != 0)
+ tran_->callback_unregister (this);
+ }
+};
+ </pre>
+
+ <h2><a name="15.2">15.2 Persistent Class Template Instantiations</a></h2>
+
+ <p>Similar to composite value types (<a href="#7.2">Section 7.2, "Composite
+ Value Types"</a>), a persistent object can be defined as an instantiation
+ of a C++ class template, for example:</p>
+
+ <pre class="cxx">
+template &lt;typename T>
+class person
+{
+ ...
+
+ T first_;
+ T last_;
+};
+
+typedef person&lt;std::string> std_person;
+
+#pragma db object(std_person)
+#pragma db member(std_person::last_) id
+ </pre>
+
+ <p>Note that the database support code for such a persistent object
+ is generated when compiling the header containing the
+ <code>db&nbsp;object</code> pragma and not the header containing
+ the template definition or the <code>typedef</code> name. This
+ allows us to use templates defined in other files, for example:</p>
+
+ <pre class="cxx">
+#include &lt;utility> // std::pair
+
+typedef std::pair&lt;unsigned int, std::string> person;
+#pragma db object(person)
+#pragma db member(person::first) id auto column("id")
+#pragma db member(person::second) column("name")
+ </pre>
+
+ <p>You may also have to explicitly specify the object type in
+ calls to certain <code>database</code> class functions due
+ to the inability do distinguish, at the API level, between
+ smart pointers and persistent objects defined as class
+ template instantiations. For example:</p>
+
+ <pre class="cxx">
+person p;
+
+db.update (p); // Error.
+db.reload (p); // Error.
+db.erase (p); // Error.
+
+db.update&lt;person> (p); // Ok.
+db.reload&lt;person> (p); // Ok.
+db.erase&lt;person> (p); // Ok.
+ </pre>
+
+ <p>It also makes sense to factor persistent data members that do not
+ depend on template arguments into a common, non-template base class.
+ The following more realistic example illustrates this approach:</p>
+
+ <pre class="cxx">
+#pragma db object abstract
+class base_common
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id;
+};
+
+template &lt;typename T>
+class base: public base_common
+{
+ ...
+
+ T value;
+};
+
+typedef base&lt;std::string> string_base;
+#pragma db object(string_base) abstract
+
+#pragma db object
+class derived: public string_base
+{
+ ...
+};
+ </pre>
+
+ <h2><a name="15.3">15.3 Bulk Database Operations</a></h2>
+
+ <p>Some database systems supported by ODB provide a mechanism, often called
+ bulk or batch statement execution, that allows us to execute the same SQL
+ statement on multiple sets of data at once and with a single database API
+ call (or equivalent). This often results in significantly better
+ performance if we need to execute the same statement for a large number
+ of data sets (thousands to millions).</p>
+
+ <p>ODB translates this mechanism to bulk operations which allow us to
+ persist, update, or erase a range of objects in the database. Currently,
+ from all the database systems supported by ODB, only Oracle, Microsoft
+ SQL Server, and PostgreSQL are capable of bulk operations (but
+ see <a href="#19.5.7">Section 19.5.7, "Bulk Operations Support"</a> for
+ PostgreSQL limitations). There is also currently no emulation of the bulk
+ API for other databases nor dynamic multi-database support. As a result,
+ if you are using dynamic multi-database support, you will need to "drop
+ down" to static support in order to access the bulk API. Refer
+ to <a href="#16">Chapter 16, "Multi-Database Support"</a> for
+ details.</p>
+
+ <p>As we will discuss later in this section, bulk operations have
+ complex failure semantics that is dictated by the underlying
+ database API. As a result, support for bulk persist, update,
+ and erase is limited to persistent classes for which these
+ operations can be performed with a single database statement
+ execution. In particular, bulk operations are not available
+ for polymorphic objects (<a href="#8.2">Section 8.2,
+ "Polymorphism Inheritance"</a>) or objects that have
+ containers (inverse containers of object pointers are an
+ exception). Furthermore, for objects that have sections
+ (<a href="#9">Chapter 9, "Sections"</a>) the bulk update operation
+ will only be available if all the sections are manually-updated.
+ On the other hand, bulk operations are supported for objects
+ that use optimistic concurrency (<a href="#12">Chapter 12,
+ "Optimistic Concurrency"</a>) or have no object id
+ (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
+
+ <p>To enable the generation of bulk operation support for a persistent
+ class we use the <code>bulk</code> pragma. For example:</p>
+
+ <pre class="cxx">
+#pragma db object bulk(5000)
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id;
+};
+ </pre>
+
+ <p>The single argument to the <code>bulk</code> pragma is the batch
+ size. The batch size specifies the maximum number of data sets that
+ should be handled with a single underlying statement execution (or
+ equivalent). If the range that we want to perform the bulk operation on
+ contains more objects than the batch size, then ODB will split this
+ operation into multiple underlying statement executions (batches). To
+ illustrate this point with an example, suppose we want to persist 53,000
+ objects and the batch size is 5,000. ODB will then execute the statement
+ 11 times, the first 10 times with 5,000 data sets each, and the last time
+ with the remaining 3,000 data sets.</p>
+
+ <p>The commonly used batch sizes are in the 2,000-5,000 range, though
+ smaller or larger batches could provide better performance,
+ depending on the situation. As a result, it is recommended to
+ experiment with different batch sizes to determine the optimum
+ number for a particular object and its use-cases. Note also that
+ you may achieve better performance by also splitting a large bulk
+ operation into multiple transactions (<a href="#3.5">Section 3.5,
+ "Transactions"</a>).</p>
+
+ <p>For database systems that do not support bulk operations the
+ <code>bulk</code> pragma is ignored. It is also possible to
+ specify different batch sizes for different database systems
+ by using the database prefix, for example:</p>
+
+ <pre class="cxx">
+#pragma db object mssql:bulk(3000) oracle:bulk(4000) pgsql:bulk(2000)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>Note that while specifying the batch size at compile time might
+ seem inflexible, this approach allows ODB to place internal
+ arrays of the fixed batch size on the stack rather than
+ allocating them in the dynamic memory. However, specifying the
+ batch size at runtime may be supported in the future.</p>
+
+ <p>Once the bulk support is enabled for a particular object, we can
+ use the following <code>database</code> functions to perform bulk
+ operations:</p>
+
+ <pre class="cxx">
+template &lt;typename I>
+void
+persist (I begin, I end, bool continue_failed = true);
+
+template &lt;typename I>
+void
+update (I begin, I end, bool continue_failed = true);
+
+template &lt;typename I>
+void
+erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+template &lt;typename T, typename I>
+void
+erase (I id_begin, I id_end, bool continue_failed = true);
+ </pre>
+
+ <p>Every bulk API function expects a range of elements, passed in
+ the canonical C++ form as a pair of input iterators. In case of
+ <code>persist()</code>, <code>update()</code>, and the first
+ <code>erase()</code> overload, we pass a range of objects,
+ either as references or as pointers, raw or smart. The following
+ example illustrates the most common scenarios using the
+ <code>persist()</code> call:</p>
+
+ <pre class="cxx">
+// C array of objects.
+//
+person a[2] {{"John", "Doe"}, {"Jane", "Doe"}};
+
+db.persist (a, a + sizeof(a) / sizeof(a[0]));
+
+
+// Vector of objects.
+//
+std::vector&lt;person> v {{"John", "Doe"}, {"Jane", "Doe"}};
+
+db.persist (v.begin (), v.end ());
+
+
+// C array of raw pointers to objects.
+//
+person p1 ("John", "Doe");
+person p2 ("Jane", "Doe");
+person* pa[2] {&amp;p1, &amp;p2};
+
+db.persist (pa, pa + sizeof(pa) / sizeof(pa[0]));
+
+
+// Vector of raw pointers to objects.
+//
+std::vector&lt;person*> pv {&amp;p1, &amp;p2};
+
+db.persist (pv.begin (), pv.end ());
+
+
+// Vector of smart (shared) pointers to objects.
+//
+std::vector&lt;std::shared_ptr&lt;person>> sv {
+ std::make_shared&lt;person> ("John", "Doe"),
+ std::make_shared&lt;person> ("Jane", "Doe")};
+
+db.persist (sv.begin (), sv.end ());
+ </pre>
+
+ <p>The ability to perform a bulk operation on a range of raw pointers
+ to objects can be especially useful when the application stores
+ objects in a way that does not easily conform to the pair of
+ iterators interface. In such cases we can create a temporary
+ container of shallow pointers to objects and use that to perform
+ the bulk operation, for example:</p>
+
+ <pre class="cxx">
+struct person_entry
+{
+ person obj;
+
+ // Some additional data.
+ ...
+};
+
+typedef std::vector&lt;person_entry> people;
+
+void
+persist (odb::database&amp; db, people&amp; p)
+{
+ std::vector&lt;person*> tmp;
+ tmp.reserve (p.size ());
+ std::for_each (p.begin (),
+ p.end (),
+ [&amp;tmp] (person_entry&amp; pe)
+ {
+ tmp.push_back (&amp;pe.obj);
+ });
+
+
+ db.persist (tmp.begin (), tmp.end ());
+}
+ </pre>
+
+ <p>The second overload of the bulk <code>erase()</code> function
+ allows us to pass a range of object ids rather than objects
+ themselves. As with the corresponding non-bulk version, we
+ have to specify the object type explicitly, for example:</p>
+
+ <pre class="cxx">
+std::vector&lt;unsigned long> ids {1, 2};
+
+db.erase&lt;person> (ids.begin (), ids.end ());
+ </pre>
+
+ <p>Conceptually, a bulk operation is equivalent to performing the
+ corresponding non-bulk version in a loop, except when it comes to the
+ failure semantics. Some databases that currently are capable of bulk
+ operations (specifically, Oracle and SQL Server) do not stop when a data
+ set in a batch fails (for example, because of a unique constraint
+ violation). Instead, they continue executing subsequent data sets until
+ every element in the batch has been attempted. The
+ <code>continue_failed</code> argument in the bulk functions listed
+ above specifies whether ODB should extend this behavior and continue
+ with subsequent batches if the one it has tried to execute has failed
+ elements. The default behavior is to continue.</p>
+
+ <p>The consequence of this failure semantics is that we may have
+ multiple elements in the range failed for different reasons.
+ For example, if we tried to persist a number of objects, some
+ of them might have failed because they are already persistent
+ while others &mdash; because of a unique constraint violation.
+ As a result, ODB uses the special <code>odb::multiple_exceptions</code>
+ class to report failures in the bulk API functions. This
+ exception is thrown if one or more elements in the range have
+ failed and it contains the error information in the form of other
+ ODB exception for each failed position. The
+ <code>multiple_exceptions</code> class has the following interface:</p>
+
+ <pre class="cxx">
+struct multiple_exceptions: odb::exception
+{
+ // Element type.
+ //
+ struct value_type
+ {
+ std::size_t
+ position () const;
+
+ const odb::exception&amp;
+ exception () const;
+
+ bool
+ maybe () const;
+ };
+
+ // Iteration.
+ //
+ typedef std::set&lt;value_type> set_type;
+
+ typedef set_type::const_iterator iterator;
+ typedef set_type::const_iterator const_iterator;
+
+ iterator
+ begin () const;
+
+ iterator
+ end () const;
+
+ // Lookup.
+ //
+ const value_type*
+ operator[] (std::size_t) const;
+
+ // Severity, failed and attempted counts.
+ //
+ std::size_t
+ attempted () const;
+
+ std::size_t
+ failed () const;
+
+ bool
+ fatal () const;
+
+ void
+ fatal (bool);
+
+ // Direct data access.
+ //
+ const set_type&amp;
+ set () const;
+
+ // odb::exception interface.
+ //
+ virtual const char*
+ what () const throw ();
+};
+ </pre>
+
+ <p>The <code>multiple_exceptions</code> class has a map-like interface
+ with the key being the position in the range and the value being
+ the exception plus the <code>maybe</code> flag (discussed below).
+ As a result, we can either iterate over the failed positions or
+ we can check whether a specific position in the range has failed.
+ The following example shows what a <code>catch</code>-handler for
+ this exception might look like:</p>
+
+ <pre class="cxx">
+std::vector&lt;person> objs {{"John", "Doe"}, {"Jane", "Doe"}};
+
+try
+{
+ db.persist (objs.begin (), objs.end ());
+}
+catch (const odb::multiple_exceptions&amp; me)
+{
+ for (const auto&amp; v: me)
+ {
+ size_t p (v.position ());
+
+ try
+ {
+ throw v.exception ();
+ }
+ catch (const odb::object_already_persistent&amp;)
+ {
+ cerr &lt;&lt; p &lt;&lt; ": duplicate id: " &lt;&lt; objs[p].id () &lt;&lt; endl;
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; p &lt;&lt; ": " &lt;&lt; e.what () &lt;&lt; endl;
+ }
+ }
+}
+ </pre>
+
+ <p>If, however, all we want is to show the diagnostics to the user,
+ then the string returned by the <code>what()</code> function
+ will contain the error information for each failed position.
+ Here is what it might look like (using Oracle as an example):</p>
+
+ <pre class="terminal">
+multiple exceptions, 4 elements attempted, 2 failed:
+[0] object already persistent
+[3] 1: ORA-00001: unique constraint (ODB_TEST.person_last_i) violated
+ </pre>
+
+ <p>Some databases that currently are capable of bulk operations
+ (specifically, Oracle and SQL Server) return a total count of affected
+ rows rather than individual counts for each data set. This limitation
+ prevents ODB from being able to always determine which elements in the
+ batch haven't affected any rows and, for the update and erase operations,
+ translate this to the <code>object_not_persistent</code> exceptions. As a
+ result, if some elements in the batch haven't affected any rows and ODB
+ is unable to determine exactly which ones, it will mark all the elements
+ in this batch as "maybe not persistent". That is, it will insert
+ the <code>object_not_persistent</code> exception and set
+ the <code>maybe</code> flag for every position in the batch. The
+ diagnostics string returned by <code>what()</code> will also reflect this
+ situation, for example (assuming batch size of 3):</p>
+
+ <pre class="terminal">
+multiple exceptions, 4 elements attempted, 4 failed:
+[0-2] (some) object not persistent
+[3] object not persistent
+ </pre>
+
+ <p>The way to handle and recover from such "maybe failures" will have
+ to be application-specific. For example, for some applications the
+ fact that some objects no longer exist in the database when
+ performing bulk erase might be an ignorable error. If, however,
+ the application needs to determine exactly which elements in the batch
+ have failed, then a <code>load()</code> call will be required for each
+ element in the batch (or a query using a view to avoid loading all
+ the data members; <a href="#10">Chapter 10, "Views"</a>). This is also
+ something to keep in mind when selecting the batch size since for
+ larger sizes it will be more expensive (more loads to perform) to
+ handle such "maybe failures". If the failures are not uncommon, as
+ is the case, for example, when using optimistic concurrency, then
+ it may make sense to use a smaller batch.</p>
+
+ <p>The lookup operator (<code>operator[]</code>) returns <code>NULL</code>
+ if the element at this position has no exception. Note also that the
+ returned value is <code>value_type*</code> and not
+ <code>odb::exception*</code> in order to provide access to the
+ <code>maybe</code> flag discussed above.</p>
+
+ <p>The <code>multiple_exceptions</code> class also provides access
+ to the number of positions attempted (the <code>attempted()</code>
+ accessor) and failed (the <code>failed()</code> accessor). Note
+ that the failed count includes the "maybe failed" positions.</p>
+
+ <p>The <code>multiple_exceptions</code> exception can also be fatal.
+ If the <code>fatal()</code> accessor returns <code>true</code>, then
+ (some of) the exceptions were fatal. In this case, even for positions
+ that did not fail, no attempts were made to complete the operation
+ and the transaction must be aborted.</p>
+
+ <p>If <code>fatal()</code> returns false, then the operation on the
+ elements that don't have an exception has succeeded. The application
+ can ignore the errors or try to correct the errors and re-attempt
+ the operation on the elements that did fail. In either case, the
+ transaction can be committed.</p>
+
+ <p>An example of a fatal exception would be the situation where the
+ execution of the underlying statement failed summarily, without
+ attempting any data sets, for instance, because of an error in
+ the statement itself.</p>
+
+ <p>The <code>fatal()</code> modifier allows you to "upgrade" an
+ exception to fatal, for example, for specific database error
+ codes.</p>
+
+
+ <!-- PART -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="II">PART II&nbsp;&nbsp;
+ <span style="font-weight: normal;">DATABASE SYSTEMS</span></a></h1>
+
+ <p>Part II covers topics specific to the database system
+ 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
+ <code>database</code> classes as well as the default mapping
+ between basic C++ value types and native database types. Part
+ II consists of the following chapters.</p>
+
+ <table class="toc">
+ <tr><th>16</th><td><a href="#16">Multi-Database Support</a></td></tr>
+ <tr><th>17</th><td><a href="#17">MySQL Database</a></td></tr>
+ <tr><th>18</th><td><a href="#18">SQLite Database</a></td></tr>
+ <tr><th>19</th><td><a href="#19">PostgreSQL Database</a></td></tr>
+ <tr><th>20</th><td><a href="#20">Oracle Database</a></td></tr>
+ <tr><th>21</th><td><a href="#21">Microsoft SQL Server Database</a></td></tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="16">16 Multi-Database Support</a></h1>
+
+ <p>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.</p>
+
+ <p>ODB provides two types of multi-database support: <em>static</em>
+ and <em>dynamic</em>. With static support we use the
+ database system-specific interfaces to perform database
+ operations. That is, instead of using <code>odb::database</code>,
+ <code>odb::transaction</code>, or <code>odb::query</code>, we
+ would use, for example, <code>odb::sqlite::database</code>,
+ <code>odb::sqlite::transaction</code>, or
+ <code>odb::sqlite::query</code> to access an SQLite database.</p>
+
+ <p>In contrast, with <em>dynamic</em> 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 <code>database</code> instance being
+ used. In fact, this mechanism is very similar to C++ virtual
+ functions.</p>
+
+ <p>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.</p>
+
+ <p>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
+ <a href="#16.2">Section 16.2, "Dynamic Multi-Database Support"</a>
+ 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.</p>
+
+ <p>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.</p>
+
+ <p>By default ODB assumes single-database support. To enable
+ multi-database support we use the <code>--multi-database</code>
+ (or <code>-m</code>) ODB compiler option. This option is also used to
+ specify the support type: <code>static</code> or <code>dynamic</code>.
+ For example:</p>
+
+ <pre class="terminal">
+odb -m static ... person.hxx
+ </pre>
+
+ <p>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
+ <code>--database</code> (or <code>-d</code>) options or with multiple
+ ODB compiler invocations. Both approaches produce the same result,
+ for example:</p>
+
+ <pre class="terminal">
+odb -m static -d common -d sqlite -d pgsql person.hxx
+ </pre>
+
+ <p>Is equivalent to:</p>
+
+ <pre class="terminal">
+odb -m static -d common person.hxx
+odb -m static -d sqlite person.hxx
+odb -m static -d pgsql person.hxx
+ </pre>
+
+ <p>Notice that the first <code>-d</code> option has <code>common</code>
+ 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.</p>
+
+ <p>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 <code>person-odb.?xx</code> 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 <code>person-odb.?xx</code> set of files
+ contains the common code while the database system-specific code is
+ written to files in the form <code>person-odb-&lt;db>.?xx</code>.
+ That is, <code>person-odb-sqlite.?xx</code> for SQLite,
+ <code>person-odb-pgsql.?xx</code> for PostgreSQL, etc.</p>
+
+ <p>If we need dynamic support for some databases and static for
+ others, then the <code>common</code> 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:</p>
+
+ <pre class="terminal">
+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
+ </pre>
+
+ <p>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:</p>
+
+ <pre class="cxx">
+#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_)
+};
+ </pre>
+
+ <p>Above, the pragma for the <code>name_</code> data member shows the
+ use of a database prefix (for example, <code>pgsql:</code>) that
+ only applies to the specifier that follows. The pragma that defines
+ an index on the <code>age_</code> 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 <code>db</code> keyword.</p>
+
+
+ <p>Similar to pragmas, ODB compiler options that determine the kind
+ (for example, <code>--schema-format</code>), names (for example,
+ <code>--odb-file-suffix</code>), or content (for example, prologue
+ and epilogue options) of the output files can be prefixed with the
+ database name. For example:</p>
+
+ <pre class="terminal">
+odb --odb-file-suffix common:-odb-common ...
+ </pre>
+
+ <p>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, <code>point</code>) can
+ be mapped to a simple value in one database (for example, to the
+ <code>POINT</code> PostgreSQL type) and to a composite value
+ in another (for example, in SQLite, which does not have a
+ built-in point type).</p>
+
+ <p>The following sections discuss static and dynamic multi-database
+ support in more detail.</p>
+
+
+ <h2><a name="16.1">16.1 Static Multi-Database Support</a></h2>
+
+ <p>With static multi-database support, instead of including
+ <code>person-odb.hxx</code>, application source code has
+ to include <code>person-odb-&lt;db>.hxx</code> header files
+ corresponding to the database systems that will be used.</p>
+
+ <p>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 <code>odb</code> namespace.</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+
+odb::database&amp; db = ...
+
+typedef odb::query&lt;person> query;
+typedef odb::result&lt;person> result;
+
+odb::transaction t (db.begin ());
+result r (db.query&lt;person> (query::age &lt; 30));
+...
+t.commit ();
+ </pre>
+
+ <p>In an application that employs static multi-database support
+ the same transaction for SQLite would be rewritten like this:</p>
+
+ <pre class="cxx">
+#include "person-odb-sqlite.hxx"
+
+odb::sqlite::database&amp; db = ...
+
+typedef odb::sqlite::query&lt;person> query;
+typedef odb::result&lt;person> result; // odb:: not odb::sqlite::
+
+odb::sqlite::transaction t (db.begin ());
+result r (db.query&lt;person> (query::age &lt; 30));
+...
+t.commit ();
+ </pre>
+
+ <p>That is, the <code>database</code>, <code>transaction</code>, and
+ <code>query</code> classes now come from the <code>odb::sqlite</code>
+ namespace instead of <code>odb</code>. Other classes that have
+ database system-specific interfaces are <code>connection</code>,
+ <code>statement</code>, and <code>tracer</code>. Note that
+ all of them derive from the corresponding common versions. It
+ is also possible to use common <code>transaction</code>,
+ <code>connection</code>, and <code>statement</code> classes
+ with static support, if desired.</p>
+
+ <p>Notice that we didn't use the <code>odb::sqlite</code> namespace
+ for the <code>result</code> class template. This is because
+ <code>result</code> is database system-independent. All other
+ classes defined in namespace <code>odb</code>, except those
+ specifically mentioned above, are database system-independent.
+ In particular, <code>result</code>, <code>prepared_query</code>,
+ <code>session</code>, <code>schema_catalog</code>, and all the
+ exceptions are database system-independent.</p>
+
+ <p>Writing <code>odb::sqlite::</code> 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
+ <code>using namespace</code> directive to avoid qualifying
+ each name. For example:</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+
+odb::database&amp; db = ...
+
+{
+ using namespace odb::core;
+
+ typedef query&lt;person> person_query;
+ typedef result&lt;person> person_result;
+
+ transaction t (db.begin ());
+ person_result r (db.query&lt;person> (person_query::age &lt; 30));
+ ...
+ t.commit ();
+}
+ </pre>
+
+ <p>A similar mechanism is available in multi-database support. Each
+ database runtime defines the <code>odb::&lt;db>::core</code>
+ 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:</p>
+
+ <pre class="cxx">
+#include "person-odb-sqlite.hxx"
+
+odb::sqlite::database&amp; db = ...
+
+{
+ using namespace odb::sqlite::core;
+
+ typedef query&lt;person> person_query;
+ typedef result&lt;person> person_result;
+
+ transaction t (db.begin ());
+ person_result r (db.query&lt;person> (person_query::age &lt; 30));
+ ...
+ t.commit ();
+}
+ </pre>
+
+ <p>If the <code>using namespace</code> 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:</p>
+
+ <pre class="cxx">
+#include "person-odb-pgsql.hxx"
+#include "person-odb-sqlite.hxx"
+
+namespace pg = odb::pgsql;
+namespace sl = odb::sqlite;
+
+pg::database&amp; pg_db = ...
+sl::database&amp; sl_db = ...
+
+typedef pg::query&lt;person> pg_query;
+typedef sl::query&lt;person> sl_query;
+typedef odb::result&lt;person> result;
+
+// First check the local cache.
+//
+odb::transaction t (sl_db.begin ()); // Note: using common transaction.
+result r (sl_db.query&lt;person> (sl_query::age &lt; 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&lt;person> (pg_query::age &lt; 30);
+}
+
+// Handle the result.
+//
+...
+
+t.commit ();
+ </pre>
+
+ <p>With static multi-database support we can make one of the databases
+ the default database with the <code>--default-database</code> option.
+ The default database can be accessed via the common interface, just
+ like with single-database support. For example:</p>
+
+ <pre class="terminal">
+odb -m static -d common -d pgsql -d sqlite --default-database pgsql ...
+ </pre>
+
+ <p>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.</p>
+
+ <h2><a name="16.2">16.2 Dynamic Multi-Database Support</a></h2>
+
+ <p>With dynamic multi-database support, application source code only
+ needs to include the <code>person-odb.hxx</code> header file, just
+ like with single-database support. In particular, we don't need
+ to include any of the <code>person-odb-&lt;db>.hxx</code> files
+ unless we would also like to use certain database systems in the
+ static multi-database mode.</p>
+
+ <p>When performing database operations, the application code
+ uses the common interfaces from the <code>odb</code> 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):</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+
+std::unique_ptr&lt;person>
+load (odb::database&amp; db, const std::string&amp; name)
+{
+ odb::transaction t (db.begin ());
+ std::unique_ptr&lt;person> p (db.find (name));
+ t.commit ();
+ return p;
+}
+
+odb::pgsql::database&amp; pg_db = ...
+odb::sqlite::database&amp; sl_db = ...
+
+// First try the local cache.
+//
+std::unique_ptr&lt;person> p (load (sl_db, "John Doe"));
+
+// If not found, try the remote database.
+//
+if (p == 0)
+ p = load (pg_db, "John Doe");
+
+...
+ </pre>
+
+ <p>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 <code>odb::database</code> or a database system-specific
+ (for example, <code>odb::sqlite::database</code>) 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 <code>person-odb-&lt;db>.hxx</code>
+ 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.</p>
+
+ <p>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 <code>query</code> type implements a thin
+ wrapper around the underlying database system's <code>SELECT</code>
+ statement. With dynamic multi-database support, because the
+ underlying database system is only known at query execution
+ (or preparation) time, the <code>query</code> 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, <code>char[256]</code>) can only be bound
+ by-reference.</p>
+
+ <p>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
+ <code>LIMIT</code> 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:</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+#include "person-odb-pgsql.hxx" // Needed for static mode.
+
+void
+print (odb::database&amp; db, unsigned short age, unsigned long limit)
+{
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;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&lt;person> pg_query;
+
+ pg::database&amp; pg_db (static_cast&lt;pg::database&amp;> (db));
+ pg_query pg_q (pg_query (q) + "LIMIT" + pg_query::_val (limit));
+ r = pg_db.query&lt;person> (pg_q);
+ }
+ else
+ r = db.query&lt;person> (q);
+
+ // Handle the result up to the limit elements.
+ //
+ ...
+
+ t.commit ();
+}
+
+odb::pgsql::database&amp; pg_db = ...
+odb::sqlite::database&amp; sl_db = ...
+
+print (sl_db, 30, 100);
+print (sl_db, 30, 100);
+ </pre>
+
+ <p>A few things to note about this example. First, we use the
+ <code>database::id()</code> function to determine the actual database
+ system we use. This function has the following signature:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ enum database_id
+ {
+ id_mysql,
+ id_sqlite,
+ id_pgsql,
+ id_oracle,
+ id_mssql,
+ id_common
+ };
+
+ class database
+ {
+ public:
+ ...
+
+ database_id
+ id () const;
+ }
+}
+ </pre>
+
+ <p>Note that <code>database::id()</code> can never return the
+ <code>id_common</code> value.</p>
+
+ <p>The other thing to note is how we translate the dynamic query
+ to the database system-specific one (the <code>pg_query (q)</code>
+ expression). Every <code>odb::&lt;db>::query</code> class provides
+ such a translation constructor.</p>
+
+ <h3><a name="16.2.2">16.2.2 Dynamic Loading of Database Support Code</a></h3>
+
+ <p>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 <code>LoadLibrary()</code> or
+ POSIX <code>dlopen()</code>. 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.</p>
+
+ <p>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 <code>GetProcAddress()</code> (Win32) or <code>dlsym()</code>
+ (Unix) function.</p>
+
+ <p>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.</p>
+
+ <p>An important point to understand is that we only need to export
+ the common interface, that is, the classes defined in the
+ <code>person-odb.hxx</code> header. In particular, we don't need
+ to export the database system-specific classes defined in
+ the <code>person-odb-&lt;db>.hxx</code>, 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).</p>
+
+ <p>The ODB compiler provides two command line options,
+ <code>--export-symbol</code> and <code>--extern-symbol</code>,
+ 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 <code>extern</code> 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 <code>person-export.hxx</code>,
+ could look like this:</p>
+
+ <pre class="cxx">
+// 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
+ </pre>
+
+ <p>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 <code>-fvisibility=hidden</code> option to
+ make them hidden by default.</p>
+
+ <pre class="cxx">
+// 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
+ </pre>
+
+ <p>Next we need to export the <code>person</code> persistent class
+ using the export macro and re-compile our <code>person.hxx</code> file
+ with the <code>--export-symbol</code> and <code>--extern-symbol</code>
+ options. We will also need to include <code>person-export.hxx</code>
+ into the generated <code>person-odb.hxx</code> file. For that we use
+ the <code>--hxx-prologue</code> option. Here is how we can do
+ this with multiple invocations of the ODB compiler:</p>
+
+ <pre class="terminal">
+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
+ </pre>
+
+ <p>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
+ <code>common</code> database:</p>
+
+ <pre class="terminal">
+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
+ </pre>
+
+ <p>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 <code>*.dll</code> with <code>lib*.so</code> for
+ Unix): <code>person.dll</code> plus <code>person-sqlite.dll</code> and
+ <code>person-pgsql.dll</code>, which both link to <code>person.dll</code>,
+ as well as <code>person.exe</code>, which links to <code>person.dll</code>
+ and dynamically loads <code>person-sqlite.dll</code>
+ and/or <code>person-pgsql.dll</code>. 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 <code>person-odb.cxx</code>
+ and <code>person.cxx</code> into <code>person.dll</code>,
+ <code>person-odb-sqlite.cxx</code> into <code>person-sqlite.dll</code>,
+ etc. Note that in the pure dynamic multi-database support,
+ <code>person-sqlite.dll</code> and <code>person-pgsql.dll</code>
+ do not export any symbols.</p>
+
+ <p>We can improve on the above organization by getting rid of
+ <code>person.dll</code>, which is not really necessary unless
+ we have multiple executables sharing the same database support.
+ To achieve this, we will place <code>person-odb.cxx</code> into
+ <code>person.exe</code> 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.</p>
+
+ <p>On Windows all we have to do is place <code>person-odb.cxx</code>
+ into the executable and compile it as we would in a DLL (that is,
+ with the <code>PERSON_BUILD_DLL</code> macro defined). If Windows
+ linker detects that an executable exports any symbols, then it
+ will automatically create the corresponding import library
+ (<code>person.lib</code> in our case). We then use this import
+ library to build <code>person-sqlite.dll</code> and
+ <code>person-pgsql.dll</code> as before.</p>
+
+ <p>To export symbols from an executable on GNU/Linux all we need to
+ do is add the <code>-rdynamic</code> option when linking our
+ executable.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="17">17 MySQL Database</a></h1>
+
+ <p>To generate support code for the MySQL database you will need
+ to pass the "<code>--database&nbsp;mysql</code>"
+ (or "<code>-d&nbsp;mysql</code>") option to the ODB compiler.
+ Your application will also need to link to the MySQL ODB runtime
+ library (<code>libodb-mysql</code>). All MySQL-specific ODB
+ classes are defined in the <code>odb::mysql</code> namespace.</p>
+
+ <h2><a name="17.1">17.1 MySQL Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and MySQL database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>TINYINT(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>TINYINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>TINYINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>SMALLINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>BIGINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>BIGINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>FLOAT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>DOUBLE</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>TEXT/VARCHAR(128)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>TINYINT</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>Note that the <code>std::string</code> type is mapped
+ differently depending on whether a member of this type
+ is an object id or not. If the member is an object id,
+ then for this member <code>std::string</code> is mapped
+ to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
+ it is mapped to <code>TEXT</code>.</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to suitable MySQL types. Contiguous
+ enumerations with the zero first enumerator are mapped to
+ the MySQL <code>ENUM</code> type. All other enumerations
+ are mapped to the MySQL types corresponding to their
+ underlying integral types (see table above). In both
+ cases the default <code>NULL</code> semantics is
+ <code>NOT NULL</code>. For example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1, // Non-zero first enumerator.
+ sweet,
+ sour = 4, // Non-contiguous.
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Mapped to ENUM ('red', 'green', 'blue') NOT NULL.
+ taste taste_; // Mapped to TINYNT UNSIGNED NOT NULL.
+};
+ </pre>
+
+ <p>The only built-in mapping provided for the MySQL <code>DECIMAL</code>
+ type is to <code>std::string/char[N]</code>, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type ("DECIMAL(6,3)")
+ std::string value_;
+};
+ </pre>
+
+ <p>You can, however, map <code>DECIMAL</code> to a custom C++ type by
+ providing a suitable <code>odb::mysql::value_traits</code>
+ specialization.</p>
+
+ <p>It is also possible to add support for additional MySQL types,
+ such as geospatial types. For more information, refer to
+ <a href="#14.8">Section 14.8, "Database Type Mapping
+ Pragmas"</a>.</p>
+
+ <h3><a name="17.1.1">17.1.1 String Type Mapping</a></h3>
+
+ <p>The MySQL ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the MySQL <code>CHAR</code>,
+ <code>VARCHAR</code>, <code>TEXT</code>, <code>NCHAR</code>, and
+ <code>NVARCHAR</code> types. However, these mappings are not enabled
+ by default (in particular, by default, <code>std::array</code> will
+ be treated as a container). To enable the alternative mappings for
+ these types we need to specify the database type explicitly using
+ the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
+ 14.4.3, "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("CHAR(2)")
+ char state_[2];
+
+ #pragma db type("VARCHAR(128)")
+ std::string name_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("VARCHAR(128)")
+
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_; // Mapped to VARCHAR(128).
+};
+ </pre>
+
+ <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
+ may or may not be zero-terminated. When extracting such values from the
+ database, ODB will append the zero terminator if there is enough
+ space.</p>
+
+ <h3><a name="17.1.2">17.1.2 Binary Type Mapping</a></h3>
+
+ <p>The MySQL ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code>
+ types to the MySQL <code>BINARY</code>, <code>VARBINARY</code>,
+ and <code>BLOB</code> types. However, these mappings are not enabled
+ by default (in particular, by default, <code>std::vector</code> and
+ <code>std::array</code> will be treated as containers). To enable the
+ alternative mappings for these types we need to specify the database
+ type explicitly using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("BLOB")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BINARY(16)")
+ unsigned char uuid_[16];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BLOB")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BLOB.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::mysql::id_blob> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h2><a name="17.2">17.2 MySQL Database Class</a></h2>
+
+ <p>The MySQL <code>database</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const char* user,
+ const char* passwd,
+ const char* db,
+ const char* host = 0,
+ unsigned int port = 0,
+ const char* socket = 0,
+ const char* charset = 0,
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; passwd,
+ const std::string&amp; db,
+ const std::string&amp; host = "",
+ unsigned int port = 0,
+ const std::string* socket = 0,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string* passwd,
+ const std::string&amp; db,
+ const std::string&amp; host = "",
+ unsigned int port = 0,
+ const std::string* socket = 0,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; passwd,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ unsigned int port,
+ const std::string&amp; socket,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string* passwd,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ unsigned int port,
+ const std::string&amp; socket,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const char*
+ user () const;
+
+ const char*
+ password () const;
+
+ const char*
+ db () const;
+
+ const char*
+ host () const;
+
+ unsigned int
+ port () const;
+
+ const char*
+ socket () const;
+
+ const char*
+ charset () const;
+
+ unsigned long
+ client_flags () const;
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mysql/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us
+ to specify MySQL database parameters that should be used when
+ connecting to the database. In MySQL <code>NULL</code> and an
+ empty string are treated as the same values for all the
+ string parameters except <code>password</code> and
+ <code>socket</code>.</p>
+
+ <p>The <code>charset</code> argument allows us to specify the client
+ character set, that is, the character set in which the application
+ will encode its text data. Note that this can be different from
+ the MySQL server character set. If this argument is not specified or
+ is empty, then the default MySQL client character set is used, normally
+ <code>latin1</code>. Commonly used values for this argument are
+ <code>latin1</code> (equivalent to Windows cp1252 and similar to
+ ISO-8859-1) and <code>utf8</code>. For other possible values
+ as well as more information on character set support in MySQL,
+ refer to the MySQL documentation.</p>
+
+ <p>The <code>client_flags</code> argument allows us to specify various
+ MySQL client library flags. For more information on the possible
+ values, refer to the MySQL C API documentation. The
+ <code>CLIENT_FOUND_ROWS</code> flag is always set by the MySQL ODB
+ runtime regardless of whether it was passed in the
+ <code>client_flags</code> argument.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user &lt;login>
+ --password &lt;password>
+ --database &lt;name>
+ --host &lt;host>
+ --port &lt;integer>
+ --socket &lt;socket>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the MySQL
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::mysql::cli_exception</code>
+ exception if the MySQL option values are missing or invalid.
+ See section <a href="#17.4">Section 17.4, "MySQL Exceptions"</a>
+ for more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ MySQL database connection encapsulated by the
+ <code>odb::mysql::connection</code> class. For more information
+ on <code>mysql::connection</code>, refer to <a href="#17.3">Section
+ 17.3, "MySQL Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="17.3">17.3 MySQL Connection and Connection Factory</a></h2>
+
+ <p>The <code>mysql::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, MYSQL*);
+
+ MYSQL*
+ handle ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface,
+ refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
+ overloaded <code>mysql::connection</code> constructor establishes a
+ new MySQL connection. The second constructor allows us to create
+ a <code>connection</code> instance by providing an already connected
+ native MySQL handle. Note that the <code>connection</code>
+ instance assumes ownership of this handle. The <code>handle()</code>
+ accessor returns the MySQL handle corresponding to the connection.</p>
+
+ <p>The <code>mysql::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::mysql::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the MySQL ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/mysql/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0,
+ bool ping = true);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, MYSQL*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open. The <code>ping</code> argument specifies
+ whether the factory should validate the connection before
+ returning it to the caller.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ then the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>Connection validation (the <code>ping</code> argument) is useful
+ if your application may experience long periods of inactivity. In
+ such cases the MySQL server may close network connections that have
+ been inactive for too long. If during connection validation the pool
+ factory detects that the connection has been terminated, it silently
+ closes it and tries to find or create another connection instead.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to
+ one of the <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be
+ created by default with the min and max connections values
+ set to <code>0</code> and connection validation enabled.
+ The following code fragment shows how we can pass our own
+ connection factory instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/mysql/database.hxx>
+#include &lt;odb/mysql/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::mysql::connection_factory> f (
+ new odb::mysql::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new mysql::database (argc, argv, false, 0, f));
+}
+ </pre>
+
+ <h2><a name="17.4">17.4 MySQL Exceptions</a></h2>
+
+ <p>The MySQL ODB runtime library defines the following MySQL-specific
+ exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ unsigned int
+ error () const;
+
+ const std::string&amp;
+ sqlstate () const;
+
+ const std::string&amp;
+ message () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mysql/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::mysql::database_exception</code> is thrown if
+ a MySQL database operation fails. The MySQL-specific error
+ information is accessible via the <code>error()</code>,
+ <code>sqlstate()</code>, and <code>message()</code> functions.
+ All this information is also combined and returned in a
+ human-readable form by the <code>what()</code> function.</p>
+
+ <p>The <code>odb::mysql::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::mysql::database</code>
+ class if the MySQL option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <h2><a name="17.5">17.5 MySQL Limitations</a></h2>
+
+ <p>The following sections describe MySQL-specific limitations imposed
+ by the current MySQL and ODB runtime versions.</p>
+
+ <h3><a name="17.5.1">17.5.1 Foreign Key Constraints</a></h3>
+
+ <p>ODB relies on standard SQL behavior which requires that foreign
+ key constraints checking is deferred until the transaction is
+ committed. The only behaviors supported by MySQL are to either
+ check such constraints immediately (InnoDB engine) or to ignore
+ foreign key constraints altogether (all other engines). As a
+ result, by default, schemas generated by the ODB compiler for
+ MySQL have foreign key definitions commented out. They are
+ retained only for documentation.</p>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h2><a name="17.6">17.6 MySQL Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define a MySQL index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>, <code>FULLTEXT</code>, <code>SPATIAL</code>),
+ the <code>method</code> clause specifies the index method (for
+ example, <code>BTREE</code>, <code>HASH</code>), and the
+ <code>options</code> clause is not used. The column options
+ can be used to specify column length limits and the sort order.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index method("HASH") member(name_, "(100) DESC")
+};
+ </pre>
+
+ <h2><a name="17.7">17.7 MySQL Stored Procedures</a></h2>
+
+ <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
+ can be used to call MySQL stored procedures. For example, assuming
+ we are using the <code>person</code> class from <a href="#2">Chapter
+ 2, "Hello World Example"</a> (and the corresponding <code>person</code>
+ table), we can create a stored procedure that given the min and max
+ ages returns some information about all the people in that range:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE person_range (
+ IN min_age SMALLINT,
+ IN max_age SMALLINT)
+BEGIN
+ SELECT age, first, last FROM person
+ WHERE age >= min_age AND age &lt;= max_age;
+END
+ </pre>
+
+ <p>Given the above stored procedure we can then define an ODB view
+ that can be used to call it and retrieve its result:</p>
+
+ <pre class="cxx">
+#pragma db view query("CALL person_range((?))")
+struct person_range
+{
+ unsigned short age;
+ std::string first;
+ std::string last;
+};
+ </pre>
+
+ <p>The following example shows how we can use the above view to
+ print the list of people in a specific age range:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person_range> query;
+typedef odb::result&lt;person_range> result;
+
+transaction t (db.begin ());
+
+result r (
+ db.query&lt;person_range> (
+ query::_val (1) + "," + query::_val (18)));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Note that as with all native views, the order and types of data members
+ must match those of columns in the <code>SELECT</code> list inside
+ the stored procedure.</p>
+
+ <p>There are also a number of limitations when it comes to support for
+ MySQL stored procedures in ODB views. First of all, you have to use
+ MySQL server and client libraries version 5.5.3 or later since this
+ is the version in which support for calling stored procedures with
+ prepared statements was first added (the
+ <code>mysql_stmt_next_result()</code> function).</p>
+
+ <p>In MySQL, a stored procedure can produce multiple results.
+ For example, if a stored procedure executes several
+ <code>SELECT</code> statements, then the result of calling such
+ a procedure consists of two row sets, one for each <code>SELECT</code>
+ statement. Additionally, if the procedure has any <code>OUT</code>
+ or <code>INOUT</code> parameters, then their values are returned as
+ an additional special row set containing only a single row.
+ Because such multiple row sets can contain varying number
+ and type of columns, they cannot be all extracted into a
+ single view. As a result, an ODB view will only extract the
+ data from the first row set and ignore all the subsequent
+ ones.</p>
+
+ <p>In particular, this means that we can use an ODB view to extract
+ the values of the <code>OUT</code> and <code>INOUT</code>
+ parameters provided that the stored procedure does not generate
+ any other row sets. For example:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE person_min_max_age (
+ OUT min_age SMALLINT,
+ OUT max_age SMALLINT)
+BEGIN
+ SELECT MIN(age), MAX(age) INTO min_age, max_age FROM person;
+END
+ </pre>
+
+ <pre class="cxx">
+#pragma db view query("CALL person_min_max_age((?))")
+struct person_min_max_age
+{
+ unsigned short min_age;
+ unsigned short max_age;
+};
+ </pre>
+
+ <pre class="cxx">
+typedef odb::query&lt;person_min_max_age> query;
+
+transaction t (db.begin ());
+
+// We know this query always returns a single row, so use query_value().
+// We have to pass dummy values for OUT parameters.
+//
+person_min_max_age mma (
+ db.query_value&lt;person_min_max_age> (
+ query::_val (0) + "," + query::_val (0)));
+
+cerr &lt;&lt; mma.min_age &lt;&lt; " " &lt;&lt; mma.max_age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Another limitation that stems from having multiple results is the
+ inability to cache the result of a stored procedure call. In
+ other words, a MySQL stored procedure call always produces an
+ uncached query result (<a href="#4.4">Section 4.4, "Query
+ Result"</a>).</p>
+
+ <hr class="page-break"/>
+ <h1><a name="18">18 SQLite Database</a></h1>
+
+ <p>To generate support code for the SQLite database you will need
+ to pass the "<code>--database&nbsp;sqlite</code>"
+ (or "<code>-d&nbsp;sqlite</code>") option to the ODB compiler.
+ Your application will also need to link to the SQLite ODB runtime
+ library (<code>libodb-sqlite</code>). All SQLite-specific ODB
+ classes are defined in the <code>odb::sqlite</code> namespace.</p>
+
+ <h2><a name="18.1">18.1 SQLite Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and SQLite database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::wstring (Windows only)</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>wchar_t[N] (Windows only)</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>odb::sqlite::text</code></td>
+ <td><code>TEXT (STREAM)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>odb::sqlite::blob</code></td>
+ <td><code>BLOB (STREAM)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to the
+ <code>INTEGER</code> SQLite type using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
+
+ <p>SQLite represents the <code>NaN</code> <code>FLOAT</code> value
+ as a <code>NULL</code> value. As a result, columns of the
+ <code>float</code> and <code>double</code> types are by default
+ declared as <code>NULL</code>. However, you can override this by
+ explicitly declaring them as <code>NOT NULL</code> with the
+ <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null/not_null</code>"</a>).</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the SQLite <code>INTEGER</code> type with
+ the default <code>NULL</code> semantics being <code>NOT NULL</code>.
+ For example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to INTEGER.
+ taste taste_; // Automatically mapped to INTEGER.
+};
+ </pre>
+
+ <p>Note also that SQLite only operates with signed integers and the largest
+ value that an SQLite database can store is a signed 64-bit integer. As
+ a result, greater <code>unsigned&nbsp;long</code> and
+ <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="#14.8">Section 14.8, "Database Type Mapping
+ Pragmas"</a>.</p>
+
+ <h3><a name="18.1.1">18.1.1 String Type Mapping</a></h3>
+
+ <p>The SQLite ODB runtime library provides support for mapping the
+ <code>std::array&lt;char, N></code> and, on Windows,
+ <code>std::array&lt;wchar_t, N></code> types to the SQLite
+ <code>TEXT</code> type. However, this mapping is not enabled by
+ default (in particular, by default, <code>std::array</code> will
+ be treated as a container). To enable the alternative mapping for
+ this type we need to specify the database type explicitly using
+ the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
+ 14.4.3, "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("TEXT")
+ std::array&lt;char, 128> name_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::array&lt;char, 128> name_type;
+#pragma db value(name_type) type("TEXT")
+
+#pragma db object
+class object
+{
+ ...
+
+ name_type name_; // Mapped to TEXT.
+};
+ </pre>
+
+ <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
+ <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
+ values may or may not be zero-terminated. When extracting such values
+ from the database, ODB will append the zero terminator if there is
+ enough space.</p>
+
+ <h3><a name="18.1.2">18.1.2 Binary Type Mapping</a></h3>
+
+ <p>The SQLite ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code>
+ types to the SQLite <code>BLOB</code> type. However, these mappings
+ are not enabled by default (in particular, by default,
+ <code>std::vector</code> and <code>std::array</code> will be treated
+ as containers). To enable the alternative mappings for these types
+ we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("BLOB")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BLOB")
+ unsigned char uuid_[16];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BLOB")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BLOB.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::sqlite::id_blob> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h3><a name="18.1.3">18.1.3 Incremental <code>BLOB</code>/<code>TEXT</code> I/O</a></h3>
+
+ <p>This section describes the SQLite ODB runtime library support for
+ incremental reading and writing of <code>BLOB</code> and
+ <code>TEXT</code> values. The provided API is a thin wrapper
+ around the native SQLite <code>sqlite3_blob_*()</code> function
+ family. As a result, it is highly recommended that you familiarize
+ yourself with the semantics of this SQLite functionality before
+ continuing with this section.</p>
+
+ <p>The SQLite runtime provides the <code>blob</code> and
+ <code>text</code> types that can be used to represent
+ <code>BLOB</code> and <code>TEXT</code> data members
+ that will be read/written using the incremental I/O.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/sqlite/blob.hxx>
+#include &lt;odb/sqlite/text.hxx>
+
+#pragma db object
+class object
+{
+public
+ #pragma db id auto
+ unsigned long id;
+
+ odb::sqlite::blob b; // Mapped to BLOB.
+ odb::sqlite::text t; // Mapped to TEXT.
+};
+ </pre>
+
+ <p>The <code>blob</code> and <code>text</code> types should be
+ viewed as <em>descriptors</em> of the <code>BLOB</code> and
+ <code>TEXT</code> values (rather than the values themselves)
+ that can be used to <em>open</em> the values for reading or
+ writing. These two types have an identical interface that
+ is presented below. Notice that it is essentially the list
+ of arguments (except for <code>size</code> which is discussed
+ below) to the <code>sqlite3_blob_open()</code> function:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class blob|text
+ {
+ public:
+ explicit
+ blob|text (std::size_t = 0);
+
+ std::size_t size ()
+ void size (std::size_t);
+
+ const std::string&amp; db () const;
+ const std::string&amp; table () const;
+ const std::string&amp; column () const;
+ long long rowid () const;
+
+ void
+ clear ();
+ };
+ }
+}
+ </pre>
+
+ <p>To read/write data from/to a incremental <code>BLOB</code> or
+ <code>TEXT</code> value we use the corresponding
+ <code>blob_stream</code> and <code>text_stream</code>
+ stream types. Their interfaces closely mimic the
+ underlying <code>sqlite3_blob_*()</code> functions
+ and are presented below. Note that in order to create
+ a stream we have to pass the corresponding descriptor:</p>
+
+ <pre class="cxx">
+#include &lt;odb/sqlite/stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class stream
+ {
+ public:
+ stream (const char* db,
+ const char* table,
+ const char* column,
+ long long rowid,
+ bool rw);
+
+ std::size_t
+ size () const;
+
+ // The following two functions throw std::invalid_argument if
+ // offset + n is past size().
+ //
+ void
+ read (void* buf, std::size_t n, std::size_t offset = 0);
+
+ void
+ write (const void* buf, std::size_t n, std::size_t offset = 0);
+
+ sqlite3_blob*
+ handle () const;
+
+ // Close without reporting errors, if any.
+ //
+ ~stream ();
+
+ // Close with reporting errors, if any.
+ //
+ void
+ close ();
+
+ // Open the same BLOB but in a different row. Can be faster
+ // than creating a new stream instance. Note that the stream
+ // must be in the open state prior to calling this function.
+ //
+ void
+ reopen (long long rowid);
+ };
+ }
+}
+
+#include &lt;odb/sqlite/blob-stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class blob_stream: public stream
+ {
+ public:
+ blob_stream (const blob&amp;, bool rw);
+ };
+ }
+}
+
+#include &lt;odb/sqlite/text-stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class text_stream: public stream
+ {
+ public:
+ text_stream (const text&amp;, bool rw);
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>rw</code> argument to the constructors above
+ specifies whether to open the value for reading only
+ (<code>false</code>) or to read and write
+ (<code>true</code>).</p>
+
+ <p>In SQLite the incremental <code>BLOB</code> and
+ <code>TEXT</code> sizes are fixed in the sense that
+ they must be specified before the object is persisted
+ or updated and the following write operations can
+ only write that much data. This is what the <code>size</code>
+ data member in the descriptors is for. You can also determine
+ the size of the opened value, for both reading and writing,
+ using the <code>size()</code> stream function. The
+ following example puts all of this together:</p>
+
+ <pre class="cxx">
+#include &lt;odb/sqlite/blob-stream.hxx>
+#include &lt;odb/sqlite/text-stream.hxx>
+
+string txt (1024 * 1024, 't');
+vector&lt;char> blb (1024 * 1024, 'b');
+
+object o;
+
+// Persist.
+//
+{
+ transaction tx (db.begin ());
+
+ // Specify the sizes of the values before calling persist().
+ //
+ o.t.size (txt.size ());
+ o.b.size (blb.size ());
+
+ db.persist (o);
+
+ // Write the data.
+ //
+ blob_stream bs (o.b, true); // Open for read/write.
+ assert (bs.size () == blb.size ());
+ bs.write (blb.data (), blb.size ());
+
+ text_stream ts (o.t, true); // Open for read/write.
+ assert (ts.size () == txt.size ());
+ ts.write (txt.data (), txt.size ());
+
+ tx.commit ();
+}
+
+// Load.
+//
+{
+ transaction tx (db.begin ());
+ auto_ptr&lt;object> p (db.load&lt;object> (o.id));
+
+ text_stream ts (p->t, false); // Open for reading.
+ vector&lt;char> t (ts.size () + 1, '\0');
+ ts.read (t.data (), t.size () - 1);
+ assert (string (t.data ()) == txt);
+
+ blob_stream bs (p->b, false); // Open for reading.
+ vector&lt;char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+
+ tx.commit ();
+}
+
+// Update
+//
+txt.resize (txt.size () + 1, 't');
+txt[0] = 'A';
+txt[txt.size () - 1] = 'Z';
+
+blb.resize (blb.size () - 1);
+blb.front () = 'A';
+blb.back () = 'Z';
+
+{
+ transaction tx (db.begin ());
+
+ // Specify the new sizes of the values before calling update().
+ //
+ o.t.size (txt.size ());
+ o.b.size (blb.size ());
+
+ db.update (o);
+
+ // Write the data.
+ //
+ blob_stream bs (o.b, true);
+ bs.write (blb.data (), blb.size ());
+
+ text_stream ts (o.t, true);
+ ts.write (txt.data (), txt.size ());
+
+ tx.commit ();
+}
+ </pre>
+
+ <p>For the most part, the incremental <code>BLOB</code> and
+ <code>TEXT</code> descriptors can be used as any other
+ simple values. Specifically, they can be used as container
+ elements (<a href="#5">Chapter 5, "Containers"</a>), as
+ <code>NULL</code>-able values (<a href="#7.3">Section 7.3,
+ "Pointers and NULL Value Semantics"</a>), and in views
+ (<a href="#10">Chapter 10, "Views"</a>). The following
+ example illustrates the use within a view:</p>
+
+ <pre class="cxx">
+#pragma db view object(object)
+struct load_b
+{
+ odb::sqlite::blob b;
+};
+
+typedef odb::query&lt;load_b> query;
+
+transaction tx (db.begin ());
+
+for (load_b&amp; lb: db.query&lt;load_b> (query::t == "test"))
+{
+ blob_stream bs (lb.b, false);
+ vector&lt;char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+}
+
+tx.commit ();
+ </pre>
+
+ <p>However, being a low-level, SQLite-specific mechanism, the
+ incremental I/O has a number of nuances that should be kept in
+ mind. Firstly, the streams should be opened within a transaction
+ and, unless already closed, they will be automatically closed
+ when the transaction is committed or rolled back. The following
+ modification of the previous example helps to illustrate this
+ point:</p>
+
+ <pre class="cxx">
+{
+ transaction tx (db.begin ());
+
+ // ...
+
+ db.persist (o);
+
+ blob_stream bs (o.b, true);
+
+ tx.commit ();
+
+ // ERROR: stream is closed.
+ //
+ bs.write (blb.data (), blb.size ());
+}
+
+// ERROR: not in transaction.
+//
+text_stream ts (o.t, true);
+ </pre>
+
+ <p>Because loading an object with an incremental <code>BLOB</code> or
+ <code>TEXT</code> value involves additional actions after the
+ database function returns (that is, reading the actual data),
+ certain commonly-expected "round-trip" assumptions will no
+ longer hold unless special steps are taken, for instance
+ (again, continuing with our example):</p>
+
+ <pre class="cxx">
+transaction tx (db.begin ());
+
+auto_ptr&lt;object> p (db.load&lt;object> (o.id));
+p->name = "foo"; // Update some other member.
+db.update (*p); // Bad behavior: incremental BLOB/TEXT invalidated.
+
+tx.commit ();
+ </pre>
+
+ <p>One way to restore the expected behavior is to place the
+ incremental <code>BLOB</code> and <code>TEXT</code> values
+ into their own, separately loaded/updated sections
+ (<a href="#9">Chapter 9, "Sections"</a>). The alternative
+ approach would be to perform the incremental I/O as part
+ of the database operation <code>post_*</code> callbacks
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>).</p>
+
+ <p>Finally, note that when using incremental <code>TEXT</code>
+ values, the data that we read/write is the raw bytes in
+ the encoding used by the database (<code>UTF-8</code> by
+ default; see SQLite <code>PRAGMA encoding</code> documentation
+ for details).</p>
+
+ <h2><a name="18.2">18.2 SQLite Database Class</a></h2>
+
+ <p>The SQLite <code>database</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const std::string&amp; name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string&amp; vfs = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+#ifdef _WIN32
+ database (const std::wstring&amp; name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string&amp; vfs = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+#endif
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string&amp; vfs = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ name () const;
+
+ int
+ flags () const;
+
+ public:
+ transaction
+ begin_immediate ();
+
+ transaction
+ begin_exclusive ();
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/sqlite/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The first constructor opens the specified SQLite database. The
+ <code>name</code> argument is the database file name to open in
+ the UTF-8 encoding. If this argument is empty, then a temporary,
+ on-disk database is created. If this argument is the
+ <code>:memory:</code> special value, then a temporary, in-memory
+ database is created. The <code>flags</code> argument allows us to
+ specify SQLite opening flags. For more information on the possible
+ values, refer to the <code>sqlite3_open_v2()</code> function description
+ in the SQLite C API documentation. The <code>foreign_keys</code>
+ argument specifies whether foreign key constraints checking
+ should be enabled. See <a href="#18.5.3">Section 18.5.3,
+ "Foreign Key Constraints"</a> for more information on foreign
+ keys. The <code>vfs</code> argument specifies the SQLite
+ virtual file system module that should be used to access the
+ database. If this argument is empty, then the default vfs module
+ is used. Again, refer to the <code>sqlite3_open_v2()</code> function
+ documentation for detail.</p>
+
+ <p>The following example shows how we can open the <code>test.db</code>
+ database in the read-write mode and create it if it does not exist:</p>
+
+ <pre class="cxx">
+auto_ptr&lt;odb::database> db (
+ new odb::sqlite::database (
+ "test.db",
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
+ </pre>
+
+ <p>The second constructor is the same as the first except that the database
+ name is passes as <code>std::wstring</code> in the UTF-16 encoding. This
+ constructor is only available when compiling for Windows.</p>
+
+ <p>The third constructor extracts the database parameters from the
+ command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --database &lt;name>
+ --create
+ --read-only
+ --options-file &lt;file>
+ </pre>
+
+ <p>By default, this constructor opens the database in the read-write mode
+ (<code>SQLITE_OPEN_READWRITE</code> flag). If the <code>--create</code>
+ flag is specified, then the database file is created if it does
+ not already exist (<code>SQLITE_OPEN_CREATE</code> flag). If the
+ <code>--read-only</code> flag is specified, then the database is
+ opened in the read-only mode (<code>SQLITE_OPEN_READONLY</code>
+ flag instead of <code>SQLITE_OPEN_READWRITE</code>). The
+ <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the SQLite
+ options out of the <code>argv</code> array.</p>
+
+ <p>The <code>flags</code> argument has the same semantics as in
+ the first constructor. Flags from the command line always override
+ the corresponding values specified with this argument.</p>
+
+ <p>The third constructor throws the <code>odb::sqlite::cli_exception</code>
+ exception if the SQLite option values are missing or invalid.
+ See <a href="#18.4">Section 18.4, "SQLite Exceptions"</a>
+ for more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by the third constructor.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
+ functions are the SQLite-specific extensions to the standard
+ <code>odb::database::begin()</code> function (see
+ <a href="#3.5">Section 3.5, "Transactions"</a>). They allow us
+ to start an immediate (<code>BEGIN IMMEDIATE</code>) and an exclusive
+ (<code>BEGIN EXCLUSIVE</code>) SQLite transaction, respectively.
+ For more information on the semantics of the immediate and exclusive
+ transactions, refer to the <code>BEGIN</code> statement description
+ in the SQLite documentation.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ SQLite database connection encapsulated by the
+ <code>odb::sqlite::connection</code> class. For more information
+ on <code>sqlite::connection</code>, refer to <a href="#18.3">Section
+ 18.3, "SQLite Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="18.3">18.3 SQLite Connection and Connection Factory</a></h2>
+
+ <p>The <code>sqlite::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;, int extra_flags = 0);
+ connection (database&amp;, sqlite3*);
+
+ transaction
+ begin_immediate ();
+
+ transaction
+ begin_exclusive ();
+
+ sqlite3*
+ handle ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface,
+ refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
+ overloaded <code>sqlite::connection</code> constructor opens
+ a new SQLite connection. The <code>extra_flags</code> argument can
+ be used to specify extra <code>sqlite3_open_v2()</code> flags
+ that are combined with the flags specified in the
+ <code>sqlite::database</code> constructor. The second constructor
+ allows us to create a <code>connection</code> instance by providing
+ an already open native SQLite handle. Note that the
+ <code>connection</code> instance assumes ownership of this handle.</p>
+
+ <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
+ 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
+ <code>sqlite::database</code> class (<a href="#18.2">Section 18.2,
+ "SQLite Database Class"</a>). The <code>handle()</code> accessor
+ returns the SQLite handle corresponding to the connection.</p>
+
+ <p>The <code>sqlite::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::sqlite::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The three implementations of the <code>connection_factory</code>
+ interface provided by the SQLite ODB runtime library are
+ <code>single_connection_factory</code>,
+ <code>new_connection_factory</code>, and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/sqlite/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>single_connection_factory</code> class creates a
+ single connection that is shared between all the threads in
+ an application. If the connection is currently not in use,
+ then it is returned to the caller. Otherwise, the caller is
+ blocked until the connection becomes available. The
+ <code>single_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class single_connection_factory: public connection_factory
+ {
+ public:
+ single_connection_factory ();
+
+ protected:
+ class single_connection: public connection
+ {
+ public:
+ single_connection (database&amp;, int extra_flags = 0);
+ single_connection (database&amp;, sqlite3*);
+ };
+
+ typedef details::shared_ptr&lt;single_connection> single_connection_ptr;
+
+ virtual single_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>create()</code> virtual function is called when the
+ factory needs to create the connection. By deriving from the
+ <code>single_connection_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;, int extra_flags = 0);
+ pooled_connection (database_type&amp;, sqlite3*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ then the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>By default, connections created by <code>new_connection_factory</code>
+ and <code>connection_pool_factory</code> enable the SQLite shared cache
+ mode and use the unlock notify functionality to aid concurrency. To
+ disable the shared cache mode you can pass the
+ <code>SQLITE_OPEN_PRIVATECACHE</code> flag when creating the database
+ instance. For more information on the shared cache mode refer to the
+ SQLite documentation.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the <code>connection_pool_factory</code>
+ instance will be created by default with the min and max connections
+ values set to <code>0</code>. The following code fragment shows how we
+ can pass our own connection factory instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/sqlite/database.hxx>
+#include &lt;odb/sqlite/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::sqlite::connection_factory> f (
+ new odb::sqlite::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new sqlite::database (argc, argv, false, SQLITE_OPEN_READWRITE, f));
+}
+ </pre>
+
+ <h2><a name="18.4">18.4 SQLite Exceptions</a></h2>
+
+ <p>The SQLite ODB runtime library defines the following SQLite-specific
+ exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class forced_rollback: odb::recoverable
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+
+ class database_exception: odb::database_exception
+ {
+ public:
+ int
+ error () const
+
+ int
+ extended_error () const;
+
+ const std::string&amp;
+ message () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/sqlite/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::sqlite::forced_rollback</code> exception is thrown if
+ SQLite is forcing the current transaction to roll back. For more
+ information on this behavior refer to <a href="#18.5.6">Section 18.5.6,
+ "Forced Rollback"</a>.</p>
+
+ <p>The <code>odb::sqlite::database_exception</code> is thrown if
+ an SQLite database operation fails. The SQLite-specific error
+ information is accessible via the <code>error()</code>,
+ <code>extended_error()</code>, and <code>message()</code> functions.
+ All this information is also combined and returned in a
+ human-readable form by the <code>what()</code> function.</p>
+
+ <p>The <code>odb::sqlite::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::sqlite::database</code>
+ class if the SQLite option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+
+ <h2><a name="18.5">18.5 SQLite Limitations</a></h2>
+
+ <p>The following sections describe SQLite-specific limitations imposed by
+ the current SQLite and ODB runtime versions.</p>
+
+ <h3><a name="18.5.1">18.5.1 Query Result Caching</a></h3>
+
+ <p>SQLite ODB runtime implementation does not perform query result caching
+ (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
+ requested. The SQLite API supports interleaving execution of multiple
+ prepared statements on a single connection. As a result, with SQLite, it
+ is possible to have multiple uncached results and calls to other database
+ functions do not invalidate them. The only limitation of the uncached
+ SQLite results is the unavailability of the <code>result::size()</code>
+ function. If you call this function on an SQLite query result, then
+ the <code>odb::result_not_cached</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is always
+ thrown. Future versions of the SQLite ODB runtime library may add support
+ for result caching.</p>
+
+ <h3><a name="18.5.2">18.5.2 Automatic Assignment of Object Ids</a></h3>
+
+ <p>Due to SQLite API limitations, every automatically assigned object id
+ (<a href="#14.4.2">Section 14.4.2, "<code>auto</code>"</a>) should have
+ the <code>INTEGER</code> SQLite type. While SQLite will treat other
+ integer type names (such as <code>INT</code>, <code>BIGINT</code>, etc.)
+ as <code>INTEGER</code>, automatic id assignment will not work. By default,
+ ODB maps all C++ integral types to <code>INTEGER</code>. This means that
+ the only situation that requires consideration is the assignment of a
+ custom database type using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ //#pragma db id auto type("INT") // Will not work.
+ //#pragma db id auto type("INTEGER") // Ok.
+ #pragma db id auto // Ok, Mapped to INTEGER.
+ unsigned int id_;
+};
+ </pre>
+
+ <h3><a name="18.5.3">18.5.3 Foreign Key Constraints</a></h3>
+
+ <p>By default the SQLite ODB runtime enables foreign key constraints
+ checking (<code>PRAGMA foreign_keys=ON</code>). You can disable foreign
+ keys by passing <code>false</code> as the <code>foreign_keys</code>
+ argument to one of the <code>odb::sqlite::database</code> constructors.
+ Foreign keys will also be disabled if the SQLite library is built without
+ support for foreign keys (<code>SQLITE_OMIT_FOREIGN_KEY</code> and
+ <code>SQLITE_OMIT_TRIGGER</code> macros) or if you are using
+ an SQLite version prior to 3.6.19, which does not support foreign
+ key constraints checking.</p>
+
+ <p>If foreign key constraints checking is disabled or not available,
+ then inconsistencies in object relationships will not be detected.
+ Furthermore, using the <code>erase_query()</code> function
+ (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>)
+ to delete persistent objects that contain containers will not work
+ correctly. Container data for such objects will not be deleted.</p>
+
+ <p>When foreign key constraints checking is enabled, then you may
+ get the "foreign key constraint failed" error while re-creating the
+ database schema. This error is due to bugs in the SQLite DDL foreign
+ keys support. The recommended work-around for this problem is to
+ temporarily disable foreign key constraints checking while
+ re-creating the schema. The following code fragment shows how
+ this can be done:</p>
+
+ <pre class="cxx">
+#include &lt;odb/connection.hxx>
+#include &lt;odb/transaction.hxx>
+#include &lt;odb/schema-catalog.hxx>
+
+odb::database&amp; db = ...
+
+{
+ odb::connection_ptr c (db.connection ());
+
+ c->execute ("PRAGMA foreign_keys=OFF");
+
+ odb::transaction t (c->begin ());
+ odb::schema_catalog::create_schema (db);
+ t.commit ();
+
+ c->execute ("PRAGMA foreign_keys=ON");
+}
+ </pre>
+
+ <p>Finally, ODB assumes the standard SQL behavior which requires
+ that foreign key constraints checking is deferred until the
+ transaction is committed. Default SQLite behavior is to check such
+ constraints immediately. As a result, when used with ODB, a custom
+ database schema that defines foreign key constraints may need to
+ declare such constraints as <code>DEFERRABLE INITIALLY DEFERRED</code>,
+ as shown in the following example. By default, schemas generated by
+ the ODB compiler meet this requirement automatically.</p>
+
+ <pre class="sql">
+CREATE TABLE Employee (
+ ...
+ employer INTEGER REFERENCES Employer(id)
+ DEFERRABLE INITIALLY DEFERRED);
+ </pre>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="18.5.4">18.5.4 Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the SQLite error codes, it is impossible
+ to distinguish between the duplicate primary key and other constraint
+ violations. As a result, when making an object persistent, the SQLite
+ ODB runtime will translate all constraint violation errors to the
+ <code>object_already_persistent</code> exception (<a href="#3.14">Section
+ 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="18.5.5">18.5.5 Sharing of Queries</a></h3>
+
+ <p>As discussed in <a href="#4.3">Section 4.3, "Executing a Query"</a>, a
+ query instance that does not have any by-reference parameters is
+ immutable and can be shared between multiple threads without
+ synchronization. Currently, the SQLite ODB runtime does not support this
+ functionality. Future versions of the library will remove this
+ limitation.</p>
+
+ <h3><a name="18.5.6">18.5.6 Forced Rollback</a></h3>
+
+ <p>In SQLite 3.7.11 or later, if one of the connections participating in
+ the shared cache rolls back a transaction, then ongoing transactions
+ on other connections in the shared cache may also be forced to roll back.
+ An example of such behavior would be a read-only transaction that is
+ forced to roll back while iterating over the query result because another
+ transaction on another connection was rolled back.</p>
+
+ <p>If a transaction is being forced to roll back by SQLite, then ODB
+ throws <code>odb::sqlite::forced_rollback</code>
+ (<a href="#18.4">Section 18.4, "SQLite Exceptions"</a>) which is
+ a recoverable exception (<a href="#3.7">3.7 Error Handling and
+ Recovery</a>). As a result, the recommended way to handle this
+ exception is to re-execute the affected transaction.</p>
+
+ <h3><a name="18.5.7">18.5.7 Database Schema Evolution</a></h3>
+
+ <p>From the list of schema migration changes supported by ODB
+ (<a href="#13.2">Section 13.2, "Schema Migration"</a>), the
+ following are not supported by SQLite:</p>
+
+ <ul class="list">
+ <li>drop column</li>
+ <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
+ <li>add foreign key</li>
+ <li>drop foreign key</li>
+ </ul>
+
+ <p>The biggest problem is the lack of support for dropping columns.
+ This means that it would be impossible to delete a data member
+ in a persistent class. To work around this limitation ODB
+ implements <em>logical delete</em> for columns that allow
+ <code>NULL</code> values. In this case, instead of dropping
+ the column (in the post-migration stage), the schema migration
+ statements will automatically reset this column in all the
+ existing rows to <code>NULL</code>. Any new rows that are
+ inserted later will also automatically have this column set
+ to <code>NULL</code> (unless the column specifies a default
+ value).</p>
+
+ <p>Since it is also impossible to change the column's
+ <code>NULL</code>/<code>NOT NULL</code> attribute after it
+ has been added, to make schema evolution support usable in
+ SQLite, all the columns should be added as <code>NULL</code>
+ even if semantically they should not allow <code>NULL</code>
+ values. We should also normally refrain from assigning
+ default values to columns (<a href="#14.4.7">Section 14.4.7,
+ <code>default</code></a>), unless the space overhead of
+ a default value is not a concern. Explicitly making all
+ the data members <code>NULL</code> would be burdensome
+ and ODB provides the <code>--sqlite-override-null</code>
+ command line option that forces all the columns, even those
+ that were explicitly marked <code>NOT NULL</code>, to be
+ <code>NULL</code> in SQLite.</p>
+
+ <p>SQLite only supports adding foreign keys as part of the
+ column addition. As a result, we can only add a new
+ data member of an object pointer type if it points
+ to an object with a simple (single-column) object id.</p>
+
+ <p>SQLite also doesn't support dropping foreign keys.
+ Leaving a foreign key around works well with logical
+ delete unless we also want to delete the pointed-to
+ object. In this case we will have to leave an
+ empty table corresponding to the pointed-to object
+ around. An alternative would be to make a copy of the
+ pointing object without the object pointer, migrate the
+ data, and then delete both the old pointing and the
+ pointed-to objects. Since this will result in dropping
+ the pointing table, the foreign key will be dropped
+ as well. Yet another, more radical, solution to this
+ problem is to disable foreign keys checking altogether
+ (see the <code>foreign_keys</code> SQLite pragma).</p>
+
+ <p>To summarize, to make schema evolution support usable
+ in SQLite we should pass the <code>--sqlite-override-null</code>
+ option when compiling our persistent classes and also refrain
+ from assigning default values to data members. Note also that
+ this has to be done from the start so that every column is added
+ as <code>NULL</code> and therefore can be logically deleted later.
+ In particular, you cannot add the <code>--sqlite-override-null</code>
+ option when you realize you need to delete a data member. At this
+ point it is too late since the column has already been added
+ as <code>NOT NULL</code> in existing databases. We should also
+ avoid composite object ids if we are planning to use object
+ relationships.</p>
+
+ <h2><a name="18.6">18.6 SQLite Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define an SQLite index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>) while the <code>method</code> and
+ <code>options</code> clauses are not used. The column options
+ can be used to specify collations and the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index member(name_, "COLLATE binary DESC")
+};
+ </pre>
+
+ <p>Index names in SQLite are database-global. To avoid name clashes,
+ ODB automatically prefixes each index name with the table name on
+ which it is defined.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="19">19 PostgreSQL Database</a></h1>
+
+ <p>To generate support code for the PostgreSQL database you will need
+ to pass the "<code>--database&nbsp;pgsql</code>"
+ (or "<code>-d&nbsp;pgsql</code>") option to the ODB compiler.
+ Your application will also need to link to the PostgreSQL ODB runtime
+ library (<code>libodb-pgsql</code>). All PostgreSQL-specific ODB
+ classes are defined in the <code>odb::pgsql</code> namespace.</p>
+
+ <p>ODB utilizes prepared statements extensively. Support for prepared
+ statements was added in PostgreSQL version 7.4 with the introduction
+ of the messaging protocol version 3.0. For this reason, ODB supports
+ only PostgreSQL version 7.4 and later.</p>
+
+ <h2><a name="19.1">19.1 PostgreSQL Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and PostgreSQL database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>BOOLEAN</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>DOUBLE PRECISION</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>SMALLINT</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the PostgreSQL types corresponding to their
+ underlying integral types (see table above). The default
+ <code>NULL</code> semantics is <code>NOT NULL</code>. For
+ example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to INTEGER.
+ taste taste_; // Automatically mapped to SMALLINT.
+};
+ </pre>
+
+ <p>Note also that because PostgreSQL does not support unsigned integers,
+ the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
+ <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
+ are by default mapped to the <code>SMALLINT</code>, <code>INTEGER</code>,
+ and <code>BIGINT</code> PostgreSQL types, respectively. The sign bit
+ of the value stored by the database for these types will contain
+ 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="#14.8">Section 14.8,
+ "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="19.1.1">19.1.1 String Type Mapping</a></h3>
+
+ <p>The PostgreSQL ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the PostgreSQL
+ <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
+ types. However, these mappings are not enabled by default (in
+ particular, by default, <code>std::array</code> will be treated
+ as a container). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("CHAR(2)")
+ char state_[2];
+
+ #pragma db type("VARCHAR(128)")
+ std::string name_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("VARCHAR(128)")
+
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_; // Mapped to VARCHAR(128).
+};
+ </pre>
+
+ <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
+ may or may not be zero-terminated. When extracting such values from the
+ database, ODB will append the zero terminator if there is enough
+ space.</p>
+
+ <h3><a name="19.1.2">19.1.2 Binary Type and <code>UUID</code> Mapping</a></h3>
+
+ <p>The PostgreSQL ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code> types to the PostgreSQL
+ <code>BYTEA</code> type. There is also support for mapping the
+ <code>char[16]</code> array to the PostgreSQL <code>UUID</code> type.
+ However, these mappings are not enabled by default (in particular, by
+ default, <code>std::vector</code> and <code>std::array</code> will be
+ treated as containers). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("UUID")
+ char uuid_[16];
+
+ #pragma db type("BYTEA")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BYTEA")
+ unsigned char data_[256];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BYTEA")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BYTEA.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::pgsql::id_uuid> (u));
+db.query&lt;object> ("buf = " + query::_val&lt;odb::pgsql::id_bytea> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h2><a name="19.2">19.2 PostgreSQL Database Class</a></h2>
+
+ <p>The PostgreSQL <code>database</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; host = "",
+ unsigned int port = 0,
+ const std::string&amp; extra_conninfo = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ const std::string&amp; socket_ext,
+ const std::string&amp; extra_conninfo = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; conninfo,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ const std::string&amp; extra_conninfo = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ user () const;
+
+ const std::string&amp;
+ password () const;
+
+ const std::string&amp;
+ db () const;
+
+ const std::string&amp;
+ host () const;
+
+ unsigned int
+ port () const;
+
+ const std::string&amp;
+ socket_ext () const;
+
+ const std::string&amp;
+ extra_conninfo () const;
+
+ const std::string&amp;
+ conninfo () const;
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/pgsql/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us to specify
+ the PostgreSQL database parameters that should be used when connecting
+ to the database. The <code>port</code> argument in the first constructor
+ is an integer value specifying the TCP/IP port number to connect to. A
+ zero port number indicates that the default port should be used.
+ The <code>socket_ext</code> argument in the second constructor is a
+ string value specifying the UNIX-domain socket file name extension.</p>
+
+ <p>The third constructor allows us to specify all the database parameters
+ as a single <code>conninfo</code> string. All other constructors
+ accept additional database connection parameters as the
+ <code>extra_conninfo</code> argument. For more information
+ about the format of the <code>conninfo</code> string, refer to
+ the <code>PQconnectdb()</code> function description in the PostgreSQL
+ documentation. In the case of <code>extra_conninfo</code>, all the
+ database parameters provided in this string will take precedence
+ over those explicitly specified with other constructor arguments.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user &lt;login> | --username &lt;login>
+ --password &lt;password>
+ --database &lt;name> | --dbname &lt;name>
+ --host &lt;host>
+ --port &lt;integer>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the PostgreSQL
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::pgsql::cli_exception</code>
+ exception if the PostgreSQL option values are missing or invalid.
+ See section <a href="#19.4">Section 19.4, "PostgreSQL Exceptions"</a>
+ for more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance. Note that
+ the <code>conninfo()</code> accessor returns a complete
+ <code>conninfo</code> string which includes parameters that were
+ explicitly specified with the various constructor arguments, as well as
+ the extra parameters passed in the <code>extra_conninfo</code> argument.
+ The <code>extra_conninfo()</code> accessor will return the
+ <code>conninfo</code> string as passed in the <code>extra_conninfo</code>
+ argument.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ PostgreSQL database connection encapsulated by the
+ <code>odb::pgsql::connection</code> class. For more information
+ on <code>pgsql::connection</code>, refer to <a href="#19.3">Section
+ 19.3, "PostgreSQL Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="19.3">19.3 PostgreSQL Connection and Connection Factory</a></h2>
+
+ <p>The <code>pgsql::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, PGconn*);
+
+ PGconn*
+ handle ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface,
+ refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
+ overloaded <code>pgsql::connection</code> constructor establishes a
+ new PostgreSQL connection. The second constructor allows us to create
+ a <code>connection</code> instance by providing an already connected
+ native PostgreSQL handle. Note that the <code>connection</code>
+ instance assumes ownership of this handle. The <code>handle()</code>
+ accessor returns the PostgreSQL handle corresponding to the connection.</p>
+
+ <p>The <code>pgsql::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::pgsql::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the PostgreSQL ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/pgsql/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, PGconn*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be created by default
+ with the min and max connections values set to <code>0</code>. The
+ following code fragment shows how we can pass our own connection factory
+ instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/pgsql/database.hxx>
+#include &lt;odb/pgsql/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::pgsql::connection_factory> f (
+ new odb::pgsql::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new pgsql::database (argc, argv, false, "", f));
+}
+ </pre>
+
+ <h2><a name="19.4">19.4 PostgreSQL Exceptions</a></h2>
+
+ <p>The PostgreSQL ODB runtime library defines the following
+ PostgreSQL-specific exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ const std::string&amp;
+ message () const;
+
+ const std::string&amp;
+ sqlstate () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/pgsql/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::pgsql::database_exception</code> is thrown if
+ a PostgreSQL database operation fails. The PostgreSQL-specific error
+ information is accessible via the <code>message()</code> and
+ <code>sqlstate()</code> functions. All this information is also
+ combined and returned in a human-readable form by the <code>what()</code>
+ function.</p>
+
+ <p>The <code>odb::pgsql::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::pgsql::database</code>
+ class if the PostgreSQL option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <h2><a name="19.5">19.5 PostgreSQL Limitations</a></h2>
+
+ <p>The following sections describe PostgreSQL-specific limitations imposed
+ by the current PostgreSQL and ODB runtime versions.</p>
+
+ <h3><a name="19.5.1">19.5.1 Query Result Caching</a></h3>
+
+ <p>The PostgreSQL ODB runtime implementation will always return a
+ cached query result (<a href="#4.4">Section 4.4, "Query Result"</a>)
+ even when explicitly requested not to. This is a limitation of the
+ PostgreSQL client library (<code>libpq</code>) which does not
+ support uncached (streaming) query results.</p>
+
+ <h3><a name="19.5.2">19.5.2 Foreign Key Constraints</a></h3>
+
+ <p>ODB assumes the standard SQL behavior which requires that
+ foreign key constraints checking is deferred until the
+ transaction is committed. Default PostgreSQL behavior is
+ to check such constraints immediately. As a result, when
+ used with ODB, a custom database schema that defines foreign
+ key constraints may need to declare such constraints as
+ <code>INITIALLY DEFERRED</code>, as shown in the following example.
+ By default, schemas generated by the ODB compiler meet this requirement
+ automatically.</p>
+
+ <pre class="sql">
+CREATE TABLE Employee (
+ ...
+ employer BIGINT REFERENCES Employer(id) INITIALLY DEFERRED);
+ </pre>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="19.5.3">19.5.3 Unique Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the PostgreSQL error codes, it is impossible
+ to distinguish between the duplicate primary key and other unique
+ constraint violations. As a result, when making an object persistent,
+ the PostgreSQL ODB runtime will translate all unique constraint violation
+ errors to the <code>object_already_persistent</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="19.5.4">19.5.4 Date-Time Format</a></h3>
+
+ <p>ODB expects the PostgreSQL server to use integers as a binary
+ format for the date-time types, which is the default for most
+ PostgreSQL configurations. When creating a connection, ODB
+ examines the <code>integer_datetimes</code> PostgreSQL server
+ parameter and if it is <code>false</code>,
+ <code>odb::pgsql::database_exception</code> is thrown. You may
+ check the value of this parameter for your server by executing
+ the following SQL query:</p>
+
+ <pre class="sql">
+SHOW integer_datetimes
+ </pre>
+
+ <h3><a name="19.5.5">19.5.5 Timezones</a></h3>
+
+ <p>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 <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="19.5.6">19.5.6 <code>NUMERIC</code> Type Support</a></h3>
+
+ <p>Support for the PostgreSQL <code>NUMERIC</code> type is limited
+ to providing a binary buffer containing the binary representation
+ of the value. For more information on the binary format used to
+ store <code>NUMERIC</code> values refer to the PostgreSQL
+ documentation. An alternative approach to accessing <code>NUMERIC</code>
+ values is to map this type to one of the natively supported
+ ones, as discussed in <a href="#14.8">Section 14.8, "Database
+ Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="19.5.7">19.5.7 Bulk Operations Support</a></h3>
+
+ <p>Support for bulk operations (<a href="#15.3">Section 15.3, "Bulk
+ Database Operations"</a>) requires PostgreSQL client library
+ (<code>libpq</code>) version 14 or later and PostgreSQL server
+ version 7.4 or later.</p>
+
+
+ <h2><a name="19.6">19.6 PostgreSQL Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define a PostgreSQL index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>), the <code>method</code> clause specifies the
+ index method (for example, <code>BTREE</code>, <code>HASH</code>,
+ <code>GIN</code>, etc.), and the <code>options</code> clause
+ specifies additional index options, such as storage parameters,
+ table spaces, and the <code>WHERE</code> predicate. To support
+ the definition of concurrent indexes, the <code>type</code>
+ clause can end with the word <code>CONCURRENTLY</code> (upper and
+ lower cases are recognized). The column options can be used to
+ specify collations, operator classes, and the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index \
+ type("UNIQUE CONCURRENTLY") \
+ method("HASH") \
+ member(name_, "DESC") \
+ options("WITH(FILLFACTOR = 80)")
+};
+ </pre>
+
+ <p>Index names in PostgreSQL are schema-global. To avoid name clashes,
+ ODB automatically prefixes each index name with the table name on
+ which it is defined.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="20">20 Oracle Database</a></h1>
+
+ <p>To generate support code for the Oracle database you will need
+ to pass the "<code>--database&nbsp;oracle</code>"
+ (or "<code>-d&nbsp;oracle</code>") option to the ODB compiler.
+ Your application will also need to link to the Oracle ODB runtime
+ library (<code>libodb-oracle</code>). All Oracle-specific ODB
+ classes are defined in the <code>odb::oracle</code> namespace.</p>
+
+ <h2><a name="20.1">20.1 Oracle Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and Oracle database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>NUMBER(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>NUMBER(3)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>NUMBER(3)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>NUMBER(5)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>NUMBER(5)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>NUMBER(10)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>NUMBER(10)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>NUMBER(19)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>NUMBER(20)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>NUMBER(19)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>NUMBER(20)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>BINARY_FLOAT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>BINARY_DOUBLE</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>VARCHAR2(512)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR2(N-1)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>NUMBER(3)</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>In Oracle empty <code>VARCHAR2</code> and <code>NVARCHAR2</code>
+ strings are represented as a <code>NULL</code> value. As a result,
+ columns of the <code>std::string</code> and <code>char[N]</code>
+ types are by default declared as <code>NULL</code> except for
+ primary key columns. However, you can override this by explicitly
+ declaring such columns as <code>NOT NULL</code> with the
+ <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null/not_null</code>"</a>). This also means that for
+ object ids that are mapped to these Oracle types, an empty string is
+ an invalid value.</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the Oracle types corresponding to their
+ underlying integral types (see table above). The default
+ <code>NULL</code> semantics is <code>NOT NULL</code>. For
+ example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to NUMBER(10).
+ taste taste_; // Automatically mapped to NUMBER(3).
+};
+ </pre>
+
+ <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="#14.8">Section 14.8, "Database Type Mapping
+ Pragmas"</a>.</p>
+
+ <h3><a name="20.1.1">20.1.1 String Type Mapping</a></h3>
+
+ <p>The Oracle ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the Oracle <code>CHAR</code>,
+ <code>VARCHAR2</code>, <code>CLOB</code>, <code>NCHAR</code>,
+ <code>NVARCHAR2</code>, and <code>NCLOB</code> types. However,
+ these mappings are not enabled by default (in particular, by
+ default, <code>std::array</code> will be treated as a container).
+ To enable the alternative mappings for these types we need to
+ specify the database type explicitly using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type ("CHAR(2)")
+ char state_[2];
+
+ #pragma db type ("VARCHAR(128)") null
+ std::string name_;
+
+ #pragma db type ("CLOB")
+ std::string text_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("VARCHAR(128)") null
+
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_; // Mapped to VARCHAR(128).
+
+ #pragma db type ("CLOB")
+ std::string text_; // Mapped to CLOB.
+};
+ </pre>
+
+ <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
+ may or may not be zero-terminated. When extracting such values from the
+ database, ODB will append the zero terminator if there is enough
+ space.</p>
+
+ <h3><a name="20.1.2">20.1.2 Binary Type Mapping</a></h3>
+
+ <p>The Oracle ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code>
+ types to the Oracle <code>BLOB</code> and <code>RAW</code> types.
+ However, these mappings are not enabled by default (in particular, by
+ default, <code>std::vector</code> and <code>std::array</code> will be
+ treated as containers). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("BLOB")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("RAW(16)")
+ unsigned char uuid_[16];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BLOB")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BLOB.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::oracle::id_raw> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h2><a name="20.2">20.2 Oracle Database Class</a></h2>
+
+ <p>The Oracle <code>database</code> class encapsulates the OCI environment
+ handle as well as the database connection string and user credentials
+ that are used to establish connections to the database. It has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ ub2 charset = 0,
+ ub2 ncharset = 0,
+ OCIEnv* environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; service,
+ const std::string&amp; host,
+ unsigned int port = 0,
+ ub2 charset = 0,
+ ub2 ncharset = 0,
+ OCIEnv* environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ ub2 charset = 0,
+ ub2 ncharset = 0,
+ OCIEnv* environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ user () const;
+
+ const std::string&amp;
+ password () const;
+
+ const std::string&amp;
+ db () const;
+
+ const std::string&amp;
+ service () const;
+
+ const std::string&amp;
+ host () const;
+
+ unsigned int
+ port () const;
+
+ ub2
+ charset () const;
+
+ ub2
+ ncharset () const;
+
+ OCIEnv*
+ environment ();
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/oracle/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us to specify the
+ Oracle database parameters that should be used when connecting to the
+ database. The <code>db</code> argument in the first constructor is a
+ connection identifier that specifies the database to connect to. For more
+ information on the format of the connection identifier, refer to the
+ Oracle documentation.</p>
+
+ <p>The second constructor allows us to specify the individual components
+ of a connection identifier as the <code>service</code>, <code>host</code>,
+ and <code>port</code> arguments. If the <code>host</code> argument is
+ empty, then localhost is used by default. Similarly, if the
+ <code>port</code> argument is zero, then the default port is used.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user &lt;login>
+ --password &lt;password>
+ --database &lt;connect-id>
+ --service &lt;name>
+ --host &lt;host>
+ --port &lt;integer>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value. Note that it
+ is invalid to specify the <code>--database</code> option
+ together with <code>--service</code>, <code>--host</code>, or
+ <code>--port</code> options.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the Oracle
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::oracle::cli_exception</code>
+ exception if the Oracle option values are missing or invalid. See section
+ <a href="#20.4">Section 20.4, "Oracle Exceptions"</a> for more
+ information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>Additionally, all the constructors have the <code>charset</code>,
+ <code>ncharset</code>, and <code>environment</code> arguments.
+ The <code>charset</code> argument specifies the client-side database
+ character encoding. Character data corresponding to the <code>CHAR</code>,
+ <code>VARCHAR2</code>, and <code>CLOB</code> types will be delivered
+ to and received from the application in this encoding. Similarly,
+ the <code>ncharset</code> argument specifies the client-side national
+ character encoding. Character data corresponding to the <code>NCHAR</code>,
+ <code>NVARCHAR2</code>, and <code>NCLOB</code> types will be delivered
+ to and received from the application in this encoding. For the complete
+ list of available character encoding values, refer to the Oracle
+ documentation. Commonly used encoding values are <code>873</code>
+ (UTF-8), <code>31</code> (ISO-8859-1), and <code>1000</code> (UTF-16).
+ If the database character encoding is not specified, then the
+ <code>NLS_LANG</code> environment/registry variable is used. Similarly,
+ if the national character encoding is not specified, then the
+ <code>NLS_NCHAR</code> environment/registry variable is used. For more
+ information on character encodings, refer to the
+ <code>OCIEnvNlsCreate()</code> function in the Oracle Call Interface
+ (OCI) documentation.</p>
+
+ <p>The <code>environment</code> argument allows us to provide a custom
+ OCI environment handle. If this argument is not <code>NULL</code>,
+ then the passed handle is used in all the OCI function calls made
+ by this <code>database</code> class instance. Note also that the
+ <code>database</code> instance does not assume ownership of the
+ passed environment handle and this handle should be valid for
+ the lifetime of the <code>database</code> instance. If a custom
+ environment handle is used, then the <code>charset</code> and
+ <code>ncharset</code> arguments have no effect.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ Oracle database connection encapsulated by the
+ <code>odb::oracle::connection</code> class. For more information
+ on <code>oracle::connection</code>, refer to <a href="#20.3">Section
+ 20.3, "Oracle Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="20.3">20.3 Oracle Connection and Connection Factory</a></h2>
+
+ <p>The <code>oracle::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, OCISvcCtx*);
+
+ OCISvcCtx*
+ handle ();
+
+ OCIError*
+ error_handle ();
+
+ details::buffer&amp;
+ lob_buffer ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface, refer
+ to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
+ <code>oracle::connection</code> constructor creates a new OCI service
+ context. The OCI statement caching is enabled for the underlying session
+ while the OCI connection pooling and session pooling are not used. The
+ second constructor allows us to create a <code>connection</code> instance by
+ providing an already connected Oracle service context. Note that the
+ <code>connection</code> instance assumes ownership of this handle. The
+ <code>handle()</code> accessor returns the OCI service context handle
+ associated with the <code>connection</code> instance.</p>
+
+ <p>An OCI error handle is allocated for each <code>connection</code>
+ instance and is available via the <code>error_handle()</code> accessor
+ function.</p>
+
+ <p>Additionally, each <code>connection</code> instance maintains a large
+ object (LOB) buffer. This buffer is used by the Oracle ODB runtime
+ as an intermediate storage for piecewise handling of LOB data.
+ By default, the LOB buffer has zero initial capacity and is
+ expanded to 4096 bytes when the first LOB operation is performed.
+ If your application requires a bigger or smaller LOB buffer, you can
+ specify a custom capacity using the <code>lob_buffer()</code>
+ accessor.</p>
+
+ <p>The <code>oracle::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::oracle::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the Oracle ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/oracle/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, OCISvcCtx*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be created by default
+ with the min and max connections values set to <code>0</code>. The
+ following code fragment shows how we can pass our own connection factory
+ instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/oracle/database.hxx>
+#include &lt;odb/oracle/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::oracle::connection_factory> f (
+ new odb::oracle::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new oracle::database (argc, argv, false, 0, 0, 0, f));
+}
+ </pre>
+
+ <h2><a name="20.4">20.4 Oracle Exceptions</a></h2>
+
+ <p>The Oracle ODB runtime library defines the following
+ Oracle-specific exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ class record
+ {
+ public:
+ sb4
+ error () const;
+
+ const std::string&amp;
+ message () const;
+ };
+
+ typedef std::vector&lt;record> records;
+
+ typedef records::size_type size_type;
+ typedef records::const_iterator iterator;
+
+ iterator
+ begin () const;
+
+ iterator
+ end () const;
+
+ size_type
+ size () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+
+ class invalid_oci_handle: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/oracle/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::oracle::database_exception</code> is thrown if
+ an Oracle database operation fails. The Oracle-specific error
+ information is stored as a series of records, each containing
+ the error code as a signed 4-byte integer and the message string.
+ All this information is also combined and returned in a
+ human-readable form by the <code>what()</code> function.</p>
+
+ <p>The <code>odb::oracle::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::oracle::database</code>
+ class if the Oracle option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <p>The <code>odb::oracle::invalid_oci_handle</code> is thrown if an
+ invalid handle is passed to an OCI function or if an OCI function
+ was unable to allocate a handle. The former normally indicates
+ a programming error while the latter indicates an out of memory
+ condition. The <code>what()</code> function returns a human-readable
+ description of an error.</p>
+
+ <h2><a name="20.5">20.5 Oracle Limitations</a></h2>
+
+ <p>The following sections describe Oracle-specific limitations imposed
+ by the current Oracle and ODB runtime versions.</p>
+
+ <h3><a name="20.5.1">20.5.1 Identifier Truncation</a></h3>
+
+ <p>Oracle limits the length of database identifiers (table, column, etc.,
+ names) to 30 characters. The ODB compiler automatically truncates
+ any identifier that is longer than 30 characters. This, however,
+ can lead to duplicate names. A common symptom of this problem
+ are errors during the database schema creation indicating
+ that a database object with the same name already exists. To
+ resolve this problem we can assign custom, shorter identifiers
+ using the <code>db&nbsp;table</code> and <code>db&nbsp;column</code>
+ pragmas (<a href="#14">Chapter 14, "ODB Pragma Language")</a>. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class long_class_name
+{
+ ...
+
+ std::vector&lt;int> long_container_x_;
+ std::vector&lt;int> long_container_y_;
+};
+ </pre>
+
+ <p>In the above example, the names of the two container tables will be
+ <code>long_class_name_long_container_x_</code> and
+ <code>long_class_name_long_container_y_</code>. However, when
+ truncated to 30 characters, they both become
+ <code>long_class_name_long_container</code>. To resolve this
+ collision we can assign a custom table name for each container:</p>
+
+ <pre class="cxx">
+#pragma db object
+class long_class_name
+{
+ ...
+
+ #pragma db table("long_class_name_cont_x")
+ std::vector&lt;int> long_container_x_;
+
+ #pragma db table("long_class_name_cont_y")
+ std::vector&lt;int> long_container_y_;
+};
+ </pre>
+
+ <h3><a name="20.5.2">20.5.2 Query Result Caching</a></h3>
+
+ <p>Oracle ODB runtime implementation does not perform query result caching
+ (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
+ requested. The OCI API supports interleaving execution of multiple
+ prepared statements on a single connection. As a result, with OCI,
+ it is possible to have multiple uncached results and calls to other
+ database functions do not invalidate them. The only limitation of
+ the uncached Oracle results is the unavailability of the
+ <code>result::size()</code> function. If you call this function on
+ an Oracle query result, then the <code>odb::result_not_cached</code>
+ exception (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is
+ always thrown. Future versions of the Oracle ODB runtime library
+ may add support for result caching.</p>
+
+ <h3><a name="20.5.3">20.5.3 Foreign Key Constraints</a></h3>
+
+ <p>ODB assumes the standard SQL behavior which requires that
+ foreign key constraints checking is deferred until the
+ transaction is committed. Default Oracle behavior is
+ to check such constraints immediately. As a result, when
+ used with ODB, a custom database schema that defines foreign
+ key constraints may need to declare such constraints as
+ <code>INITIALLY DEFERRED</code>, as shown in the following example.
+ By default, schemas generated by the ODB compiler meet this requirement
+ automatically.</p>
+
+ <pre class="sql">
+CREATE TABLE Employee (
+ ...
+ employer NUMBER(20) REFERENCES Employer(id)
+ DEFERRABLE INITIALLY DEFERRED);
+ </pre>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="20.5.4">20.5.4 Unique Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the Oracle error codes, it is impossible
+ to distinguish between the duplicate primary key and other unique
+ constraint violations. As a result, when making an object persistent,
+ the Oracle ODB runtime will translate all unique constraint violation
+ errors to the <code>object_already_persistent</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="20.5.5">20.5.5 Large <code>FLOAT</code> and
+ <code>NUMBER</code> Types</a></h3>
+
+ <p>The Oracle <code>FLOAT</code> type with a binary precision greater
+ than 53 and fixed-point <code>NUMBER</code> type with a decimal
+ precision greater than 15 cannot be automatically extracted
+ into the C++ <code>float</code> and <code>double</code> types.
+ Instead, the Oracle ODB runtime uses a 21-byte buffer containing
+ the binary representation of a value as an image type for such
+ <code>FLOAT</code> and <code>NUMBER</code> types. In order to
+ convert them into an application-specific large number representation,
+ you will need to provide a suitable <code>value_traits</code>
+ template specialization. For more information on the binary format
+ used to store the <code>FLOAT</code> and <code>NUMBER</code> values,
+ refer to the Oracle Call Interface (OCI) documentation.</p>
+
+ <p>An alternative approach to accessing large <code>FLOAT</code> and
+ <code>NUMBER</code> values is to map these type to one of the
+ natively supported ones, as discussed in <a href="#14.8">Section
+ 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <p>Note that a <code>NUMBER</code> type that is used to represent a
+ floating point number (declared by specifying <code>NUMBER</code>
+ without any range and scale) can be extracted into the C++
+ <code>float</code> and <code>double</code> types.</p>
+
+ <h3><a name="20.5.6">20.5.6 Timezones</a></h3>
+
+ <p>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
+ <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="20.5.7">20.5.7 <code>LONG</code> Types</a></h3>
+
+ <p>ODB does not support the deprecated Oracle <code>LONG</code> and
+ <code>LONG RAW</code> data types. However, these types can be accessed
+ by mapping them to one of the natively supported types, as discussed
+ in <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="20.5.8">20.5.8 LOB Types and By-Value Accessors/Modifiers</a></h3>
+
+ <p>As discussed in <a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
+ accessor and modifier expressions cannot be used with data members
+ of Oracle large object (LOB) data types: <code>BLOB</code>,
+ <code>CLOB</code>, and <code>NCLOB</code>. The Oracle ODB runtime
+ uses streaming for reading/writing LOB data directly from/to
+ data members. As a result, by-reference accessors and modifiers
+ should be used for these data types.</p>
+
+ <h3><a name="20.5.9">20.5.9 Database Schema Evolution</a></h3>
+
+ <p>In Oracle, the type of the <code>name</code> column in the
+ <code>schema_version</code> table is <code>VARCHAR2(512)</code>.
+ Because this column is a primary key and <code>VARCHAR2</code>
+ represents empty strings as <code>NULL</code> values, it is
+ impossible to store an empty string in this column, which
+ is what is used to represent the default schema name. As a
+ result, in Oracle, the empty schema name is stored as a
+ string containing a single space character. ODB performs
+ all the necessary translations automatically and normally
+ you do not need to worry about this implementation detail
+ unless you are querying or modifying the <code>schema_version</code>
+ table directly.</p>
+
+ <h2><a name="20.6">20.6 Oracle Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define an Oracle index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>, <code>BITMAP</code>), the <code>method</code>
+ clause is not used, and the <code>options</code> clause specifies
+ additional index properties, such as partitioning, table spaces, etc.
+ The column options can be used to specify the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index \
+ type("BITMAP") \
+ member(name_, "DESC") \
+ options("TABLESPACE TBS1")
+};
+ </pre>
+
+ <p>Index names in Oracle are schema-global. To avoid name clashes,
+ ODB automatically prefixes each index name with the table name on
+ which it is defined.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="21">21 Microsoft SQL Server Database</a></h1>
+
+ <p>To generate support code for the SQL Server database you will need
+ to pass the "<code>--database&nbsp;mssql</code>"
+ (or "<code>-d&nbsp;mssql</code>") option to the ODB compiler.
+ Your application will also need to link to the SQL Server ODB runtime
+ library (<code>libodb-mssql</code>). All SQL Server-specific ODB
+ classes are defined in the <code>odb::mssql</code> namespace.</p>
+
+ <h2><a name="21.1">21.1 SQL Server Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and SQL Server database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>BIT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>TINYINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>TINYINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>FLOAT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::wstring</code></td>
+ <td><code>NVARCHAR(512)/NVARCHAR(256)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>wchar_t[N]</code></td>
+ <td><code>NVARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>GUID</code></td>
+ <td><code>UNIQUEIDENTIFIER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>TINYINT</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>Note that the <code>std::string</code> and <code>std::wstring</code>
+ types are mapped differently depending on whether a member of one of
+ these types is an object id or not. If the member is an object id,
+ then for this member <code>std::string</code> is mapped
+ to <code>VARCHAR(256)</code> and <code>std::wstring</code> &mdash;
+ to <code>NVARCHAR(256)</code>. Otherwise, <code>std::string</code>
+ is mapped to <code>VARCHAR(512)</code> and <code>std::wstring</code>
+ &mdash; to <code>NVARCHAR(512)</code>. Note also that you can
+ always change this mapping using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the SQL Server types corresponding to their
+ underlying integral types (see table above). The default
+ <code>NULL</code> semantics is <code>NOT NULL</code>. For
+ example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to INT.
+ taste taste_; // Automatically mapped to TINYINT.
+};
+ </pre>
+
+ <p>Note also that because SQL Server does not support unsigned integers,
+ the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
+ <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
+ are by default mapped to the <code>SMALLINT</code>, <code>INT</code>,
+ and <code>BIGINT</code> SQL Server types, respectively. The sign bit
+ of the value stored by the database for these types will contain
+ the most significant bit of the actual unsigned value being
+ persisted. Similarly, because there is no signed version of the
+ <code>TINYINT</code> SQL Server type, by default, the
+ <code>signed char</code> C++ type is mapped to <code>TINYINT</code>.
+ As a result, the most significant bit of the value stored by the
+ database for this type 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="#14.8">Section 14.8, "Database
+ Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="21.1.1">21.1.1 String Type Mapping</a></h3>
+
+ <p>The SQL Server ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the SQL Server
+ <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
+ types as well as the <code>std::wstring</code>, <code>wchar_t[N]</code>,
+ and <code>std::array&lt;wchar_t, N></code> types to <code>NCHAR</code>,
+ <code>NVARCHAR</code>, and <code>NTEXT</code>. However, these mappings
+ are not enabled by default (in particular, by default,
+ <code>std::array</code> will be treated as a container). To enable the
+ alternative mappings for these types we need to specify the database
+ type explicitly using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type ("CHAR(2)")
+ char state_[2];
+
+ #pragma db type ("NVARCHAR(max)")
+ std::wstring text_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::wstring) type("NVARCHAR(max)")
+
+#pragma db object
+class object
+{
+ ...
+
+ std::wstring text_; // Mapped to NVARCHAR(max).
+};
+ </pre>
+
+ <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
+ <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
+ values may or may not be zero-terminated. When extracting such values
+ from the database, ODB will append the zero terminator if there is
+ enough space.</p>
+
+ <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
+ Types"</a> for certain limitations of long string types.</p>
+
+ <h3><a name="21.1.2">21.1.2 Binary Type and <code>UNIQUEIDENTIFIER</code> Mapping</a></h3>
+
+ <p>The SQL Server ODB runtime library also provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and <code>std::array&lt;unsigned char, N></code>
+ types to the SQL Server <code>BINARY</code>, <code>VARBINARY</code>, and
+ <code>IMAGE</code> types. There is also support for mapping the
+ <code>char[16]</code> array to the SQL Server <code>UNIQUEIDENTIFIER</code>
+ type. However, these mappings are not enabled by default (in particular,
+ by default, <code>std::vector</code> and <code>std::array</code> will
+ be treated as containers). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("UNIQUEIDENTIFIER")
+ char uuid_[16];
+
+ #pragma db type("VARBINARY(max)")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BINARY(256)")
+ unsigned char data_[256];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("VARBINARY(max)")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to VARBINARY(max).
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::mssql::id_binary> (u));
+db.query&lt;object> (
+ "uuid = " + query::_val&lt;odb::mssql::id_uniqueidentifier> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
+ Types"</a> for certain limitations of long binary types.</p>
+
+ <h3><a name="21.1.3">21.1.3 <code>ROWVERSION</code> Mapping</a></h3>
+
+ <p><code>ROWVERSION</code> is a special SQL Server data type that is
+ automatically incremented by the database server whenever a row
+ is inserted or updated. As such, it is normally used to implement
+ optimistic concurrency and ODB provides support for using
+ <code>ROWVERSION</code> instead of the more portable approach
+ for optimistic concurrency (<a href="#12">Chapter 12, "Optimistic
+ Concurrency"</a>).</p>
+
+ <p><code>ROWVERSION</code> is a 64-bit value which is mapped by ODB
+ to <code>unsigned long long</code>. As a result, to use
+ <code>ROWVERSION</code> for optimistic concurrency we need to
+ make sure that the version column is of the <code>unsigned long
+ long</code> type. We also need to explicitly specify that it
+ should be mapped to the <code>ROWVERSION</code> data type. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version type("ROWVERSION")
+ unsigned long long version_;
+};
+ </pre>
+
+ <h3><a name="21.1.4">21.1.4 Long String and Binary Types</a></h3>
+
+ <p>For SQL Server, ODB handles character, national character, and
+ binary data in two different ways depending on its maximum length.
+ If the maximum length (in bytes) is less than or equal to the limit
+ specified with the <code>--mssql-short-limit</code> ODB compiler
+ option (1024 by default), then it is treated as <i>short data</i>,
+ otherwise &mdash; <i>long data</i>. For short data ODB pre-allocates
+ an intermediate buffer of the maximum size and binds it directly
+ to a parameter or result column. This way the underlying database
+ API (ODBC) can read/write directly from/to this buffer. In the case
+ of long data, the data is read/written in chunks using the
+ <code>SQLGetData()</code>/<code>SQLPutData()</code> ODBC functions.
+ While the long data approach reduces the amount of memory used by
+ the application, it may require greater CPU resources.</p>
+
+ <p>Long data has a number of limitations. In particular, when setting
+ a custom short data limit, make sure that it is sufficiently large
+ so that no object id in the application is treated as long data.
+ It is also impossible to load an object or view with long data more
+ than once as part of a query result iteration (<a href="#4.4">Section
+ 4.4, "Query Result"</a>). Any such attempt will result in the
+ <code>odb::mssql::long_data_reload</code> exception
+ (<a href="#21.4">Section 21.4, "SQL Server Exceptions"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ int num_;
+
+ #pragma db type("VARCHAR(max)") // Long data.
+ std::string str_;
+};
+
+typedef odb::query&lt;object> query;
+typedef odb::result&lt;object> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;object> (query::num &lt; 100));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ if (!i->str_.empty ()) // First load.
+ {
+ object o;
+ i.load (o); // Error: second load, long_data_reload is thrown.
+ }
+}
+
+t.commit ();
+ </pre>
+
+ <p>Finally, if a native view (<a href="#10.6">Section 10.6, "Native
+ Views"</a>) contains one or more long data members, then such
+ members should come last both in the select-list of the native
+ SQL query and the list of data members in the C++ class.</p>
+
+ <h2><a name="21.2">21.2 SQL Server Database Class</a></h2>
+
+ <p>The SQL Server <code>database</code> class encapsulates the ODBC
+ environment handle as well as the server instance address and
+ user credentials that are used to establish connections to the
+ database. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ enum protocol
+ {
+ protocol_auto,
+ protocol_tcp, // TCP/IP.
+ protocol_lpc, // Shared memory (local procedure call).
+ protocol_np // Named pipes.
+ };
+
+ enum transaction_isolation
+ {
+ isolation_read_uncommitted,
+ isolation_read_committed, // SQL Server default.
+ isolation_repeatable_read,
+ isolation_snapshot,
+ isolation_serializable
+ };
+
+ class database: public odb::database
+ {
+ public:
+ typedef protocol protocol_type;
+ typedef transaction_isolation transaction_isolation_type;
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; server,
+ const std::string&amp; driver = "",
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ protocol_type protocol = protocol_auto,
+ const std::string&amp; host = "",
+ const std::string&amp; instance = "",
+ const std::string&amp; driver = "",
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ unsigned int port,
+ const std::string&amp; driver = "",
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; connect_string,
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ user () const;
+
+ const std::string&amp;
+ password () const;
+
+ const std::string&amp;
+ db () const;
+
+ protocol_type
+ protocol () const;
+
+ const std::string&amp;
+ host () const;
+
+ const std::string&amp;
+ instance () const;
+
+ unsigned int
+ port () const;
+
+ const std::string&amp;
+ server () const;
+
+ const std::string&amp;
+ driver () const;
+
+ const std::string&amp;
+ extra_connect_string () const;
+
+ transaction_isolation_type
+ transaction_isolation () const;
+
+ const std::string&amp;
+ connect_string () const;
+
+ SQLHENV
+ environment ();
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mssql/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us to specify the
+ SQL Server database parameters that should be used when connecting to the
+ database. The <code>user</code> and <code>password</code> arguments
+ specify the login name and password. If <code>user</code> is empty,
+ then Windows authentication is used and the <code>password</code>
+ argument is ignored. The <code>db</code> argument specifies the
+ database name to open. If it is empty, then the default database for
+ the user is used.</p>
+
+ <p>The <code>server</code> argument in the first constructor specifies
+ the SQL Server instance address in the standard SQL Server address
+ format:</p>
+
+ <p>
+ <code>[<i>protocol</i><b>:</b>]<i>host</i>[<b>\</b><i>instance</i>][<b>,</b><i>port</i>]</code>
+ </p>
+
+ <p>Where <code><i>protocol</i></code> can be <code>tcp</code>
+ (TCP/IP), <code>lpc</code> (shared memory), or
+ <code>np</code> (named pipe). If protocol is not specified, then a
+ suitable protocol is automatically selected based on the SQL Server
+ Native Client configuration. The <code><i>host</i></code> component
+ can be a host name or an IP address. If <code><i>instance</i></code>
+ is not specified, then the default SQL Server instance is assumed.
+ If port is not specified, then the default SQL Server port is
+ used (1433). Note that you would normally specify either the
+ instance name or the port, but not both. If both are specified,
+ then the instance name is ignored by the SQL Server Native Client
+ ODBC driver. For more information on the format of the SQL
+ Server address, refer to the SQL Server Native Client ODBC
+ driver documentation.</p>
+
+ <p>The second and third constructors allow us to specify all these address
+ components (protocol, host, instance, and port) as separate
+ arguments. The third constructor always connects using TCP/IP
+ to the specified host and port.</p>
+
+ <p>The <code>driver</code> argument specifies the SQL Server Native
+ Client ODBC driver that should be used to connect to the database.
+ If not specified, then the latest available version is used. The
+ following examples show common ways of connecting to the database
+ using the first three constructors:</p>
+
+ <pre class="cxx">
+// Connect to the default SQL Server instance on the local machine
+// using the default protocol. Login as 'test' with password 'secret'
+// and open the 'example_db' database.
+//
+odb::mssql::database db1 ("test",
+ "secret",
+ "example_db");
+
+// As above except use Windows authentication and open the default
+// database for this user.
+//
+odb::mssql::database db2 ("",
+ "",
+ "");
+
+// Connect to the default SQL Server instance on 'onega' using the
+// default protocol. Login as 'test' with password 'secret' and open
+// the 'example_db' database.
+//
+odb::mssql::database db3 ("test",
+ "secret",
+ "example_db"
+ "onega");
+
+// As above but connect to the 'production' SQL Server instance.
+//
+odb::mssql::database db4 ("test",
+ "secret",
+ "example_db"
+ "onega\\production");
+
+// Same as above but specify protocol, host, and instance as separate
+// arguments.
+//
+odb::mssql::database db5 ("test",
+ "secret",
+ "example_db",
+ odb::mssql::protocol_auto,
+ "onega",
+ "production");
+
+// As above, but use TCP/IP as the protocol.
+//
+odb::mssql::database db6 ("test",
+ "secret",
+ "example_db"
+ "tcp:onega\\production");
+
+// Same as above but using separate arguments.
+//
+odb::mssql::database db7 ("test",
+ "secret",
+ "example_db",
+ odb::mssql::protocol_tcp,
+ "onega",
+ "production");
+
+// As above, but use TCP/IP port instead of the instance name.
+//
+odb::mssql::database db8 ("test",
+ "secret",
+ "example_db"
+ "tcp:onega,1435");
+
+// Same as above but using separate arguments. Note that here we
+// don't need to specify protocol explicitly since it can only
+// be TCP/IP.
+//
+odb::mssql::database db9 ("test",
+ "secret",
+ "example_db",
+ "onega",
+ 1435);
+
+// As above but use the specific SQL Server Native Client ODBC
+// driver version.
+//
+odb::mssql::database dbA ("test",
+ "secret",
+ "example_db"
+ "tcp:onega,1435",
+ "SQL Server Native Client 10.0");
+ </pre>
+
+
+ <p>The fourth constructor allows us to pass a custom ODBC connection
+ string that provides all the information necessary to connect to
+ the database. Note also that all the other constructors have the
+ <code>extra_connect_string</code> argument which can be used to
+ specify additional ODBC connection attributes. For more information
+ on the format of the ODBC connection string, refer to the SQL
+ Server Native Client ODBC driver documentation.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user | -U &lt;login>
+ --password | -P &lt;password>
+ --database | -d &lt;name>
+ --server | -S &lt;address>
+ --driver &lt;name>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the SQL Server
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::mssql::cli_exception</code>
+ exception if the SQL Server option values are missing or invalid. See
+ section <a href="#21.4">Section 21.4, "SQL Server Exceptions"</a> for
+ more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>Additionally, all the constructors have the <code>transaction_isolation</code>
+ and <code>environment</code> arguments. The <code>transaction_isolation</code>
+ argument allows us to specify an alternative transaction isolation level
+ that should be used by all the connections created by this database instance.
+ The <code>environment</code> argument allows us to provide a custom ODBC
+ environment handle. If this argument is not <code>NULL</code>, then the
+ passed handle is used in all the ODBC function calls made by this
+ <code>database</code> instance. Note also that the <code>database</code>
+ instance does not assume ownership of the passed environment handle and
+ this handle should be valid for the lifetime of the <code>database</code>
+ instance.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ SQL Server database connection encapsulated by the
+ <code>odb::mssql::connection</code> class. For more information
+ on <code>mssql::connection</code>, refer to <a href="#21.3">Section
+ 21.3, "SQL Server Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="21.3">21.3 SQL Server Connection and Connection Factory</a></h2>
+
+ <p>The <code>mssql::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, SQLHDBC handle);
+
+ SQLHDBC
+ handle ();
+
+ details::buffer&amp;
+ long_data_buffer ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface, refer
+ to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
+ <code>mssql::connection</code> constructor creates a new ODBC connection.
+ The created connection is configured to use the manual commit mode with
+ multiple active result sets (MARS) enabled. The second constructor allows
+ us to create a <code>connection</code> instance by providing an already
+ established ODBC connection. Note that the <code>connection</code>
+ instance assumes ownership of this handle. The <code>handle()</code>
+ accessor returns the underlying ODBC connection handle associated with
+ the <code>connection</code> instance.</p>
+
+ <p>Additionally, each <code>connection</code> instance maintains a long
+ data buffer. This buffer is used by the SQL Server ODB runtime
+ as an intermediate storage for piecewise handling of long data.
+ By default, the long data buffer has zero initial capacity and is
+ expanded to 4096 bytes when the first long data operation is performed.
+ If your application requires a bigger or smaller long data buffer,
+ you can specify a custom capacity using the <code>long_data_buffer()</code>
+ accessor.</p>
+
+ <p>The <code>mssql::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::mssql::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the SQL Server ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/mssql/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, SQLHDBC handle);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be created by default
+ with the min and max connections values set to <code>0</code>. The
+ following code fragment shows how we can pass our own connection factory
+ instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/mssql/database.hxx>
+#include &lt;odb/mssql/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::mssql::connection_factory> f (
+ new odb::mssql::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new mssql::database (argc, argv, false, "", 0, f));
+}
+ </pre>
+
+ <h2><a name="21.4">21.4 SQL Server Exceptions</a></h2>
+
+ <p>The SQL Server ODB runtime library defines the following
+ SQL Server-specific exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ class record
+ {
+ public:
+ SQLINTEGER
+ error () const;
+
+ const std::string&amp;
+ sqlstate () const;
+
+ const std::string&amp;
+ message () const;
+ };
+
+ typedef std::vector&lt;record> records;
+
+ typedef records::size_type size_type;
+ typedef records::const_iterator iterator;
+
+ iterator
+ begin () const;
+
+ iterator
+ end () const;
+
+ size_type
+ size () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+
+ class long_data_reload: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mssql/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::mssql::database_exception</code> is thrown if
+ an SQL Server database operation fails. The SQL Server-specific error
+ information is stored as a series of records, each containing
+ the error code as a signed 4-byte integer, the SQLSTATE code,
+ and the message string. All this information is also combined
+ and returned in a human-readable form by the <code>what()</code>
+ function.</p>
+
+ <p>The <code>odb::mssql::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::mssql::database</code>
+ class if the SQL Server option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <p>The <code>odb::mssql::long_data_reload</code> 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 <a href="#21.1">Section 21.1, "SQL Server Type Mapping"</a>.</p>
+
+ <h2><a name="21.5">21.5 SQL Server Limitations</a></h2>
+
+ <p>The following sections describe SQL Server-specific limitations imposed
+ by the current SQL Server and ODB runtime versions.</p>
+
+ <h3><a name="21.5.1">21.5.1 Query Result Caching</a></h3>
+
+ <p>SQL Server ODB runtime implementation does not perform query result
+ caching (<a href="#4.4">Section 4.4, "Query Result"</a>) even when
+ explicitly requested. The ODBC API and the SQL Server Native Client ODBC
+ driver support interleaving execution of multiple prepared statements
+ on a single connection. As a result, it is possible to have multiple
+ uncached results and calls to other database functions do not invalidate
+ them. The only limitation of the uncached SQL Server results is the
+ unavailability of the <code>result::size()</code> function. If you
+ call this function on an SQL Server query result, then the
+ <code>odb::result_not_cached</code> exception (<a href="#3.14">Section
+ 3.14, "ODB Exceptions"</a>) is always thrown. Future versions of the
+ SQL Server ODB runtime library may add support for result caching.</p>
+
+ <h3><a name="21.5.2">21.5.2 Foreign Key Constraints</a></h3>
+
+ <p>ODB assumes the standard SQL behavior which requires that foreign
+ key constraints checking is deferred until the transaction is
+ committed. The only behavior supported by SQL Server is to check
+ such constraints immediately. As a result, by default, schemas
+ generated by the ODB compiler for SQL Server have foreign key
+ definitions commented out. They are retained only for documentation.</p>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="21.5.3">21.5.3 Unique Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the ODBC error codes, it is impossible
+ to distinguish between the duplicate primary key and other unique
+ constraint violations. As a result, when making an object persistent,
+ the SQL Server ODB runtime will translate all unique constraint violation
+ errors to the <code>object_already_persistent</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="21.5.4">21.5.4 Multi-threaded Windows Applications</a></h3>
+
+ <p>Multi-threaded Windows applications must use the
+ <code>_beginthread()</code>/<code>_beginthreadex()</code> and
+ <code>_endthread()</code>/<code>_endthreadex()</code> CRT functions
+ instead of the <code>CreateThread()</code> and <code>EndThread()</code>
+ Win32 functions to start and terminate threads. This is a limitation of
+ the ODBC implementation on Windows.</p>
+
+ <h3><a name="21.5.5">21.5.5 Affected Row Count and DDL Statements</a></h3>
+
+ <p>SQL Server always returns zero as the number of affected rows
+ for DDL statements. In particular, this means that the
+ <code>database::execute()</code> (<a href="#3.12">Section 3.12,
+ "Executing Native SQL Statements"</a>) function will always
+ return zero for such statements.</p>
+
+ <h3><a name="21.5.6">21.5.6 Long Data and Auto Object Ids, <code>ROWVERSION</code></a></h3>
+
+ <p>SQL Server 2005 has a bug that causes it to fail on an <code>INSERT</code>
+ or <code>UPDATE</code> statement with the <code>OUTPUT</code> clause
+ (used to return automatically assigned object ids as well as
+ <code>ROWVERSION</code> values) if one of the inserted columns
+ is long data. The symptom of this bug in ODB is an exception thrown
+ by the <code>database::persist()</code> or <code>database::update()</code>
+ function when used on an object that contains long data and has an
+ automatically assigned object id or uses <code>ROWVERSION</code>-based
+ optimistic concurrency (<a href="#21.1.1">Section 21.1.1,
+ "<code>ROWVERSION</code> Support"</a>). The error message reads "This
+ operation conflicts with another pending operation on this transaction.
+ The operation failed."</p>
+
+ <p>For automatically assigned object ids ODB includes a workaround for
+ this bug which uses a less efficient method to obtain id values for
+ objects that contain long data. To enable this workaround you need
+ to specify that the generated code will be used with SQL Server 2005
+ or later by passing the <code>--mssql-server-version&nbsp;9.0</code>
+ ODB compiler option.</p>
+
+ <p>For <code>ROWVERSION</code>-based optimistic concurrency no workaround
+ is currently provided. The ODB compiler will issue an error for
+ objects that use <code>ROWVERSION</code> for optimistic concurrency
+ and containing long data.</p>
+
+ <h3><a name="21.5.7">21.5.7 Long Data and By-Value Accessors/Modifiers</a></h3>
+
+ <p>As discussed in <a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
+ accessor and modifier expressions cannot be used with data members
+ of long data types. The SQL Server ODB runtime uses streaming for
+ reading/writing long data directly from/to data members. As a result,
+ by-reference accessors and modifiers should be used for these data
+ types.</p>
+
+ <h3><a name="21.5.8">21.5.8 Bulk Update and <code>ROWVERSION</code></a></h3>
+
+ <p>The bulk update operation (<a href="#15.3">Section 15.3, "Bulk Database
+ Operations"</a>) is not yet supported for persistent classes that use
+ <code>ROWVERSION</code>-based optimistic concurrency. For such classes
+ the bulk <code>update()</code> function is not available. The bulk
+ persist and erase support is still provided.</p>
+
+ <h2><a name="21.6">21.6 SQL Server Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define an SQL Server index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>, <code>CLUSTERED</code>), the <code>method</code>
+ clause is not used, and the <code>options</code> clause specifies
+ additional index properties. The column options can be used to specify
+ the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index \
+ type("UNIQUE CLUSTERED") \
+ member(name_, "DESC") \
+ options("WITH(FILLFACTOR = 80)")
+};
+ </pre>
+
+ <h2><a name="21.7">21.7 SQL Server Stored Procedures</a></h2>
+
+ <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
+ can be used to call SQL Server stored procedures. For example, assuming
+ we are using the <code>person</code> class from <a href="#2">Chapter
+ 2, "Hello World Example"</a> (and the corresponding <code>person</code>
+ table), we can create a stored procedure that given the min and max
+ ages returns some information about all the people in that range:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.person_range (
+ @min_age SMALLINT,
+ @max_age SMALLINT)
+AS
+ SELECT age, first, last FROM person
+ WHERE age >= @min_age AND age &lt;= @max_age;
+ </pre>
+
+ <p>Given the above stored procedure we can then define an ODB view
+ that can be used to call it and retrieve its result:</p>
+
+ <pre class="cxx">
+#pragma db view query("EXEC person_range (?)")
+struct person_range
+{
+ unsigned short age;
+ std::string first;
+ std::string last;
+};
+ </pre>
+
+ <p>The following example shows how we can use the above view to
+ print the list of people in a specific age range:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person_range> query;
+typedef odb::result&lt;person_range> result;
+
+transaction t (db.begin ());
+
+result r (
+ db.query&lt;person_range> (
+ query::_val (1) + "," + query::_val (18)));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Note that as with all native views, the order and types of data members
+ must match those of columns in the <code>SELECT</code> list inside
+ the stored procedure.</p>
+
+ <p>There are also a number of limitations when it comes to calling
+ SQL Server stored procedures with ODB views. There is currently
+ no support for output parameters, however, this is planned for
+ a future version. In the meantime, to call a stored procedure
+ that has output parameters we have to use a wrapper procedure
+ that converts such parameters to a <code>SELECT</code>
+ result. For example, given the following procedure that
+ calculates the age range of the people in our database:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.person_age_range (
+ @min_age SMALLINT = NULL OUTPUT,
+ @max_age SMALLINT = NULL OUTPUT)
+AS
+ SELECT @min_age = MIN(age), @max_age = MAX(max) FROM person;
+ </pre>
+
+ <p>We can create a wrapper procedure like this:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.person_age_range_odb
+AS
+ DECLARE @min_age SMALLINT, @max_age SMALLINT;
+ EXEC person_age_range @min_age OUTPUT, @max_age OUTPUT;
+ SELECT @min_age, @max_age;
+ </pre>
+
+ <p>And a view like this:</p>
+
+ <pre class="cxx">
+#pragma db view query("EXEC person_age_range_odb")
+struct person_age_range
+{
+ unsigned short min_age;
+ unsigned short max_age;
+};
+ </pre>
+
+ <p>Which we can then use to call the stored procedure:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+person_age_range ar (db.query_value&lt;person_age_range> ());
+cerr &lt;&lt; ar.min_age &lt;&lt; " " &lt;&lt; ar.max_age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>In SQL Server, a stored procedure can produce multiple results.
+ For example, if a stored procedure executes several
+ <code>SELECT</code> statements, then the result of calling such
+ a procedure consists of two row sets, one for each <code>SELECT</code>
+ statement. Because such multiple row sets can contain varying number
+ and type of columns, they cannot be all extracted into a
+ single view. Consequently, these kind of stored procedures are
+ currently not supported.</p>
+
+ <p>A stored procedure may also produce no row sets at all. For
+ example, a stored procedure that only executes DML statements
+ would exhibit this behavior. To call such a procedure we use
+ an empty view, for example:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.insert_person (
+ @first VARCHAR(512),
+ @last VARCHAR(512),
+ @age SMALLINT)
+AS
+ INSERT INTO person(first, last, age)
+ VALUES(@first, @last, @age);
+ </pre>
+
+ <pre class="cxx">
+#pragma db view
+struct no_result {};
+
+transaction t (db.begin ());
+
+db.query_one&lt;no_result> (
+ "EXEC insert_person" +
+ query::_val ("John") + "," +
+ query::_val ("Doe") + "," +
+ query::_val (21));
+
+t.commit ();
+ </pre>
+
+ <p>Finally, an SQL Server stored procedure can also return an
+ integer status code. Similar to output parameters, this code
+ can only be observed by an ODB view if it is converted to a
+ <code>SELECT</code> result. For more information on how to
+ do this and for other examples of stored procedure calls,
+ refer to the <code>mssql/stored-proc</code> test in the
+ <code>odb-tests</code> package.</p>
+
+ <!-- PART -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="III">PART III&nbsp;&nbsp;
+ <span style="font-weight: normal;">PROFILES</span></a></h1>
+
+ <p>Part III covers the integration of ODB with popular C++ frameworks
+ and libraries. It consists of the following chapters.</p>
+
+ <table class="toc">
+ <tr><th>22</th><td><a href="#22">Profiles Introduction</a></td></tr>
+ <tr><th>23</th><td><a href="#23">Boost Profile</a></td></tr>
+ <tr><th>24</th><td><a href="#24">Qt Profile</a></td></tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="22">22 Profiles Introduction</a></h1>
+
+ <p>ODB profiles are a generic mechanism for integrating ODB with
+ widely-used C++ frameworks and libraries. A profile provides glue
+ code which allows you to seamlessly persist various components, such
+ as smart pointers, containers, and value types found in these
+ frameworks or libraries. The code necessary to implement a profile
+ is packaged into the so called profile library. For example, the
+ Boost profile implementation is provided by the <code>libodb-boost</code>
+ profile library.</p>
+
+ <p>Besides linking the profile library to our application, it is also
+ necessary to let the ODB compiler know which profiles we
+ are using. This is accomplished with the <code>--profile</code>
+ (or <code>-p</code> alias) option. For example:</p>
+
+ <pre class="terminal">
+odb --profile boost ...
+ </pre>
+
+ <p>Some profiles, especially those covering frameworks or libraries that
+ consist of multiple sub-libraries, provide sub-profiles that allow you
+ to pick and choose which components you would like to use in your
+ application. For example, the <code>boost</code> profile contains
+ the <code>boost/data-time</code> sub-profile. If we are only
+ interested in the <code>date_time</code> types, then we can
+ pass <code>boost/data-time</code> instead of <code>boost</code>
+ to the <code>--profile</code> option, for example:</p>
+
+ <pre class="terminal">
+odb --profile boost/date-time ...
+ </pre>
+
+ <p>To summarize, you will need to perform the following steps in order
+ to make use of a profile in your application:</p>
+
+ <ol>
+ <li>ODB compiler: if necessary, specify the path to the profile library
+ headers (<code>-I</code> option).</li>
+ <li>ODB compiler: specify the profile you would like to use with
+ the <code>--profile</code> option.</li>
+ <li>C++ compiler: if necessary, specify the path to the profile library
+ headers (normally <code>-I</code> option).</li>
+ <li>Linker: link the profile library to the application.</li>
+ </ol>
+
+ <p>The remaining chapters in this part of the manual describe the
+ standard profiles provided by ODB.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="23">23 Boost Profile</a></h1>
+
+ <p>The ODB profile implementation for Boost is provided by the
+ <code>libodb-boost</code> library and consists of multiple sub-profiles
+ corresponding to the individual Boost libraries. To enable all the
+ available Boost sub-profiles, pass <code>boost</code> as the profile
+ name to the <code>--profile</code> ODB compiler option. Alternatively,
+ you can enable only specific sub-profiles by passing individual
+ sub-profile names to <code>--profile</code>. The following sections in
+ this chapter discuss each Boost sub-profile in detail. The
+ <code>boost</code> example in the <code>odb-examples</code>
+ package shows how to enable and use the Boost profile.</p>
+
+ <p>Some sub-profiles may throw exceptions to indicate error conditions,
+ such as the inability to store a specific value in a particular database
+ system. All such exceptions derive from the
+ <code>odb::boost::exception</code> class which in turn derives from
+ the root of the ODB exception hierarchy, class <code>odb::exception</code>
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
+ <code>odb::boost::exception</code> class is defined in the
+ <code>&lt;odb/boost/exception.hxx></code> header file and has the
+ same interface as <code>odb::exception</code>. Concrete exceptions
+ that can be thrown by the Boost sub-profiles are described in the
+ following sections.</p>
+
+ <h2><a name="23.1">23.1 Smart Pointers Library</a></h2>
+
+ <p>The <code>smart-ptr</code> sub-profile provides persistence
+ support for a subset of smart pointers from the Boost
+ <code>smart_ptr</code> library. To enable only this profile,
+ pass <code>boost/smart-ptr</code> to the <code>--profile</code>
+ ODB compiler option.</p>
+
+ <p>The currently supported smart pointers are
+ <code>boost::shared_ptr</code> and <code>boost::weak_ptr</code>. For
+ more information on using smart pointers as pointers to objects and
+ views, refer to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>
+ and <a href="#6">Chapter 6, "Relationships"</a>. For more information
+ on using smart pointers as pointers to values, refer to
+ <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
+ Semantics"</a>. When used as a pointer to a value, only
+ <code>boost::shared_ptr</code> is supported. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db null
+ boost::shared_ptr&lt;std::string> middle_name_;
+};
+ </pre>
+
+ <p>To provide finer grained control over object relationship loading,
+ the <code>smart-ptr</code> sub-profile also provides the lazy
+ counterparts for the above pointers: <code>odb::boost::lazy_shared_ptr</code> and
+ <code>odb::boost::lazy_weak_ptr</code>. You will need to include the
+ <code>&lt;odb/boost/lazy-ptr.hxx></code> header file to make the lazy
+ variants available in your application. For a description of the lazy
+ pointer interface and semantics refer to <a href="#6.4">Section 6.4,
+ "Lazy Pointers"</a>. The following example shows how we can use these
+ smart pointers to establish a relationship between persistent objects.</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db inverse(position_)
+ odb::boost::lazy_weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ boost::shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>Besides providing persistence support for the above smart pointers,
+ the <code>smart-ptr</code> sub-profile also changes the default
+ pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
+ to <code>boost::shared_ptr</code>. In particular, this means that
+ database functions that return dynamically allocated objects and views
+ will return them as <code>boost::shared_ptr</code> pointers. To override
+ this behavior, add the <code>--default-pointer</code> option specifying
+ the alternative pointer type after the <code>--profile</code> option.</p>
+
+ <h2><a name="23.2">23.2 Unordered Containers Library</a></h2>
+
+ <p>The <code>unordered</code> sub-profile provides persistence support for
+ the containers from the Boost <code>unordered</code> library. To enable
+ only this profile, pass <code>boost/unordered</code> to
+ the <code>--profile</code> ODB compiler option.</p>
+
+ <p>The supported containers are <code>boost::unordered_set</code>,
+ <code>boost::unordered_map</code>, <code>boost::unordered_multiset</code>,
+ and <code>boost::unordered_multimap</code>. For more information on using
+ the set and multiset containers with ODB, refer to <a href="#5.2">Section
+ 5.2, "Set and Multiset Containers"</a>. For more information on using the
+ map and multimap containers with ODB, refer to <a href="#5.3"> Section
+ 5.3, "Map and Multimap Containers"</a>. The following example shows how
+ the <code>unordered_set</code> container may be used within a persistent
+ object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ boost::unordered_set&lt;std::string&gt; emails_;
+};
+ </pre>
+
+ <h2><a name="23.3">23.3 Multi-Index Container Library</a></h2>
+
+ <p>The <code>multi-index</code> sub-profile provides persistence support for
+ <code>boost::multi_index_container</code> from the Boost Multi-Index
+ library. To enable only this profile, pass <code>boost/multi-index</code>
+ to the <code>--profile</code> ODB compiler option. The following example
+ shows how <code>multi_index_container</code> may be used within a
+ persistent object.</p>
+
+ <pre class="cxx">
+namespace mi = boost::multi_index;
+
+#pragma db object
+class person
+{
+ ...
+
+ typedef
+ mi::multi_index_container&lt;
+ std::string,
+ mi::indexed_by&lt;
+ mi::sequenced&lt;>,
+ mi::ordered_unique&lt;mi::identity&lt;std::string> >
+ >
+ > emails;
+
+ emails emails_;
+};
+ </pre>
+
+ <p>Note that a <code>multi_index_container</code> instantiation is
+ stored differently in the database depending on whether it has
+ any <code>sequenced</code> or <code>random_access</code> indexes.
+ If it does, then it is treated as an ordered container
+ (<a href="#5.1">Section 5.1, "Ordered Containers"</a>) with the
+ first such index establishing the order. Otherwise, it is treated
+ as a set container (<a href="#5.2">Section 5.2, "Set and Multiset
+ Containers"</a>).</p>
+
+ <p>Note also that there is a terminology clash between ODB and Boost
+ Multi-Index. The ODB term <em>ordered container</em> translates
+ to Multi-Index terms <em>sequenced index</em> and <em>random access
+ index</em> while the ODB term <em>set container</em> translates
+ to Multi-Index terms <em>ordered index</em> and <em>hashed
+ index</em>.</p>
+
+ <p>The <code>emails</code> container from the above example is stored
+ as an ordered container. In contrast, the following <code>aliases</code>
+ container is stored as a set.</p>
+
+ <pre class="cxx">
+namespace mi = boost::multi_index;
+
+#pragma db value
+struct name
+{
+ std::string first;
+ std::string last;
+};
+
+bool operator&lt; (const name&amp;, const name&amp;);
+
+#pragma db object
+class person
+{
+ ...
+
+ typedef
+ mi::multi_index_container&lt;
+ name,
+ mi::indexed_by&lt;
+ mi::ordered_unique&lt;mi::identity&lt;name> >
+ mi::ordered_non_unique&lt;
+ mi::member&lt;name, std::string, &amp;name::first>
+ >,
+ mi::ordered_non_unique&lt;
+ mi::member&lt;name, std::string, &amp;name::last>
+ >
+ >
+ > aliases;
+
+ aliases aliases_;
+};
+ </pre>
+
+ <h2><a name="23.4">23.4 Optional Library</a></h2>
+
+ <p>The <code>optional</code> sub-profile provides persistence support for
+ the <code>boost::optional</code> container from the Boost
+ <code>optional</code> library. To enable only this profile, pass
+ <code>boost/optional</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>In a relational database <code>boost::optional</code> is mapped to
+ a column that can have a <code>NULL</code> value. Similar to
+ <code>odb::nullable</code> (<a href="#7.3">Section 7.3, "Pointers and
+ <code>NULL</code> Value Semantics"</a>), it can be used to add the
+ <code>NULL</code> semantics to existing C++ types. For example:</p>
+
+ <pre class="cxx">
+#include &lt;boost/optional.hpp>
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_; // TEXT NOT NULL
+ boost::optional&lt;std::string> middle_; // TEXT NULL
+ std::string last_; // TEXT NOT NULL
+};
+ </pre>
+
+ <p>Note also that similar to <code>odb::nullable</code>, when
+ this profile is used, the <code>NULL</code> values are automatically
+ enabled for data members of the <code>boost::optional</code> type.</p>
+
+ <h2><a name="23.5">23.5 Date Time Library</a></h2>
+
+ <p>The <code>date-time</code> sub-profile provides persistence support for a
+ subset of types from the Boost <code>date_time</code> library. It is
+ further subdivided into two sub-profiles, <code>gregorian</code>
+ and <code>posix_time</code>. The <code>gregorian</code> sub-profile
+ provides support for types from the <code>boost::gregorian</code>
+ namespace, while the <code>posix-time</code> sub-profile provides support
+ for types from the <code>boost::posix_time</code> namespace. To enable
+ the entire <code>date-time</code> sub-profile, pass
+ <code>boost/date-time</code> to the <code>--profile</code> ODB compiler
+ option. To enable only the <code>gregorian</code> sub-profile, pass
+ <code>boost/date-time/gregorian</code>, and to enable only the
+ <code>posix-time</code> sub-profile, pass
+ <code>boost/date-time/posix-time</code>.</p>
+
+ <p>The only type that the <code>gregorian</code> sub-profile currently
+ supports is <code>gregorian::date</code>. The types currently supported
+ by the <code>posix-time</code> sub-profile are
+ <code>posix_time::ptime</code> and
+ <code>posix_time::time_duration</code>. The manner in which these types
+ are persisted is database system dependent and is discussed in the
+ sub-sections that follow. The example below shows how
+ <code>gregorian::date</code> may be used within a persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ boost::gregorian::date date_of_birth_;
+};
+ </pre>
+
+ <p>Concrete exceptions that can be thrown by the <code>date-time</code>
+ sub-profile implementation are presented below.</p>
+
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace boost
+ {
+ namespace date_time
+ {
+ struct special_value: odb::boost::exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct value_out_of_range: odb::boost::exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+ }
+ }
+}
+ </pre>
+
+ <p>You will need to include the
+ <code>&lt;odb/boost/date-time/exceptions.hxx&gt;</code> header file to
+ make these exceptions available in your application.</p>
+
+ <p>The <code>special_value</code> exception is thrown if an attempt is made
+ to store a Boost date-time special value that cannot be represented in
+ the target database. The <code>value_out_of_range</code> exception is
+ thrown if an attempt is made to store a date-time value that is out of
+ the target database range. The specific conditions under which these
+ exceptions are thrown are database system dependent and are discussed in
+ more detail in the following sub-sections.</p>
+
+ <h3><a name="23.5.1">23.5.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the MySQL database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>DATETIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in a MySQL database.</p>
+
+ <p>The <code>posix-time</code> sub-profile implementation also provides
+ support for mapping <code>posix_time::ptime</code> to the
+ <code>TIMESTAMP</code> MySQL type. However, this mapping has to be
+ explicitly requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("TIMESTAMP") not_null
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Starting with MySQL version 5.6.4 it is possible to store fractional
+ seconds up to microsecond precision in <code>TIME</code>,
+ <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
+ to enable sub-second precision, the corresponding type with the
+ desired precision has to be specified explicitly, as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATETIME(6)") // Microsecond precision.
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Alternatively, you can enable sub-second precision on the per-type
+ basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(boost::posix_time::ptime) type("DATETIME(6)")
+
+#pragma db object
+class person
+{
+ ...
+ boost::posix_time::ptime created_; // Microsecond precision.
+ boost::posix_time::ptime updated_; // Microsecond precision.
+};
+ </pre>
+
+ <p>Some valid Boost date-time values cannot be stored in a MySQL database.
+ An attempt to persist any Boost date-time special value other than
+ <code>date_time::not_a_date_time</code> will result in the
+ <code>special_value</code> exception. An attempt to persist a Boost
+ date-time value that is out of the MySQL type range will result in
+ the <code>out_of_range</code> exception. Refer to the MySQL
+ documentation for more information on the MySQL data type ranges.</p>
+
+ <h3><a name="23.5.2">23.5.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the SQLite database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in an SQLite database.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>gregorian::date</code> and
+ <code>posix_time::ptime</code> to the <code>INTEGER</code> SQLite type,
+ with the integer value representing the UNIX time. Similarly, an
+ alternative mapping for <code>posix_time::time_duration</code> to the
+ <code>INTEGER</code> type represents the duration as a number of
+ seconds. These mappings have to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("INTEGER")
+ boost::gregorian::date born_;
+};
+ </pre>
+
+ <!--
+
+ <p>The Boost UNIX time interface does not support 64 bit time arithmetic.
+ As a result, the UNIX time representations of <code>gregorian::date</code>
+ and <code>posix_time::ptime</code> are restricted to the 32 bit range.
+ The minimum and maximum date representable by
+ <code>gregorian::date</code> is 1901-12-14 and 2038-01-19 respectively,
+ while the minimum and maximum date-time representable by
+ <code>posix_time::ptime</code> is 1901-12-13&nbsp;20:45:54 GMT and
+ 2038-01-19&nbsp;03:14:07&nbsp;GMT respectively. Persisting and loading
+ of values outside of these ranges will result in undefined behavior.</p>
+
+ -->
+
+ <p>Some valid Boost date-time values cannot be stored in an SQLite database.
+ An attempt to persist any Boost date-time special value other than
+ <code>date_time::not_a_date_time</code> will result in the
+ <code>special_value</code> exception. An attempt to persist a negative
+ <code>posix_time::time_duration</code> value as SQLite <code>TEXT</code>
+ will result in the <code>out_of_range</code> exception.</p>
+
+
+ <h3><a name="23.5.3">23.5.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the PostgreSQL database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>TIMESTAMP</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in a PostgreSQL database.
+ <code>posix_time::ptime</code> values representing the special values
+ <code>date_time::pos_infin</code> and <code>date_time::neg_infin</code>
+ are stored as the special PostgreSQL TIMESTAMP values
+ <code>infinity</code> and <code>-infinity</code>, respectively.</p>
+
+ <p>Some valid Boost date-time values cannot be stored in a PostgreSQL
+ database. The PostgreSQL TIME type represents a clock time, and can
+ therefore only store positive durations with a total length of time less
+ than 24 hours. An attempt to persist a
+ <code>posix_time::time_duration</code> value outside of this range will
+ result in the <code>value_out_of_range</code> exception. An attempt to
+ persist a <code>posix_time::time_duration</code> value representing any
+ special value other than <code>date_time::not_a_date_time</code> will
+ result in the <code>special_value</code> exception.</p>
+
+
+ <h3><a name="23.5.4">23.5.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the Oracle database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>TIMESTAMP</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>INTERVAL DAY TO SECOND</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in an Oracle database.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>posix_time::ptime</code> to the
+ <code>DATE</code> Oracle type with fractional seconds that may be
+ stored in a <code>ptime</code> instance being ignored. This
+ alternative mapping has to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATE")
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Some valid Boost date-time values cannot be stored in an Oracle database.
+ An attempt to persist a <code>gregorian::date</code>,
+ <code>posix_time::ptime</code>, or
+ <code>posix_time::time_duration</code> value representing any special
+ value other than <code>date_time::not_a_date_time</code> will result in
+ the <code>special_value</code> exception.</p>
+
+
+ <h3><a name="23.5.5">23.5.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the SQL Server database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>DATETIME2</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in an SQL Server database.</p>
+
+ <p>Note that the <code>DATE</code>, <code>TIME</code>, and
+ <code>DATETIME2</code> types are only available in SQL Server 2008 and
+ later. SQL Server 2005 only supports the <code>DATETIME</code> and
+ <code>SMALLDATETIME</code> date-time types. The new types are
+ also unavailable when connecting to an SQL Server 2008 or
+ later with the SQL Server 2005 Native Client ODBC driver.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation provides
+ support for mapping <code>posix_time::ptime</code> to the
+ <code>DATETIME</code> and <code>SMALLDATETIME</code> types,
+ however, this mapping has to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATETIME")
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Some valid Boost date-time values cannot be stored in an SQL Server
+ database. An attempt to persist a <code>gregorian::date</code>,
+ <code>posix_time::ptime</code>, or <code>posix_time::time_duration</code>
+ value representing any special value other than
+ <code>date_time::not_a_date_time</code> will result in the
+ <code>special_value</code> exception. The range of the <code>TIME</code>
+ type in SQL server is from <code>00:00:00.0000000</code> to
+ <code>23:59:59.9999999</code>. An attempt to persist a
+ <code>posix_time::time_duration</code> value out of this range will
+ result in the <code>value_out_of_range</code> exception.</p>
+
+ <h2><a name="23.6">23.6 Uuid Library</a></h2>
+
+ <p>The <code>uuid</code> sub-profile provides persistence support for the
+ <code>uuid</code> type from the Boost <code>uuid</code> library. To
+ enable only this profile, pass <code>boost/uuid</code> to the
+ <code>--profile</code> ODB compiler option.</p>
+
+ <p>The manner in which these types are persisted is database system
+ dependent and is discussed in the sub-sections that follow. By
+ default a data member of the <code>uuid</code> type is mapped to a
+ database column with <code>NULL</code> enabled and nil <code>uuid</code>
+ instances are stored as a <code>NULL</code> value. However, you can
+ change this behavior by declaring the data member <code>NOT NULL</code>
+ with the <code>not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
+ case, or if the data member is an object id, the implementation
+ will store nil <code>uuid</code> instances as zero UUID values
+ (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ boost::uuids::uuid x_; // Nil values stored as NULL.
+
+ #pragma db not_null
+ boost::uuids::uuid y_; // Nil values stored as zero.
+};
+ </pre>
+
+ <h3><a name="23.6.1">23.6.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the MySQL database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>BINARY(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.2">23.6.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the SQLite database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.3">23.6.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the PostgreSQL database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>UUID</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.4">23.6.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the Oracle database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>RAW(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.5">23.6.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the SQL Server database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>UNIQUEIDENTIFIER</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="24">24 Qt Profile</a></h1>
+
+ <p>The ODB profile implementation for Qt is provided by the
+ <code>libodb-qt</code> library. Both Qt4 and Qt5 as well
+ as C++98/03 and C++11 are supported.</p>
+
+ <p>The Qt profile consists of multiple sub-profiles
+ corresponding to the common type groups within Qt. Currently,
+ only types from the <code>QtCore</code> module are supported. To
+ enable all the available Qt sub-profiles, pass <code>qt</code> as the
+ profile name to the <code>--profile</code> ODB compiler option.
+ Alternatively, you can enable only specific sub-profiles by passing
+ individual sub-profile names to <code>--profile</code>. The following
+ sections in this chapter discuss each Qt sub-profile in detail. The
+ <code>qt</code> example in the <code>odb-examples</code>
+ package shows how to enable and use the Qt profile.</p>
+
+ <p>Some sub-profiles may throw exceptions to indicate error conditions,
+ such as the inability to store a specific value in a particular database
+ system. All such exceptions derive from the
+ <code>odb::qt::exception</code> class which in turn derives from
+ the root of the ODB exception hierarchy, class <code>odb::exception</code>
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
+ <code>odb::qt::exception</code> class is defined in the
+ <code>&lt;odb/qt/exception.hxx></code> header file and has the
+ same interface as <code>odb::exception</code>. Concrete exceptions
+ that can be thrown by the Qt sub-profiles are described in the
+ following sections.</p>
+
+ <h2><a name="24.1">24.1 Basic Types Library</a></h2>
+
+ <p>The <code>basic</code> sub-profile provides persistence support for basic
+ types defined by Qt. To enable only this profile, pass
+ <code>qt/basic</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported basic types are <code>QString</code>,
+ <code>QByteArray</code>, and <code>QUuid</code>. The manner in
+ which these types are persisted is database system dependent
+ and is discussed in the sub-sections that follow. The example
+ below shows how <code>QString</code> may be used within a
+ persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ QString name_;
+};
+ </pre>
+
+ <p>By default a data member of the <code>QUuid</code> type is mapped to a
+ database column with <code>NULL</code> enabled and null <code>QUuid</code>
+ instances are stored as a <code>NULL</code> value. However, you can
+ change this behavior by declaring the data member <code>NOT NULL</code>
+ with the <code>not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
+ case, or if the data member is an object id, the implementation
+ will store null <code>QUuid</code> instances as zero UUID values
+ (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ QUuid x_; // Null values stored as NULL.
+
+ #pragma db not_null
+ QUuid y_; // Null values stored as zero.
+};
+ </pre>
+
+ <h3><a name="24.1.1">24.1.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the MySQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>TEXT/VARCHAR(128)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>BINARY(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code>
+ types are stored as a <code>NULL</code> value if their
+ <code>isNull()</code> member function returns <code>true</code>.</p>
+
+ <p>Note also that the <code>QString</code> type is mapped
+ differently depending on whether a member of this type
+ is an object id or not. If the member is an object id,
+ then for this member <code>QString</code> is mapped
+ to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
+ it is mapped to <code>TEXT</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>,
+ <code>NCHAR</code>, and <code>NVARCHAR</code> MySQL types.
+ However, these alternative mappings have to be explicitly
+ requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("CHAR(2)") not_null
+ QString licenseState_;
+};
+ </pre>
+
+
+ <h3><a name="24.1.2">24.1.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the SQLite database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <h3><a name="24.1.3">24.1.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the PostgreSQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BYTEA</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>UUID</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>
+ and <code>VARCHAR</code> PostgreSQL types.
+ However, these alternative mappings have to be explicitly
+ requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("CHAR(2)") not_null
+ QString licenseState_;
+};
+ </pre>
+
+ <h3><a name="24.1.4">24.1.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the Oracle database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>VARCHAR2(512)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>RAW(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>,
+ <code>NCHAR</code>, <code>NVARCHAR</code>, <code>CLOB</code>, and
+ <code>NCLOB</code> Oracle types, and for mapping <code>QByteArray</code>
+ to the <code>RAW</code> Oracle type. However, these alternative
+ mappings have to be explicitly requested using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("CLOB") not_null
+ QString firstName_;
+
+ #pragma db type("RAW(16)") null
+ QByteArray uuid_;
+};
+ </pre>
+
+ <h3><a name="24.1.5">24.1.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the SQL Server database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>VARBINARY(max)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>UNIQUEIDENTIFIER</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <p>Note also that the <code>QString</code> type is mapped
+ differently depending on whether a member of this type
+ is an object id or not. If the member is an object id,
+ then for this member <code>QString</code> is mapped
+ to the <code>VARCHAR(256)</code> SQL Server type. Otherwise,
+ it is mapped to <code>VARCHAR(512)</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>,
+ <code>NCHAR</code>, <code>NVARCHAR</code>, <code>TEXT</code>, and
+ <code>NTEXT</code> SQL Server types, and for mapping
+ <code>QByteArray</code> to the <code>BINARY</code> and
+ <code>IMAGE</code> SQL Server types. However, these alternative
+ mappings have to be explicitly requested using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("NVARCHAR(256)") not_null
+ QString firstName_;
+
+ #pragma db type("BINARY(16)") null
+ QByteArray uuid_;
+};
+ </pre>
+
+ <h2><a name="24.2">24.2 Smart Pointers Library</a></h2>
+
+ <p>The <code>smart-ptr</code> sub-profile provides persistence support the
+ Qt smart pointers. To enable only this profile, pass
+ <code>qt/smart-ptr</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported smart pointers are
+ <code>QSharedPointer</code> and <code>QWeakPointer</code>.
+ For more information on using smart pointers as pointers to objects
+ and views, refer to <a href="#3.3">Section 3.3, "Object and View
+ Pointers"</a> and <a href="#6">Chapter 6, "Relationships"</a>. For
+ more information on using smart pointers as pointers to values, refer
+ to <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
+ Semantics"</a>. When used as a pointer to a value, only
+ <code>QSharedPointer</code> is supported. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db null
+ QSharedPointer&lt;QString> middle_name_;
+};
+ </pre>
+
+ <p>To provide finer grained control over object relationship loading,
+ the <code>smart-ptr</code> sub-profile also provides the lazy
+ counterparts for the above pointers: <code>QLazySharedPointer</code>
+ and <code>QLazyWeakPointer</code>. You will need to include the
+ <code>&lt;odb/qt/lazy-ptr.hxx></code> header file to make the lazy
+ variants available in your application. For a description of the lazy
+ pointer interface and semantics refer to <a href="#6.4">Section 6.4,
+ "Lazy Pointers"</a>. The following example shows how we can use these
+ smart pointers to establish a relationship between persistent objects.</p>
+
+ <pre class="cxx">
+class Employee;
+
+#pragma db object
+class Position
+{
+ ...
+
+ #pragma db inverse(position_)
+ QLazyWeakPointer&lt;Employee> employee_;
+};
+
+#pragma db object
+class Employee
+{
+ ...
+
+ #pragma db not_null
+ QSharedPointer&lt;Position> position_;
+};
+ </pre>
+
+ <p>Besides providing persistence support for the above smart pointers,
+ the <code>smart-ptr</code> sub-profile also changes the default
+ pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
+ to <code>QSharedPointer</code>. In particular, this means that
+ database functions that return dynamically allocated objects and views
+ will return them as <code>QSharedPointer</code> pointers. To override
+ this behavior, add the <code>--default-pointer</code> option specifying
+ the alternative pointer type after the <code>--profile</code> option.</p>
+
+ <h2><a name="24.3">24.3 Containers Library</a></h2>
+
+ <p>The <code>containers</code> sub-profile provides persistence support for
+ Qt containers. To enable only this profile, pass
+ <code>qt/containers</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported ordered containers are <code>QVector</code>,
+ <code>QList</code>, and <code>QLinkedList</code>. Supported map
+ containers are <code>QMap</code>, <code>QMultiMap</code>,
+ <code>QHash</code>, and <code>QMultiHash</code>. The supported set
+ container is <code>QSet</code>. For more information on using
+ containers with ODB, refer to <a href="#5">Chapter 5, "Containers"</a>.
+ The following example shows how the <code>QSet</code> container may
+ be used within a persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ QSet&lt;QString> emails_;
+};
+ </pre>
+
+ <p>The <code>containers</code> sub-profile also provide a change-tracking
+ equivalent for <code>QList</code> (<a href="#24.3.1">Section 24.3.1,
+ "Change-Tracking <code>QList</code>"</a>) with support for other Qt
+ container equivalents planned for future releases. For general information
+ on change-tracking containers refer to <a href="#5.4">Section 5.4,
+ "Change-Tracking Containers"</a>.</p>
+
+ <h3><a name="24.3.1">24.3.1 Change-Tracking <code>QList</code></a></h3>
+
+ <p>Class template <code>QOdbList</code>, defined in
+ <code>&lt;odb/qt/list.hxx></code>, is a change-tracking
+ equivalent for <code>QList</code>. It
+ is implemented in terms of <code>QList</code> and is
+ implicit-convertible to and implicit-constructible from
+ <code>const QList&amp;</code>. In particular, this
+ means that we can use <code>QOdbList</code> instance
+ anywhere <code>const QList&amp;</code> is
+ expected. In addition, <code>QOdbList</code> constant
+ iterator (<code>const_iterator</code>) is the same type as
+ that of <code>QList</code>.</p>
+
+ <p><code>QOdbList</code> incurs 2-bit per element overhead
+ in order to store the change state. It cannot
+ be stored unordered in the database (<a href="#14.4.19">Section
+ 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
+ side of a relationship (<a href="#6.2">6.2 "Bidirectional
+ Relationships"</a>). In this case, no change tracking is performed
+ since no state for such a container is stored in the database.</p>
+
+ <p>The number of database operations required to update the state
+ of <code>QOdbList</code> corresponds well to the complexity
+ of <code>QList</code> functions, except for
+ <code>prepend()</code>/<code>push_front()</code>. In particular, adding
+ or removing an element from the back of the list (for example,
+ with <code>append()</code>/<code>push_back()</code> and
+ <code>removeLast()</code>/<code>pop_back()</code>),
+ requires only a single database statement execution. In contrast,
+ inserting or erasing an element at the beginning or in the middle
+ of the list will require a database statement for every element that
+ follows it.</p>
+
+ <p><code>QOdbList</code> replicates most of the <code>QList</code>
+ interface as defined in both Qt4 and Qt5 and includes support for
+ C++11. However, functions and operators that provide direct write
+ access to the elements had to be altered or disabled in order to
+ support change tracking. Additional functions used to interface with
+ <code>QList</code> and to control the change tracking state
+ were also added. The following listing summarizes the differences
+ between the <code>QOdbList</code> and <code>QList</code>
+ interfaces. Any <code>QList</code> function or operator
+ not mentioned in this listing has exactly the same signature
+ and semantics in <code>QOdbList</code>. Functions and
+ operators that were disabled are shown as commented out and
+ are followed by functions/operators that replace them.</p>
+
+ <pre class="cxx">
+template &lt;typename T>
+class QOdbList
+{
+ ...
+
+ // Element access.
+ //
+
+ //T&amp; operator[] (int);
+ T&amp; modify (int);
+
+ //T&amp; first();
+ T&amp; modifyFirst();
+
+ //T&amp; last();
+ T&amp; modifyLast();
+
+ //T&amp; front();
+ T&amp; modify_front();
+
+ //T&amp; back();
+ T&amp; modify_back();
+
+ // Iterators.
+ //
+ typedef typename QList&lt;T>::const_iterator const_iterator;
+
+ class iterator
+ {
+ ...
+
+ // Element Access.
+ //
+
+ //reference operator* () const;
+ const_reference operator* () const;
+ reference modify () const;
+
+ //pointer operator-> () const;
+ const_pointer operator-> () const;
+
+ //reference operator[] (difference_type);
+ const_reference operator[] (difference_type);
+ reference modify (difference_type) const;
+
+ // Interfacing with QList::iterator.
+ //
+ typename QList&lt;T>::iterator base () const;
+ };
+
+ // Return QList iterators. The begin() functions mark all
+ // the elements as modified.
+ //
+ typename QList&lt;T>::iterator mbegin ();
+ typename QList&lt;T>::iterator modifyBegin ();
+ typename QList&lt;T>::iterator mend ();
+ typename QList&lt;T>::iterator modifyEnd ();
+
+ // Interfacing with QList.
+ //
+ QOdbList (const QList&lt;T>&amp;);
+ QOdbList (QList&lt;T>&amp;&amp;); // C++11 only.
+
+ QOdbList&amp; operator= (const QList&lt;T>&amp;);
+ QOdbList&amp; operator= (QList&lt;T>&amp;&amp;);
+
+ operator const QList&lt;T>&amp; () const;
+ QList&lt;T>&amp; base ();
+ const QList&lt;T>&amp; base () const;
+
+ // Change tracking.
+ //
+ bool _tracking () const;
+ void _start () const;
+ void _stop () const;
+ void _arm (transaction&amp;) const;
+};
+ </pre>
+
+ <p>The following example highlights some of the differences between
+ the two interfaces. <code>QList</code> versions are commented
+ out.</p>
+
+ <pre class="cxx">
+#include &lt;QtCore/QList>
+#include &lt;odb/qt/list.hxx>
+
+void f (const QList&lt;int>&amp;);
+
+QOdbList&lt;int> l ({1, 2, 3});
+
+f (l); // Ok, implicit conversion.
+
+if (l[1] == 2) // Ok, const access.
+ //l[1]++;
+ l.modify (1)++;
+
+//l.last () = 4;
+l.modifyLast () = 4;
+
+for (auto i (l.begin ()); i != l.end (); ++i)
+{
+ if (*i != 0) // Ok, const access.
+ //*i += 10;
+ i.modify () += 10;
+}
+
+qSort (l.modifyBegin (), l.modifyEnd ());
+ </pre>
+
+ <p>Note also the subtle difference between copy/move construction
+ and copy/move assignment of <code>QOdbList</code> instances.
+ While copy/move constructor will copy/move both the elements as
+ well as their change state, in contrast, assignment is tracked
+ as any other change to the vector content.</p>
+
+ <p>The <code>QListIterator</code> and <code>QMutableListIterator</code>
+ equivalents are also provided. These are <code>QOdbListIterator</code>
+ and <code>QMutableOdbListIterator</code> and are defined in
+ <code>&lt;odb/qt/list-iterator.hxx></code> and
+ <code>&lt;odb/qt/mutable-list-iterator.hxx></code>, respectively.</p>
+
+ <p><code>QOdbListIterator</code> has exactly the same interface and
+ semantics as <code>QListIterator</code>. In fact, we can use
+ <code>QListIterator</code> to iterate over a <code>QOdbList</code>
+ instance.</p>
+
+ <p><code>QMutableOdbListIterator</code> also has exactly the same
+ interface as <code>QMutableListIterator</code>. Note, however,
+ that any element that such an iterator passes over with the
+ call to <code>next()</code> is marked as modified.</p>
+
+ <h2><a name="24.4">24.4 Date Time Library</a></h2>
+
+ <p>The <code>date-time</code> sub-profile provides persistence support for
+ the Qt date-time types. To enable only this profile, pass
+ <code>qt/date-time</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported date-time types are <code>QDate</code>,
+ <code>QTime</code>, and <code>QDateTime</code>. The manner in which
+ these types are persisted is database system dependent and is
+ discussed in the sub-sections that follow. The example below shows how
+ <code>QDate</code> may be used within a persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ QDate dateOfBirth_;
+};
+ </pre>
+
+ <p>The single concrete exception that can be thrown by the
+ <code>date-time</code> sub-profile implementation is presented below.</p>
+
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace qt
+ {
+ namespace date_time
+ {
+ struct value_out_of_range: odb::qt::exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+ }
+ }
+}
+ </pre>
+
+ <p>You will need to include the
+ <code>&lt;odb/qt/date-time/exceptions.hxx&gt;</code> header file to
+ make this exception available in your application.</p>
+
+ <p>The <code>value_out_of_range</code> exception is thrown if an attempt
+ is made to store a date-time value that is out of the target database
+ range. The specific conditions under which it is thrown is database
+ system dependent and is discussed in more detail in the
+ following sub-sections.</p>
+
+ <h3><a name="24.4.1">24.4.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the MySQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>DATETIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>QDateTime</code> to the <code>TIMESTAMP</code>
+ MySQL type. However, this mapping has to be explicitly requested using
+ the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ #pragma db type("TIMESTAMP") not_null
+ QDateTime updated_;
+};
+ </pre>
+
+ <p>Starting with MySQL version 5.6.4 it is possible to store fractional
+ seconds up to microsecond precision in <code>TIME</code>,
+ <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
+ to enable sub-second precision, the corresponding type with the
+ desired precision has to be specified explicitly, as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ #pragma db type("DATETIME(3)") // Millisecond precision.
+ QDateTime updated_;
+};
+ </pre>
+
+ <p>Alternatively, you can enable sub-second precision on the per-type
+ basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(QDateTime) type("DATETIME(3)")
+
+#pragma db object
+class Person
+{
+ ...
+ QDateTime created_; // Millisecond precision.
+ QDateTime updated_; // Millisecond precision.
+};
+ </pre>
+
+ <p>Some valid Qt date-time values cannot be stored in a MySQL database. An
+ attempt to persist a Qt date-time value that is out of the MySQL type
+ range will result in the <code>out_of_range</code> exception. Refer to
+ the MySQL documentation for more information on the MySQL data type
+ ranges.</p>
+
+ <h3><a name="24.4.2">24.4.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the SQLite database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>QDate</code> and <code>QDateTime</code> to the
+ SQLite <code>INTEGER</code> type, with the integer value representing the
+ UNIX time. Similarly, an alternative mapping for <code>QTime</code> to
+ the <code>INTEGER</code> type represents a clock time as the number of
+ seconds since midnight. These mappings have to be explicitly requested
+ using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown
+ in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ #pragma db type("INTEGER")
+ QDate born_;
+};
+ </pre>
+
+ <p>Some valid Qt date-time values cannot be stored in an SQLite database.
+ An attempt to persist any Qt date-time value representing a negative UNIX
+ time (any point in time prior to the 1970-01-01&nbsp;00:00:00 UNIX time
+ epoch) as an SQLite <code>INTEGER</code> will result in the
+ <code>out_of_range</code> exception.</p>
+
+ <h3><a name="24.4.3">24.4.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the PostgreSQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>TIMESTAMP</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <h3><a name="24.4.4">24.4.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the Oracle database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>INTERVAL DAY(0) TO SECOND(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>TIMESTAMP(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>QDateTime</code> to the
+ <code>DATE</code> Oracle type with fractional seconds that may be
+ stored in a <code>QDateTime</code> instance being ignored. This
+ alternative mapping has to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATE")
+ QDateTime updated_;
+};
+ </pre>
+
+ <h3><a name="24.4.5">24.4.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the SQL Server database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TIME(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>DATETIME2(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>Note that the <code>DATE</code>, <code>TIME</code>, and
+ <code>DATETIME2</code> types are only available in SQL Server 2008 and
+ later. SQL Server 2005 only supports the <code>DATETIME</code> and
+ <code>SMALLDATETIME</code> date-time types. The new types are
+ also unavailable when connecting to an SQL Server 2008 or
+ later with the SQL Server 2005 Native Client ODBC driver.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation provides
+ support for mapping <code>QDateTime</code> to the <code>DATETIME</code>
+ and <code>SMALLDATETIME</code> types, however, this mapping has to
+ be explicitly requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as
+ shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATETIME")
+ QDateTime updated_;
+};
+ </pre>
+
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/odb/doc/odb-arch.png b/odb/doc/odb-arch.png
new file mode 100644
index 0000000..511b198
--- /dev/null
+++ b/odb/doc/odb-arch.png
Binary files differ
diff --git a/odb/doc/odb-arch.svg b/odb/doc/odb-arch.svg
new file mode 100644
index 0000000..368c223
--- /dev/null
+++ b/odb/doc/odb-arch.svg
@@ -0,0 +1,410 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg1947"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/boris/tmp"
+ sodipodi:docname="odb-arch.svg"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs1949">
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path3971"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.4) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleInS"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="TriangleInS"
+ style="overflow:visible">
+ <path
+ id="path4105"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(-0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="TriangleOutS"
+ style="overflow:visible">
+ <path
+ id="path4114"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Send"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Send"
+ style="overflow:visible;">
+ <path
+ id="path3998"
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(0.3) rotate(180) translate(-2.3,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Send"
+ style="overflow:visible;">
+ <path
+ id="path3980"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.2) rotate(180) translate(6,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleInL"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="TriangleInL"
+ style="overflow:visible">
+ <path
+ id="path4099"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(-0.8)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3965"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.8) translate(12.5,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Mend"
+ style="overflow:visible;">
+ <path
+ id="path3992"
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(0.6) rotate(180) translate(0,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mend"
+ style="overflow:visible;">
+ <path
+ id="path3974"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lend"
+ style="overflow:visible;">
+ <path
+ id="path3968"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.8) rotate(180) translate(12.5,0)" />
+ </marker>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective77" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.79999996"
+ inkscape:cx="325.54804"
+ inkscape:cy="759.16274"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1670"
+ inkscape:window-height="1025"
+ inkscape:window-x="0"
+ inkscape:window-y="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2450"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata1952">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="opacity:1">
+ <rect
+ style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:19.11361885;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4099"
+ width="356.4422"
+ height="85.467361"
+ x="203.24568"
+ y="56.645363"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <rect
+ style="opacity:1;fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.99446297;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4103"
+ width="153.07578"
+ height="33.819218"
+ x="-542.89569"
+ y="93.412643"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <text
+ xml:space="preserve"
+ style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="426.08978"
+ y="109.31374"
+ id="text4105"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9475703,1.0553307)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4125"
+ x="426.08978"
+ y="109.31374">Application Code</tspan></text>
+ <rect
+ style="opacity:1;fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.14485359;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4111"
+ width="138.53285"
+ height="34.538216"
+ x="-357.79926"
+ y="93.017075"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <text
+ xml:space="preserve"
+ style="font-size:13.70444584px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="232.20055"
+ y="108.67963"
+ id="text4113"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4120"
+ x="232.20055"
+ y="108.67963">Persistent Classes</tspan></text>
+ <rect
+ style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:25.07188988;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4061"
+ width="350.48395"
+ height="149.55804"
+ x="206.27087"
+ y="179.81218"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <rect
+ style="fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.85818291;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4035"
+ width="153.5531"
+ height="33.297592"
+ x="-543.32996"
+ y="214.04765"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <rect
+ style="fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.26772499;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4043"
+ width="137.97311"
+ height="35.082535"
+ x="-357.69339"
+ y="213.11913"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <text
+ xml:space="preserve"
+ style="font-size:12.72555733px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+ x="363.88818"
+ y="188.32079"
+ id="text4127"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4131"
+ x="363.88818"
+ y="188.32079">ODB</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12.72555733px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+ x="339.01544"
+ y="67.706772"
+ id="text4085"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4087"
+ x="339.01544"
+ y="67.706772">Application</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="417.64713"
+ y="223.02219"
+ id="text4165"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9475703,1.0553307)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4169"
+ x="417.64713"
+ y="223.02219">ODB Common Runtime</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="246.68355"
+ y="223.02219"
+ id="text4171"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9475703,1.0553307)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4177"
+ x="246.68355"
+ y="223.02219">Generated Code</tspan></text>
+ <rect
+ style="opacity:1;fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:28.23288155;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4179"
+ width="316.83414"
+ height="26.922894"
+ x="-539.57642"
+ y="287.25192"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <text
+ xml:space="preserve"
+ style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="331.11002"
+ y="289.35214"
+ id="text4181"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9475703,1.0553307)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4185"
+ x="331.11002"
+ y="289.35214">ODB MySQL Runtime</tspan></text>
+ <rect
+ style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:22.96506691;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4207"
+ width="352.76007"
+ height="33.008934"
+ x="205.11604"
+ y="368.49875"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091" />
+ <text
+ xml:space="preserve"
+ style="font-size:12.72555733px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+ x="318.12234"
+ y="392.35941"
+ id="text4201"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"><tspan
+ sodipodi:role="line"
+ id="tspan4205"
+ x="318.12234"
+ y="392.35941">MySQL Database</tspan></text>
+ </g>
+</svg>
diff --git a/odb/doc/odb-epilogue.1 b/odb/doc/odb-epilogue.1
new file mode 100644
index 0000000..e331b23
--- /dev/null
+++ b/odb/doc/odb-epilogue.1
@@ -0,0 +1,153 @@
+.\"
+.\" SQL NAME TRANSFORMATIONS
+.\"
+.SH SQL NAME TRANSFORMATIONS
+The ODB compiler provides a number of mechanisms for transforming
+automatically-derived SQL names, such as tables, columns, etc.,
+to match a specific naming convention. At the higher level, we can
+add a prefix to global names (tables and, for some databases,
+indexes and/or foreign keys) with the
+.B --table-prefix
+option. Similarly, we can specify custom suffixes for automatically-derived
+index
+.RB ( --index-suffix ;
+default is
+.BR _i ),
+foreign key
+.RB ( --fkey-suffix ;
+default is
+.BR _fk ),
+and sequence
+.RB ( --sequence-suffix ;
+default is
+.BR _seq )
+names. Finally, we can also convert all the names to upper or lower
+case with the
+.B --sql-name-case
+option (valid values are
+.B upper
+and
+.BR lower ).
+
+At the lower level we can specify a set of regular expressions to
+implement arbitrary transformations of the automatically-derived SQL
+names. If we want a particular regular expression only to apply to
+a specific name, for example, table or column, then we use one of the
+.B --\fIkind\fB-regex
+options, where
+.I kind
+can be
+.BR table ,
+.BR column ,
+.BR index ,
+.BR fkey ,
+.BR sequence ,
+or
+.BR statement .
+On the other hand, if we want our regular expressions to apply to all SQL
+names, then we use the
+.B --sql-name-regex
+option.
+
+The interaction between the higher and lower level transformations
+is as follows. Prefixes and suffixes are added first. Then the
+regular expression transformations are applied. Finally, if requested,
+the name is converted to upper or lower case. Note also that all of
+these transformations except for
+.B --table-prefix
+only apply to automatically-derived names. In other words, if a table,
+column, etc., name was explicitly specified with a pragma, then it
+is used as is, without applying any (except for the table prefix)
+transformations.
+
+The value for the
+.B --*-regex
+options is a Perl-like regular expression in the form
+.BI / pattern / replacement /\fR.
+Any character can be used as a delimiter instead of
+.B /
+and the delimiter can be escaped inside
+.I pattern
+and
+.I replacement
+with a backslash
+.RB ( \e ).
+You can also specify multiple regular expressions by repeating these
+options.
+
+All the regular expressions are tried in the order specified with the
+name-specific expressions (for example,
+.BR --table-regex)
+tried first followed by the generic expressions
+.RB ( --sql-name-regex ).
+The first expression that matches is used.
+
+As an example, consider a regular expression that transforms a class
+name in the form
+.B CFoo
+to a table name in the form
+.BR FOO:
+
+.B --table-regex '/C(.+)/\eU$1/'
+
+As a more interesting example, consider the transformation of class
+names that follow the upper camel case convention (for example,
+.BR FooBar )
+to table names that follow the underscore-separated, all upper case
+convention (for example,
+.BR FOO_BAR ).
+For this case we have to use separate expressions to handle one-word,
+two-word, etc., names:
+
+.B --table-regex '/([A-z][a-z]+)/\eU$1/'
+
+.B --table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\eU$1_$2/'
+
+See also the REGEX AND SHELL QUOTING section below.
+.\"
+.\" REGEX AND SHELL QUOTING
+.\"
+.SH REGEX AND SHELL QUOTING
+When entering a regular expression argument in the shell command line
+it is often necessary to use quoting (enclosing the argument in " "
+or ' ') in order to prevent the shell from interpreting certain
+characters, for example, spaces as argument separators and $ as
+variable expansions.
+
+Unfortunately it is hard to achieve this in a manner that is portable
+across POSIX shells, such as those found on GNU/Linux and UNIX, and
+Windows shell. For example, if you use " " for quoting you will get
+a wrong result with POSIX shells if your expression contains $. The
+standard way of dealing with this on POSIX systems is to use ' '
+instead. Unfortunately, Windows shell does not remove ' ' from
+arguments when they are passed to applications. As a result you may
+have to use ' ' for POSIX and " " for Windows ($ is not treated as
+a special character on Windows).
+
+Alternatively, you can save regular expression options into a file,
+one option per line, and use this file with the
+.B --options-file
+option. With this approach you don't need to worry about shell quoting.
+.\"
+.\" DIAGNOSTICS
+.\"
+.SH DIAGNOSTICS
+If the input file is not valid C++,
+.B odb
+will issue diagnostic messages to STDERR and exit with non-zero exit code.
+.\"
+.\" BUGS
+.\"
+.SH BUGS
+Send bug reports to the odb-users@codesynthesis.com mailing list.
+.\"
+.\" COPYRIGHT
+.\"
+.SH COPYRIGHT
+Copyright (c) $copyright$.
+
+Permission is granted to copy, distribute and/or modify this
+document under the terms of the GNU Free Documentation License,
+version 1.2; with no Invariant Sections, no Front-Cover Texts and
+no Back-Cover Texts. Copy of the license can be obtained from
+http://www.codesynthesis.com/licenses/fdl-1.3.txt
diff --git a/odb/doc/odb-epilogue.xhtml b/odb/doc/odb-epilogue.xhtml
new file mode 100644
index 0000000..9eba558
--- /dev/null
+++ b/odb/doc/odb-epilogue.xhtml
@@ -0,0 +1,126 @@
+ <h1>SQL NAME TRANSFORMATIONS</h1>
+
+ <p>The ODB compiler provides a number of mechanisms for transforming
+ automatically-derived SQL names, such as tables, columns, etc.,
+ to match a specific naming convention. At the higher level, we can
+ add a prefix to global names (tables and, for some databases,
+ indexes and/or foreign keys) with the <code><b>--table-prefix</b></code>
+ option. Similarly, we can specify custom suffixes for
+ automatically-derived
+ index (<code><b>--index-suffix</b></code>; default is <code><b>_i</b></code>),
+ foreign key (<code><b>--fkey-suffix</b></code>; default is <code><b>_fk</b></code>), and
+ sequence (<code><b>--sequence-suffix</b></code>; default is <code><b>_seq</b></code>)
+ names. Finally, we can also convert all the names to upper or lower
+ case with the <code><b>--sql-name-case</b></code> option (valid values
+ are <code><b>upper</b></code> and <code><b>lower</b></code>).</p>
+
+ <p>At the lower level we can specify a set of regular expressions to
+ implement arbitrary transformations of the automatically-derived SQL
+ names. If we want a particular regular expression only to apply to
+ a specific name, for example, table or column, then we use one of the
+ <code><b>--</b><i>kind</i><b>-regex</b></code> options, where
+ <code><i>kind</i></code> can be <code><b>table</b></code>,
+ <code><b>column</b></code>, <code><b>index</b></code>,
+ <code><b>fkey</b></code>, <code><b>sequence</b></code>, or
+ <code><b>statement</b></code>. On the other hand, if we want our
+ regular expressions to apply to all SQL names, then we use the
+ <code><b>--sql-name-regex</b></code> option.</p>
+
+ <p>The interaction between the higher and lower level transformations
+ is as follows. Prefixes and suffixes are added first. Then the
+ regular expression transformations are applied. Finally, if requested,
+ the name is converted to upper or lower case. Note also that all of
+ these transformations except for <code><b>--table-prefix</b></code>
+ only apply to automatically-derived names. In other words, if a table,
+ column, etc., name was explicitly specified with a pragma, then it
+ is used as is, without applying any (except for the table prefix)
+ transformations.</p>
+
+ <p>The value for the <code><b>--*-regex</b></code> options is a Perl-like
+ regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>.
+ Any character can be used as a delimiter instead of <code><b>/</b></code>
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can also specify multiple regular expressions by repeating these
+ options.</p>
+
+ <p>All the regular expressions are tried in the order specified with the
+ name-specific expressions (for example, <code><b>--table-regex</b></code>)
+ tried first followed by the generic expressions
+ (<code><b>--sql-name-regex</b></code>). The first expression that
+ matches is used.</p>
+
+ <p>As an example, consider a regular expression that transforms a class
+ name in the form <code><b>CFoo</b></code> to a table name in the
+ form <code><b>FOO</b></code>:</p>
+
+ <p><code><b>--table-regex '/C(.+)/\U$1/'</b></code></p>
+
+ <p>As a more interesting example, consider the transformation of class
+ names that follow the upper camel case convention (for example,
+ <code><b>FooBar</b></code>) to table names that follow the
+ underscore-separated, all upper case convention (for example,
+ <code><b>FOO_BAR</b></code>). For this case we have to use
+ separate expressions to handle one-word, two-word, etc.,
+ names:</p>
+
+ <p><code><b>--table-regex '/([A-z][a-z]+)/\U$1/'</b></code></p>
+ <p><code><b>--table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\U$1_$2/'</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p>
+
+ <h1>REGEX AND SHELL QUOTING</h1>
+
+ <p>When entering a regular expression argument in the shell
+ command line it is often necessary to use quoting (enclosing
+ the argument in <code><b>"&nbsp;"</b></code> or
+ <code><b>'&nbsp;'</b></code>) in order to prevent the shell
+ from interpreting certain characters, for example, spaces as
+ argument separators and <code><b>$</b></code> as variable
+ expansions.</p>
+
+ <p>Unfortunately it is hard to achieve this in a manner that is
+ portable across POSIX shells, such as those found on
+ GNU/Linux and UNIX, and Windows shell. For example, if you
+ use <code><b>"&nbsp;"</b></code> for quoting you will get a
+ wrong result with POSIX shells if your expression contains
+ <code><b>$</b></code>. The standard way of dealing with this
+ on POSIX systems is to use <code><b>'&nbsp;'</b></code> instead.
+ Unfortunately, Windows shell does not remove <code><b>'&nbsp;'</b></code>
+ from arguments when they are passed to applications. As a result you
+ may have to use <code><b>'&nbsp;'</b></code> for POSIX and
+ <code><b>"&nbsp;"</b></code> for Windows (<code><b>$</b></code> is
+ not treated as a special character on Windows).</p>
+
+ <p>Alternatively, you can save regular expression options into
+ a file, one option per line, and use this file with the
+ <code><b>--options-file</b></code> option. With this approach
+ you don't need to worry about shell quoting.</p>
+
+ <h1>DIAGNOSTICS</h1>
+
+ <p>If the input file is not valid C++, <code><b>odb</b></code>
+ will issue diagnostic messages to STDERR and exit with non-zero exit
+ code.</p>
+
+ <h1>BUGS</h1>
+
+ <p>Send bug reports to the
+ <a href="mailto:odb-users@codesynthesis.com">odb-users@codesynthesis.com</a> mailing list.</p>
+
+ </div>
+ <div id="footer">
+ Copyright &#169; $copyright$.
+
+ <div id="terms">
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the
+ <a href="http://codesynthesis.com/licenses/fdl-1.3.txt">GNU Free
+ Documentation License, version 1.3</a>; with no Invariant Sections,
+ no Front-Cover Texts and no Back-Cover Texts.
+ </div>
+ </div>
+</div>
+</body>
+</html>
diff --git a/odb/doc/odb-flow.png b/odb/doc/odb-flow.png
new file mode 100644
index 0000000..0063317
--- /dev/null
+++ b/odb/doc/odb-flow.png
Binary files differ
diff --git a/odb/doc/odb-flow.svg b/odb/doc/odb-flow.svg
new file mode 100644
index 0000000..292a121
--- /dev/null
+++ b/odb/doc/odb-flow.svg
@@ -0,0 +1,822 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg1947"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/boris/tmp"
+ sodipodi:docname="odb-flow.svg"
+ inkscape:export-filename="/home/boris/inkscape/odb-flow-t.png"
+ inkscape:export-xdpi="66.988091"
+ inkscape:export-ydpi="66.988091"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs1949">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient8420">
+ <stop
+ style="stop-color:#5679a6;stop-opacity:1;"
+ offset="0"
+ id="stop8422" />
+ <stop
+ style="stop-color:#5679a6;stop-opacity:0;"
+ offset="1"
+ id="stop8424" />
+ </linearGradient>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path3971"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.4) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleInS"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="TriangleInS"
+ style="overflow:visible">
+ <path
+ id="path4105"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(-0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="TriangleOutS"
+ style="overflow:visible">
+ <path
+ id="path4114"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Send"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Send"
+ style="overflow:visible;">
+ <path
+ id="path3998"
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(0.3) rotate(180) translate(-2.3,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Send"
+ style="overflow:visible;">
+ <path
+ id="path3980"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.2) rotate(180) translate(6,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleInL"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="TriangleInL"
+ style="overflow:visible">
+ <path
+ id="path4099"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(-0.8)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3965"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.8) translate(12.5,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Mend"
+ style="overflow:visible;">
+ <path
+ id="path3992"
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(0.6) rotate(180) translate(0,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mend"
+ style="overflow:visible;">
+ <path
+ id="path3974"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lend"
+ style="overflow:visible;">
+ <path
+ id="path3968"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.8) rotate(180) translate(12.5,0)" />
+ </marker>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective77" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8420"
+ id="linearGradient8435"
+ gradientUnits="userSpaceOnUse"
+ x1="-353.55341"
+ y1="187.9241"
+ x2="-348.25009"
+ y2="247.1443" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.5656854"
+ inkscape:cx="289.60025"
+ inkscape:cy="507.25221"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1670"
+ inkscape:window-height="1025"
+ inkscape:window-x="0"
+ inkscape:window-y="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2450"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata1952">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="opacity:1">
+ <g
+ id="g8170"
+ transform="matrix(1,0,0,1.0116606,-768.75431,-37.497017)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532">
+ <g
+ transform="translate(-136.11807,24.74874)"
+ id="g8121">
+ <g
+ id="g8116">
+ <rect
+ y="227.29126"
+ x="1335.5197"
+ height="79.829231"
+ width="85.835815"
+ id="rect8090"
+ style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:16.14982605;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ transform="matrix(1.3965811,0,0,0.9383887,-917.47809,-64.418294)"
+ d="M 1670.0001,346.73715 A 17.5,29.375002 0 1 1 1635.0001,346.73715 A 17.5,29.375002 0 1 1 1670.0001,346.73715 z"
+ sodipodi:ry="29.375002"
+ sodipodi:rx="17.5"
+ sodipodi:cy="346.73715"
+ sodipodi:cx="1652.5001"
+ id="path8092"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <rect
+ y="218.95593"
+ x="1317.5464"
+ height="77.754135"
+ width="70.679863"
+ id="rect8094"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:13.31936932;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" />
+ </g>
+ <rect
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:13.31936932;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ id="rect8096"
+ width="104.53996"
+ height="68.249184"
+ x="1324.8125"
+ y="254.09189" />
+ </g>
+ <rect
+ y="67.686447"
+ x="1309.4884"
+ height="496.32056"
+ width="94.349838"
+ id="rect7857"
+ style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:34.28735733;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="71.978958"
+ x="998.24951"
+ height="151.17961"
+ width="310.93619"
+ id="rect7859"
+ style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:42.29946136;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="210.06084"
+ x="1289.3831"
+ height="36.790138"
+ width="60.296692"
+ id="rect8168"
+ style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:9.18893147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <rect
+ style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00813448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7887"
+ width="99.316032"
+ height="483.52316"
+ x="-638.03534"
+ y="53.841568"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="598.49298"
+ y="269.48282"
+ id="text7889"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9196215,1.0874039)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ y="269.48282"
+ x="598.49298"
+ sodipodi:role="line"
+ id="tspan7897">C++ Source</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+ x="368.14328"
+ y="42.076069"
+ id="text7875"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan7879"
+ x="368.14328"
+ y="42.076069">Application Code</tspan></text>
+ <g
+ id="g8428"
+ transform="translate(-12.693573,-1.1112137)">
+ <g
+ inkscape:export-ydpi="66.254532"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ id="g8378">
+ <rect
+ style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7867"
+ width="100.25191"
+ height="120.85359"
+ x="-412.69357"
+ y="66.473396"
+ transform="scale(-1,1)" />
+ <rect
+ style="opacity:1;fill:url(#linearGradient8435);fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7869"
+ width="100.25191"
+ height="120.85359"
+ x="-404.69354"
+ y="74.327034"
+ transform="scale(-1,1)" />
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="335.10498"
+ y="131.25627"
+ id="text7871"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"><tspan
+ sodipodi:role="line"
+ id="tspan7873"
+ x="335.10498"
+ y="131.25627">C++ Header</tspan></text>
+ </g>
+ </g>
+ <g
+ style="opacity:1"
+ id="g8079"
+ transform="matrix(1.119828,0,0,1,-718.61594,-8.6768615)">
+ <g
+ transform="translate(-392.50002,-507.50002)"
+ id="g8053">
+ <rect
+ style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:25.7060318;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ id="rect8049"
+ width="220.90669"
+ height="60.921265"
+ x="-1453.0748"
+ y="775.9975"
+ transform="scale(-1,1)" />
+ <rect
+ style="opacity:1;fill:#6c98d0;fill-opacity:1;fill-rule:evenodd;stroke:#6c98d0;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ id="rect8051"
+ width="217.97319"
+ height="58.396206"
+ x="-1451.4867"
+ y="777.53906"
+ transform="scale(-1,1)" />
+ </g>
+ </g>
+ <rect
+ style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:29.97532463;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7901"
+ width="244.9944"
+ height="146.08546"
+ x="222.99557"
+ y="389.85779"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <rect
+ style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7905"
+ width="100.25191"
+ height="120.85359"
+ x="-334.76144"
+ y="409.26379"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <rect
+ style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7907"
+ width="100.25191"
+ height="120.85359"
+ x="-326.76141"
+ y="417.11743"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:13.99999993px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="252.10341"
+ y="454.42068"
+ id="text7909"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan7911"
+ x="252.10341"
+ y="454.42068">C++ Source</tspan></text>
+ <rect
+ style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7913"
+ width="100.25191"
+ height="120.85359"
+ x="-463.01337"
+ y="409.11737"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <rect
+ style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7915"
+ width="100.25191"
+ height="120.85359"
+ x="-455.01334"
+ y="416.97101"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:13.99999993px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="388.51886"
+ y="454.05215"
+ id="text7917"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan7919"
+ x="388.51886"
+ y="454.05215">C++ Header</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+ x="279.88074"
+ y="397.92328"
+ id="text7921"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan7923"
+ x="279.88074"
+ y="397.92328">Generated Code</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="301.90692"
+ y="279.349"
+ id="text8063"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan8065"
+ x="301.90692"
+ y="279.349">ODB Compiler</tspan></text>
+ <rect
+ style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:34.97430801;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ id="rect8229"
+ width="408.91873"
+ height="60.921265"
+ x="-634.43976"
+ y="609.82062"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <rect
+ style="opacity:1;fill:#6c98d0;fill-opacity:1;fill-rule:evenodd;stroke:#6c98d0;stroke-width:34.01371765;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ id="rect8231"
+ width="403.48856"
+ height="58.396206"
+ x="-631.5"
+ y="611.36218"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="393.47134"
+ y="609.98444"
+ id="text8237"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan8241"
+ x="393.47134"
+ y="609.98444">C++ Compiler</tspan></text>
+ <rect
+ style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:23.70592499;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect7983"
+ width="251.26381"
+ height="89.08802"
+ x="391.23618"
+ y="738.27417"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+ x="423.17896"
+ y="754.1955"
+ id="text8003"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan8035"
+ x="423.17896"
+ y="754.1955">ODB Runtime Libraries</tspan></text>
+ <rect
+ style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect8021"
+ width="89.33712"
+ height="59.938789"
+ x="-483.33713"
+ y="767.36218"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="438.39462"
+ y="754.94214"
+ id="text8023"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ y="754.94214"
+ x="438.39462"
+ sodipodi:role="line"
+ id="tspan8025">libodb</tspan></text>
+ <rect
+ style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect8029"
+ width="124.21878"
+ height="59.82045"
+ x="-639"
+ y="767.36218"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="562.58875"
+ y="754.94214"
+ id="text8031"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ y="754.94214"
+ x="562.58875"
+ sodipodi:role="line"
+ id="tspan8033">libodb-mysql</tspan></text>
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:34.97430801;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ id="rect8289"
+ width="408.91873"
+ height="60.921265"
+ x="-635.43976"
+ y="895.82062"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <rect
+ style="fill:#6c98d0;fill-opacity:1;fill-rule:evenodd;stroke:#6c98d0;stroke-width:34.01371765;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1"
+ id="rect8291"
+ width="403.48856"
+ height="58.396206"
+ x="-632.49994"
+ y="897.36218"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:17.99999991px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="421.74155"
+ y="880.25012"
+ id="text8293"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan8299"
+ x="421.74155"
+ y="880.25012">Linker</tspan></text>
+ <rect
+ style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.8308351;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect8336"
+ width="443.51535"
+ height="91.56765"
+ x="-652.71075"
+ y="1007.2007"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="364.17606"
+ y="997.43353"
+ id="text8338"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan8340"
+ x="364.17606"
+ y="997.43353">Application Executable</tspan></text>
+ <rect
+ style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.81996387;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect8311"
+ width="100.43195"
+ height="81.109138"
+ x="-165.93936"
+ y="1008.9133"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <rect
+ style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.81996387;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect8355"
+ width="100.43195"
+ height="81.109138"
+ x="-157.49997"
+ y="1017.3622"
+ transform="scale(-1,1)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532" />
+ <text
+ xml:space="preserve"
+ style="font-size:13.99999993px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold"
+ x="81.206375"
+ y="994.76172"
+ id="text8315"
+ sodipodi:linespacing="100%"
+ transform="scale(0.9420736,1.0614882)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan8317"
+ x="81.206375"
+ y="994.76172">Database</tspan><tspan
+ sodipodi:role="line"
+ id="tspan8319"
+ x="81.206375"
+ y="1008.4662"> Schema</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 342.29515,194.11129 L 342.51612,242.94585"
+ id="path8437"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 261.25001,334.23715 L 261.25001,406.73715"
+ id="path9008"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 429.37502,334.86215 L 429.37502,406.11215"
+ id="path9010"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 539.06252,132.36214 L 403.49283,131.7975"
+ id="path10721"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 539.37502,479.23716 L 465.93752,479.23716"
+ id="path11861"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 590.62503,537.98716 L 590.62503,588.92466"
+ id="path12432"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 276.37501,537.98716 L 276.37501,589.86216"
+ id="path13003"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 291.25001,688.61217 L 290.62501,874.23717"
+ id="path13574"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 439.37502,826.11217 L 439.37502,875.48717"
+ id="path14145"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 576.87503,826.73717 L 577.50003,874.86217"
+ id="path14716"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
+ d="M 430.82781,972.76525 L 430.88016,1006.2853"
+ id="path15287"
+ inkscape:connector-type="polyline"
+ inkscape:connection-start="#rect8291"
+ inkscape:connection-end="#rect8336" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 209.48039,291.33848 L 129.93088,291.33849 C 123.50431,291.55946 118.76084,290.51681 114.00562,295.03073 C 110.26728,299.9874 110.72483,302.73691 110.54068,308.73995 L 111.36932,1009.0519"
+ id="path15858"
+ inkscape:connector-type="polyline"
+ sodipodi:nodetypes="ccccc" />
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+ x="461.78705"
+ y="473.59952"
+ id="text16431"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan16437"
+ x="461.78705"
+ y="473.59952">#include</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
+ x="431.71301"
+ y="123.92746"
+ id="text16439"
+ sodipodi:linespacing="100%"
+ transform="scale(1.0051164,0.9949096)"
+ inkscape:export-filename="/home/boris/inkscape/g8360.png"
+ inkscape:export-xdpi="66.254532"
+ inkscape:export-ydpi="66.254532"><tspan
+ sodipodi:role="line"
+ id="tspan16441"
+ x="431.71301"
+ y="123.92746">#include</tspan></text>
+ </g>
+</svg>
diff --git a/odb/doc/odb-prologue.1 b/odb/doc/odb-prologue.1
new file mode 100644
index 0000000..24e83f4
--- /dev/null
+++ b/odb/doc/odb-prologue.1
@@ -0,0 +1,84 @@
+.\" Process this file with
+.\" groff -man -Tascii odb.1
+.\"
+.TH ODB 1 "February 2015" "ODB 2.4.0"
+.SH NAME
+odb \- object-relational mapping (ORM) compiler for C++
+.\"
+.\"
+.\"
+.\"--------------------------------------------------------------------
+.SH SYNOPSIS
+.\"--------------------------------------------------------------------
+.B odb
+.B [
+.I options
+.B ]
+.I file
+.B [
+.IR file...
+.B ]
+.\"
+.\"
+.\"
+.\"--------------------------------------------------------------------
+.SH DESCRIPTION
+.\"--------------------------------------------------------------------
+Given a set of C++ classes in a header file,
+.B odb
+generates C++ code that allows you to persist, query, and update objects
+of these classes in a relational database (RDBMS). The relational
+database that the generated code should target is specified with the
+required
+.B --database
+option (see below).
+
+
+For an input file in the form
+.B name.hxx
+(other file extensions can be used instead of
+.BR .hxx ),
+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
+(inline file), and
+.B name-odb.cxx
+(source file). Additionally, if the
+.B --generate-schema
+option is specified and the
+.B sql
+schema format is requested (see
+.BR --schema-format ),
+the
+.B name.sql
+database schema file is generated. If the
+.B separate
+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).
+.\"
+.\"
+.\"
+.\"--------------------------------------------------------------------
+.SH OPTIONS
+.\"--------------------------------------------------------------------
diff --git a/odb/doc/odb-prologue.xhtml b/odb/doc/odb-prologue.xhtml
new file mode 100644
index 0000000..b8cc694
--- /dev/null
+++ b/odb/doc/odb-prologue.xhtml
@@ -0,0 +1,88 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+
+<head>
+ <title>ODB 2.4.0 Compiler Command Line Manual</title>
+
+ <meta name="copyright" content="&#169; $copyright$"/>
+ <meta name="keywords" content="odb,object,relational,mapping,compiler,c++"/>
+ <meta name="description" content="ODB Compiler Command Line Manual"/>
+
+ <link rel="stylesheet" type="text/css" href="default.css" />
+
+<style type="text/css">
+
+ #synopsis {
+ list-style-type: none;
+ }
+
+ #synopsis li {
+ padding-top : 0.0em;
+ padding-bottom : 0.0em;
+ }
+
+ .options {
+ margin: 1em 0 1em 0;
+ }
+
+ .options dt {
+ margin: 1em 0 0 0;
+ }
+
+ .options dd {
+ margin: .1em 0 0 4.5em;
+ }
+
+</style>
+</head>
+
+<body>
+<div id="container">
+ <div id="content">
+
+ <h1>NAME</h1>
+
+ <p>odb - object-relational mapping (ORM) compiler for C++</p>
+
+ <h1>SYNOPSIS</h1>
+
+ <dl id="synopsis">
+ <dt><code><b>odb</b> [<i>options</i>] <i>file</i> [<i>file</i>...]</code></dt>
+ </dl>
+
+ <h1>DESCRIPTION</h1>
+
+ <p>Given a set of C++ classes in a header file, <code><b>odb</b></code>
+ generates C++ code that allows you to persist, query, and update objects
+ of these classes in a relational database (RDBMS). The relational
+ database that the generated code should target is specified with the
+ required <code><b>--database</b></code> option (see below).</p>
+
+ <p>For an input file in the form <code><b>name.hxx</b></code> (other
+ file extensions can be used instead of <code><b>.hxx</b></code>),
+ in the single-database mode (the default), the generated C++ files
+ by default have the following names:
+ <code><b>name-odb.hxx</b></code> (header file),
+ <code><b>name-odb.ixx</b></code> (inline file), and
+ <code><b>name-odb.cxx</b></code> (source file).
+
+ Additionally, if the <code><b>--generate-schema</b></code> option is
+ specified and the <code><b>sql</b></code> schema format is requested (see
+ <code><b>--schema-format</b></code>), the <code><b>name.sql</b></code>
+ database schema file is generated. If the <code><b>separate</b></code>
+ schema format is requested, the database creation code is generated
+ into the separate <code><b>name-schema.cxx</b></code> file.</p>
+
+ <p>In the multi-database mode (see the <code><b>--multi-database</b></code>
+ option below), the generated files corresponding to the
+ <code><b>common</b></code> database have the same names as in the
+ single-database mode. For other databases, the file names include
+ the database name:
+ <code><b>name-odb-</b><i>db</i><b>.hxx</b></code>,
+ <code><b>name-odb-</b><i>db</i><b>.ixx</b></code>,
+ <code><b>name-odb-</b><i>db</i><b>.cxx</b></code>,
+ <code><b>name-</b><i>db</i><b>.sql</b></code>, and
+ <code><b>name-schema-</b><i>db</i><b>.cxx</b></code>
+ (where <code><i>db</i></code> is the database name).</p>
+
+ <h1>OPTIONS</h1>
diff --git a/odb/doc/pregenerated/odb.1 b/odb/doc/pregenerated/odb.1
new file mode 100644
index 0000000..884c937
--- /dev/null
+++ b/odb/doc/pregenerated/odb.1
@@ -0,0 +1,799 @@
+.\" Process this file with
+.\" groff -man -Tascii odb.1
+.\"
+.TH ODB 1 "February 2015" "ODB 2.4.0"
+.SH NAME
+odb \- object-relational mapping (ORM) compiler for C++
+.\"
+.\"
+.\"
+.\"--------------------------------------------------------------------
+.SH SYNOPSIS
+.\"--------------------------------------------------------------------
+.B odb
+.B [
+.I options
+.B ]
+.I file
+.B [
+.IR file...
+.B ]
+.\"
+.\"
+.\"
+.\"--------------------------------------------------------------------
+.SH DESCRIPTION
+.\"--------------------------------------------------------------------
+Given a set of C++ classes in a header file,
+.B odb
+generates C++ code that allows you to persist, query, and update objects
+of these classes in a relational database (RDBMS). The relational
+database that the generated code should target is specified with the
+required
+.B --database
+option (see below).
+
+
+For an input file in the form
+.B name.hxx
+(other file extensions can be used instead of
+.BR .hxx ),
+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
+(inline file), and
+.B name-odb.cxx
+(source file). Additionally, if the
+.B --generate-schema
+option is specified and the
+.B sql
+schema format is requested (see
+.BR --schema-format ),
+the
+.B name.sql
+database schema file is generated. If the
+.B separate
+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).
+.\"
+.\"
+.\"
+.\"--------------------------------------------------------------------
+.SH OPTIONS
+.\"--------------------------------------------------------------------
+.IP "\fB--help\fR"
+Print usage information and exit\.
+.IP "\fB--version\fR"
+Print version and exit\.
+.IP "\fB-I\fR \fIdir\fR"
+Add \fIdir\fR to the beginning of the list of directories to be searched for
+included header files\.
+.IP "\fB-D\fR \fIname\fR[=\fIdef\fR]"
+Define macro \fIname\fR with definition \fIdef\fR\. If definition is omitted,
+define \fIname\fR to be 1\.
+.IP "\fB-U\fR \fIname\fR"
+Cancel any previous definitions of macro \fIname\fR, either built-in or
+provided with the \fB-D\fR option\.
+.IP "\fB--database\fR|\fB-d\fR \fIdb\fR"
+Generate code for the \fIdb\fR database\. Valid values are \fBmssql\fR,
+\fBmysql\fR, \fBoracle\fR, \fBpgsql\fR, \fBsqlite\fR, and \fBcommon\fR
+(multi-database mode only)\.
+.IP "\fB--multi-database\fR|\fB-m\fR \fItype\fR"
+Enable multi-database support and specify its type\. Valid values for this
+option are \fBstatic\fR and \fBdynamic\fR\.
+
+In the multi-database mode, options that determine the kind (for example,
+\fB--schema-format\fR), names (for example, \fB--odb-file-suffix\fR), 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,
+\fBmysql:value\fR\. This restricts the value of such an option to only apply
+to generated files corresponding to this database\.
+.IP "\fB--default-database\fR \fIdb\fR"
+When static multi-database support is used, specify the database that should
+be made the default\. When dynamic multi-database support is used,
+\fBcommon\fR is always made the default database\.
+.IP "\fB--generate-query\fR|\fB-q\fR"
+Generate query support code\. Without this support you cannot use views and
+can only load objects via their ids\.
+.IP "\fB--generate-prepared\fR"
+Generate prepared query execution support code\.
+.IP "\fB--omit-unprepared\fR"
+Omit un-prepared (once-off) query execution support code\.
+.IP "\fB--generate-session\fR|\fB-e\fR"
+Generate session support code\. With this option session support will be
+enabled by default for all the persistent classes except those for which it
+was explicitly disabled using the \fBdb session\fR pragma\.
+.IP "\fB--generate-schema\fR|\fB-s\fR"
+Generate the database schema\. The database schema contains SQL statements
+that create database tables necessary to store persistent classes defined in
+the file being compiled\. Note that by applying this schema, all the existing
+information stored in such tables will be lost\.
+
+Depending on the database being used (\fB--database\fR option), the schema is
+generated either as a standalone SQL file or embedded into the generated C++
+code\. By default the SQL file is generated for the MySQL, PostgreSQL, Oracle,
+and Microsoft SQL Server databases and the schema is embedded into the C++
+code for the SQLite database\. Use the \fB--schema-format\fR option to alter
+the default schema format\.
+
+If database schema evolution support is enabled (that is, the object model
+version is specified), then this option also triggers the generation of
+database schema migration statements, again either as standalong SQL files or
+embedded into the generated C++ code\. You can suppress the generation of
+schema migration statements by specifying the \fB--suppress-migration\fR
+option\.
+.IP "\fB--generate-schema-only\fR"
+Generate only the database schema\. Note that this option is only valid when
+generating schema as a standalone SQL file (see \fB--schema-format\fR for
+details)\.
+.IP "\fB--suppress-migration\fR"
+Suppress the generation of database schema migration statements\.
+.IP "\fB--suppress-schema-version\fR"
+Suppress the generation of schema version table\. If you specify this option
+then you are also expected to manually specify the database schema version and
+migration state at runtime using the \fBodb::database::schema_version()\fR
+function\.
+.IP "\fB--schema-version-table\fR \fIname\fR"
+Specify the alternative schema version table name instead of the default
+\fBschema_version\fR\. If you specify this option then you are also expected
+to manually specify the schema version table name at runtime using the
+\fBodb::database::schema_version_table()\fR function\. The table name can be
+qualified\.
+.IP "\fB--schema-format\fR \fIformat\fR"
+Generate the database schema in the specified format\. Pass \fBsql\fR as
+\fIformat\fR to generate the database schema as a standalone SQL file or pass
+\fBembedded\fR to embed the schema into the generated C++ code\. The
+\fBseparate\fR value is similar to \fBembedded\fR except the schema creation
+code is generated into a separate C++ file (\fBname-schema\.cxx\fR by
+default)\. This value is primarily useful if you want to place the schema
+creation functionality into a separate program or library\. Repeat this option
+to generate the same database schema in multiple formats\.
+.IP "\fB--omit-drop\fR"
+Omit \fBDROP\fR statements from the generated database schema\.
+.IP "\fB--omit-create\fR"
+Omit \fBCREATE\fR statements from the generated database schema\.
+.IP "\fB--schema-name\fR \fIname\fR"
+Use \fIname\fR as the database schema name\. Schema names are primarily used
+to distinguish between multiple embedded schemas in the schema catalog\. They
+are not to be confused with database schemas (database namespaces) which are
+specified with the \fB--schema\fR option\. If this option is not specified,
+the empty name, which is the default schema name, is used\.
+.IP "\fB--fkeys-deferrable-mode\fR \fIm\fR"
+Use constraint checking mode \fIm\fR in foreign keys generated for object
+relationships\. Valid values for this option are \fBnot_deferrable\fR,
+\fBimmediate\fR, and \fBdeferred\fR (default)\. MySQL and SQL Server do not
+support deferrable foreign keys and for these databases such keys are
+generated commented out\. Other foreign keys generated by the ODB compiler
+(such as the ones used to support containers and polymorphic hierarchies) are
+always generated as not deferrable\.
+
+Note also that if you use either \fBnot_deferrable\fR or \fBimmediate\fR mode,
+then the order in which you persist, update, and erase objects within a
+transaction becomes important\.
+.IP "\fB--default-pointer\fR \fIptr\fR"
+Use \fIptr\fR as the default pointer for persistent objects and views\.
+Objects and views that do not have a pointer assigned with the \fBdb
+pointer\fR pragma will use this pointer by default\. The value of this option
+can be '\fB*\fR' which denotes the raw pointer and is the default, or
+qualified name of a smart pointer class template, for example,
+\fBstd::shared_ptr\fR\. In the latter case, the ODB compiler constructs the
+object or view pointer by adding a single template argument of the object or
+view type to the qualified name, for example \fBstd::shared_ptr<object>\fR\.
+The ODB runtime uses object and view pointers to return, and, in case of
+objects, pass and cache dynamically allocated instances of object and view
+types\.
+
+Except for the raw pointer and the standard smart pointers defined in the
+\fB<memory>\fR header file, you are expected to include the definition of the
+default pointer at the beginning of the generated header file\. There are two
+common ways to achieve this: you can either include the necessary header in
+the file being compiled or you can use the \fB--hxx-prologue\fR option to add
+the necessary \fB#include\fR directive to the generated code\.
+.IP "\fB--session-type\fR \fItype\fR"
+Use \fItype\fR as the alternative session type instead of the default
+\fBodb::session\fR\. This option can be used to specify a custom session
+implementation to be use by the persistent classes\. Note that you will also
+need to include the definition of the custom session type into the generated
+header file\. This is normally achieved with the \fB--hxx-prologue*\fR
+options\.
+.IP "\fB--profile\fR|\fB-p\fR \fIname\fR"
+Specify a profile that should be used during compilation\. A profile is an
+options file\. The ODB compiler first looks for a database-specific version
+with the name constructed by appending the
+\fB-\fR\fIdatabase\fR\fB\.options\fR suffix to \fIname\fR, where
+\fIdatabase\fR is the database name as specified with the \fB--database\fR
+option\. If this file is not found, then the ODB compiler looks for a
+database-independant version with the name constructed by appending just the
+\fB\.options\fR suffix\.
+
+The profile options files are searched for in the same set of directories as
+C++ headers included with the \fB#include <\.\.\.>\fR directive (built-in
+paths plus those specified with the \fB-I\fR options)\. The options file is
+first searched for in the directory itself and then in its \fBodb/\fR
+subdirectory\.
+
+For the format of the options file refer to the \fB--options-file\fR option
+below\. You can repeat this option to specify more than one profile\.
+.IP "\fB--at-once\fR"
+Generate code for all the input files as well as for all the files that they
+include at once\. The result is a single set of source/schema files that
+contain all the generated code\. If more than one input file is specified
+together with this option, then the \fB--input-name\fR option must also be
+specified in order to provide the base name for the output files\. In this
+case, the directory part of such a base name is used as the location of the
+combined file\. This can be important for the \fB#include\fR directive
+resolution\.
+.IP "\fB--schema\fR \fIschema\fR"
+Specify a database schema (database namespace) that should be assigned to the
+persistent classes in the file being compiled\. Database schemas are not to be
+confused with database schema names (schema catalog names) which are specified
+with the \fB--schema-name\fR option\.
+.IP "\fB--export-symbol\fR \fIsymbol\fR"
+Insert \fIsymbol\fR in places where DLL export/import control statements
+(\fB__declspec(dllexport/dllimport)\fR) are necessary\. See also the
+\fB--extern-symbol\fR option below\.
+.IP "\fB--extern-symbol\fR \fIsymbol\fR"
+If \fIsymbol\fR is defined, insert it in places where a template instantiation
+must be declared \fBextern\fR\. This option is normally used together with
+\fB--export-symbol\fR when both multi-database support and queries are
+enabled\.
+.IP "\fB--std\fR \fIversion\fR"
+Specify the C++ standard that should be used during compilation\. Valid values
+are \fBc++98\fR (default), \fBc++11\fR, \fBc++14\fR, \fBc++17\fR, and
+\fBc++20\fR\.
+.IP "\fB--warn-hard-add\fR"
+Warn about hard-added data members\.
+.IP "\fB--warn-hard-delete\fR"
+Warn about hard-deleted data members and persistent classes\.
+.IP "\fB--warn-hard\fR"
+Warn about both hard-added and hard-deleted data members and persistent
+classes\.
+.IP "\fB--output-dir\fR|\fB-o\fR \fIdir\fR"
+Write the generated files to \fIdir\fR instead of the current directory\.
+.IP "\fB--input-name\fR \fIname\fR"
+Use \fIname\fR instead of the input file to derive the names of the generated
+files\. If the \fB--at-once\fR option is specified, then the directory part of
+\fIname\fR is used as the location of the combined file\. Refer to the
+\fB--at-once\fR option for details\.
+.IP "\fB--changelog\fR \fIfile\fR"
+Read/write changelog from/to \fIfile\fR instead of the default changelog
+file\. The default changelog file name is derived from the input file name and
+it is placed into the same directory as the input file\. Note that the
+\fB--output-dir\fR option does not affect the changelog file location\. In
+other words, by default, the changelog file is treated as another input rather
+than output even though the ODB compiler may modify it\. Use the
+\fB--changelog-in\fR and \fB--changelog-out\fR options to specify different
+input and output chaneglog files\.
+.IP "\fB--changelog-in\fR \fIfile\fR"
+Read changelog from \fIfile\fR instead of the default changelog file\. If this
+option is specified, then you must also specify the output chanegelog file
+with \fB--changelog-out\fR\.
+.IP "\fB--changelog-out\fR \fIfile\fR"
+Write changelog to \fIfile\fR instead of the default changelog file\. If this
+option is specified, then you must also specify the input chanegelog file with
+\fB--changelog-in\fR\.
+.IP "\fB--changelog-dir\fR \fIdir\fR"
+Use \fIdir\fR instead of the input file directory as the changelog file
+directory\. This directory is also added to changelog files specified with the
+\fB--changelog\fR, \fB--changelog-in\fR, and \fB--changelog-in\fR options
+unless they are absolute paths\.
+.IP "\fB--init-changelog\fR"
+Force re-initialization of the changelog even if one exists (all the existing
+change history will be lost)\. This option is primarily useful for automated
+testing\.
+.IP "\fB--odb-file-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR to construct the names of the generated C++ files\. In the
+single-database mode the default value for this option is \fB-odb\fR\. In the
+multi-database mode it is \fB-odb\fR for the files corresponding to the
+\fBcommon\fR database and \fB-odb-\fR\fIdb\fR\fR (where \fIdb\fR is the
+database name) for other databases\.
+.IP "\fB--sql-file-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR to construct the name of the generated schema SQL file\. In
+the single-database mode by default no suffix is used\. In the multi-database
+mode the default value for this option is \fB-\fR\fIdb\fR\fR (where \fIdb\fR
+is the database name)\.
+.IP "\fB--schema-file-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR to construct the name of the generated schema C++ source
+file\. In the single-database mode the default value for this option is
+\fB-schema\fR\. In the multi-database mode it is \fB-schema-\fR\fIdb\fR\fR
+(where \fIdb\fR is the database name)\. See the \fB--schema-format\fR option
+for details\.
+.IP "\fB--changelog-file-suffix\fR \fIsfx\fR"
+Use \fIsfx\fR to construct the name of the changelog file\. In the
+single-database mode by default no suffix is used\. In the multi-database mode
+the default value for this option is \fB-\fR\fIdb\fR\fR (where \fIdb\fR is the
+database name)\.
+.IP "\fB--hxx-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB\.hxx\fR to construct the name of
+the generated C++ header file\.
+.IP "\fB--ixx-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB\.ixx\fR to construct the name of
+the generated C++ inline file\.
+.IP "\fB--cxx-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB\.cxx\fR to construct the name of
+the generated C++ source file\.
+.IP "\fB--sql-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB\.sql\fR to construct the name of
+the generated database schema file\.
+.IP "\fB--changelog-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB\.xml\fR to construct the name of
+the changelog file\.
+.IP "\fB--hxx-prologue\fR \fItext\fR"
+Insert \fItext\fR at the beginning of the generated C++ header file\.
+.IP "\fB--ixx-prologue\fR \fItext\fR"
+Insert \fItext\fR at the beginning of the generated C++ inline file\.
+.IP "\fB--cxx-prologue\fR \fItext\fR"
+Insert \fItext\fR at the beginning of the generated C++ source file\.
+.IP "\fB--schema-prologue\fR \fItext\fR"
+Insert \fItext\fR at the beginning of the generated schema C++ source file\.
+.IP "\fB--sql-prologue\fR \fItext\fR"
+Insert \fItext\fR at the beginning of the generated database schema file\.
+.IP "\fB--migration-prologue\fR \fItext\fR"
+Insert \fItext\fR at the beginning of the generated database migration file\.
+.IP "\fB--sql-interlude\fR \fItext\fR"
+Insert \fItext\fR after all the \fBDROP\fR and before any \fBCREATE\fR
+statements in the generated database schema file\.
+.IP "\fB--hxx-epilogue\fR \fItext\fR"
+Insert \fItext\fR at the end of the generated C++ header file\.
+.IP "\fB--ixx-epilogue\fR \fItext\fR"
+Insert \fItext\fR at the end of the generated C++ inline file\.
+.IP "\fB--cxx-epilogue\fR \fItext\fR"
+Insert \fItext\fR at the end of the generated C++ source file\.
+.IP "\fB--schema-epilogue\fR \fItext\fR"
+Insert \fItext\fR at the end of the generated schema C++ source file\.
+.IP "\fB--sql-epilogue\fR \fItext\fR"
+Insert \fItext\fR at the end of the generated database schema file\.
+.IP "\fB--migration-epilogue\fR \fItext\fR"
+Insert \fItext\fR at the end of the generated database migration file\.
+.IP "\fB--hxx-prologue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the beginning of the generated C++ header
+file\.
+.IP "\fB--ixx-prologue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the beginning of the generated C++ inline
+file\.
+.IP "\fB--cxx-prologue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the beginning of the generated C++ source
+file\.
+.IP "\fB--schema-prologue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the beginning of the generated schema C++
+source file\.
+.IP "\fB--sql-prologue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the beginning of the generated database
+schema file\.
+.IP "\fB--migration-prologue-file\fR \fIf\fR"
+Insert the content of file \fIf\fR at the beginning of the generated database
+migration file\.
+.IP "\fB--sql-interlude-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR after all the \fBDROP\fR and before any
+\fBCREATE\fR statements in the generated database schema file\.
+.IP "\fB--hxx-epilogue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the end of the generated C++ header file\.
+.IP "\fB--ixx-epilogue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the end of the generated C++ inline file\.
+.IP "\fB--cxx-epilogue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the end of the generated C++ source file\.
+.IP "\fB--schema-epilogue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the end of the generated schema C++ source
+file\.
+.IP "\fB--sql-epilogue-file\fR \fIfile\fR"
+Insert the content of \fIfile\fR at the end of the generated database schema
+file\.
+.IP "\fB--migration-epilogue-file\fR \fIf\fR"
+Insert the content of file \fIf\fR at the end of the generated database
+migration file\.
+.IP "\fB--odb-prologue\fR \fItext\fR"
+Compile \fItext\fR before the input header file\. This option allows you to
+add additional declarations, such as custom traits specializations, to the ODB
+compilation process\.
+.IP "\fB--odb-prologue-file\fR \fIfile\fR"
+Compile \fIfile\fR contents before the input header file\. Prologue files are
+compiled after all the prologue text fragments (\fB--odb-prologue\fR option)\.
+.IP "\fB--odb-epilogue\fR \fItext\fR"
+Compile \fItext\fR after the input header file\. This option allows you to add
+additional declarations, such as custom traits specializations, to the ODB
+compilation process\.
+.IP "\fB--odb-epilogue-file\fR \fIfile\fR"
+Compile \fIfile\fR contents after the input header file\. Epilogue files are
+compiled after all the epilogue text fragments (\fB--odb-epilogue\fR option)\.
+.IP "\fB--table-prefix\fR \fIprefix\fR"
+Add \fIprefix\fR to table names and, for databases that have global index
+and/or foreign key names, to those names as well\. The prefix is added to both
+names that were specified with the \fBdb table\fR and \fBdb index\fR pragmas
+and those that were automatically derived from class and data member names\.
+If you require a separator, such as an underscore, between the prefix and the
+name, then you should include it into the prefix value\.
+.IP "\fB--index-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB_i\fR to construct index names\.
+The suffix is only added to names that were automatically derived from data
+member names\. If you require a separator, such as an underscore, between the
+name and the suffix, then you should include it into the suffix value\.
+.IP "\fB--fkey-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB_fk\fR to construct foreign key
+names\. If you require a separator, such as an underscore, between the name
+and the suffix, then you should include it into the suffix value\.
+.IP "\fB--sequence-suffix\fR \fIsuffix\fR"
+Use \fIsuffix\fR instead of the default \fB_seq\fR to construct sequence
+names\. If you require a separator, such as an underscore, between the name
+and the suffix, then you should include it into the suffix value\.
+.IP "\fB--sql-name-case\fR \fIcase\fR"
+Convert all automatically-derived SQL names to upper or lower case\. Valid
+values for this option are \fBupper\fR and \fBlower\fR\.
+.IP "\fB--table-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions that is used to transform
+automatically-derived table names\. See the SQL NAME TRANSFORMATIONS section
+below for details\.
+.IP "\fB--column-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions that is used to transform
+automatically-derived column names\. See the SQL NAME TRANSFORMATIONS section
+below for details\.
+.IP "\fB--index-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions that is used to transform
+automatically-derived index names\. See the SQL NAME TRANSFORMATIONS section
+below for details\.
+.IP "\fB--fkey-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions that is used to transform
+automatically-derived foreign key names\. See the SQL NAME TRANSFORMATIONS
+section below for details\.
+.IP "\fB--sequence-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions that is used to transform
+automatically-derived sequence names\. See the SQL NAME TRANSFORMATIONS
+section below for details\.
+.IP "\fB--statement-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions that is used to transform
+automatically-derived prepared statement names\. See the SQL NAME
+TRANSFORMATIONS section below for details\.
+.IP "\fB--sql-name-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions that is used to transform
+all automatically-derived SQL names\. See the SQL NAME TRANSFORMATIONS section
+below for details\.
+.IP "\fB--sql-name-regex-trace\fR"
+Trace the process of applying regular expressions specified with the SQL name
+\fB--*-regex\fR options\. Use this option to find out why your regular
+expressions don't do what you expected them to do\.
+.IP "\fB--accessor-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions used to transform data
+member names to function names when searching for a suitable accessor
+function\. The argument to this option is a Perl-like regular expression in
+the form \fB/\fR\fIpattern\fR\fB/\fR\fIreplacement\fR\fB/\fR\fR\. Any
+character can be used as a delimiter instead of '\fB/\fR' and the delimiter
+can be escaped inside \fIpattern\fR and \fIreplacement\fR with a backslash
+(\fB\e\fR)\. You can specify multiple regular expressions by repeating this
+option\.
+
+All the regular expressions are tried in the order specified and the first
+expression that produces a suitable accessor function is used\. Each
+expression is tried twice: first with the actual member name and then with the
+member's \fIpublic name\fR which is obtained by removing the common member
+name decorations, such as leading and trailing underscores, the \fBm_\fR
+prefix, etc\. The ODB compiler also includes a number of built-in expressions
+for commonly used accessor names, such as \fBget_foo\fR, \fBgetFoo\fR,
+\fBgetfoo\fR, and just \fBfoo\fR\. The built-in expressions are tried last\.
+
+As an example, the following expression transforms data members with public
+names in the form \fBfoo\fR to accessor names in the form \fBGetFoo\fR:
+
+\fB/(\.+)/Get\eu$1/\fR
+
+See also the REGEX AND SHELL QUOTING section below\.
+.IP "\fB--accessor-regex-trace\fR"
+Trace the process of applying regular expressions specified with the
+\fB--accessor-regex\fR option\. Use this option to find out why your regular
+expressions don't do what you expected them to do\.
+.IP "\fB--modifier-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions used to transform data
+member names to function names when searching for a suitable modifier
+function\. The argument to this option is a Perl-like regular expression in
+the form \fB/\fR\fIpattern\fR\fB/\fR\fIreplacement\fR\fB/\fR\fR\. Any
+character can be used as a delimiter instead of '\fB/\fR' and the delimiter
+can be escaped inside \fIpattern\fR and \fIreplacement\fR with a backslash
+(\fB\e\fR)\. You can specify multiple regular expressions by repeating this
+option\.
+
+All the regular expressions are tried in the order specified and the first
+expression that produces a suitable modifier function is used\. Each
+expression is tried twice: first with the actual member name and then with the
+member's \fIpublic name\fR which is obtained by removing the common member
+name decorations, such as leading and trailing underscores, the \fBm_\fR
+prefix, etc\. The ODB compiler also includes a number of built-in expressions
+for commonly used modifier names, such as \fBset_foo\fR, \fBsetFoo\fR,
+\fBsetfoo\fR, and just \fBfoo\fR\. The built-in expressions are tried last\.
+
+As an example, the following expression transforms data members with public
+names in the form \fBfoo\fR to modifier names in the form \fBSetFoo\fR:
+
+\fB/(\.+)/Set\eu$1/\fR
+
+See also the REGEX AND SHELL QUOTING section below\.
+.IP "\fB--modifier-regex-trace\fR"
+Trace the process of applying regular expressions specified with the
+\fB--modifier-regex\fR option\. Use this option to find out why your regular
+expressions don't do what you expected them to do\.
+.IP "\fB--include-with-brackets\fR"
+Use angle brackets (<>) instead of quotes ("") in the generated \fB#include\fR
+directives\.
+.IP "\fB--include-prefix\fR \fIprefix\fR"
+Add \fIprefix\fR to the generated \fB#include\fR directive paths\.
+.IP "\fB--include-regex\fR \fIregex\fR"
+Add \fIregex\fR to the list of regular expressions used to transform generated
+\fB#include\fR directive paths\. The argument to this option is a Perl-like
+regular expression in the form
+\fB/\fR\fIpattern\fR\fB/\fR\fIreplacement\fR\fB/\fR\fR\. Any character can be
+used as a delimiter instead of '\fB/\fR' and the delimiter can be escaped
+inside \fIpattern\fR and \fIreplacement\fR with a backslash (\fB\e\fR)\. You
+can specify multiple regular expressions by repeating this option\. All the
+regular expressions are tried in the order specified and the first expression
+that matches is used\.
+
+As an example, the following expression transforms include paths in the form
+\fBfoo/bar-odb\.h\fR to paths in the form \fBfoo/generated/bar-odb\.h\fR:
+
+\fB%foo/(\.+)-odb\.h%foo/generated/$1-odb\.h%\fR
+
+See also the REGEX AND SHELL QUOTING section below\.
+.IP "\fB--include-regex-trace\fR"
+Trace the process of applying regular expressions specified with the
+\fB--include-regex\fR option\. Use this option to find out why your regular
+expressions don't do what you expected them to do\.
+.IP "\fB--guard-prefix\fR \fIprefix\fR"
+Add \fIprefix\fR to the generated header inclusion guards\. The prefix is
+transformed to upper case and characters that are illegal in a preprocessor
+macro name are replaced with underscores\.
+.IP "\fB--show-sloc\fR"
+Print the number of generated physical source lines of code (SLOC)\.
+.IP "\fB--sloc-limit\fR \fInum\fR"
+Check that the number of generated physical source lines of code (SLOC) does
+not exceed \fInum\fR\.
+.IP "\fB--options-file\fR \fIfile\fR"
+Read additional options from \fIfile\fR\. Each option should appear on a
+separate line optionally followed by space or equal sign (\fB=\fR) and an
+option value\. Empty lines and lines starting with \fB#\fR are ignored\.
+Option values can be enclosed in double (\fB"\fR) or single (\fB'\fR) quotes
+to preserve leading and trailing whitespaces as well as to specify empty
+values\. If the value itself contains trailing or leading quotes, enclose it
+with an extra pair of quotes, for example \fB'"x"'\fR\. Non-leading and
+non-trailing quotes are interpreted as being part of the option value\.
+
+The semantics of providing options in a file is equivalent to providing the
+same set of options in the same order on the command line at the point where
+the \fB--options-file\fR option is specified except that the shell escaping
+and quoting is not required\. Repeat this option to specify more than one
+options file\.
+.IP "\fB-x\fR \fIoption\fR"
+Pass \fIoption\fR to the underlying C++ compiler (\fBg++\fR)\. The
+\fIoption\fR value that doesn't start with '\fB-\fR' is considered the
+\fBg++\fR executable name\.
+.IP "\fB-v\fR"
+Print the commands executed to run the stages of compilation\.
+.IP "\fB--trace\fR"
+Trace the compilation process\.
+.IP "\fB--mysql-engine\fR \fIengine\fR"
+Use \fIengine\fR instead of the default \fBInnoDB\fR in the generated database
+schema file\. For more information on the storage engine options see the MySQL
+documentation\. If you would like to use the database-default engine, pass
+\fBdefault\fR as the value for this option\.
+.IP "\fB--sqlite-override-null\fR"
+Make all columns in the generated database schema allow \fBNULL\fR values\.
+This is primarily useful in schema migration since SQLite does not support
+dropping of columns\. By making all columns \fBNULL\fR we can later "delete"
+them by setting their values to \fBNULL\fR\. Note that this option overrides
+even the \fBnot_null\fR pragma\.
+.IP "\fB--sqlite-lax-auto-id\fR"
+Do not force monotonically increasing automatically-assigned object ids\. In
+this mode the generated database schema omits the \fBAUTOINCREMENT\fR keyword
+which results in faster object persistence but may lead to
+automatically-assigned ids not being in a strictly ascending order\. Refer to
+the SQLite documentation for details\.
+.IP "\fB--pgsql-server-version\fR \fIver\fR"
+Specify the minimum PostgreSQL server version with which the generated C++
+code and schema will be used\. This information is used to enable
+version-specific optimizations and workarounds in the generated C++ code and
+schema\. The version must be in the \fImajor\fR\fB\.\fR\fIminor\fR\fR form,
+for example, \fB9\.1\fR\. If this option is not specified, then \fB7\.4\fR or
+later is assumed\.
+.IP "\fB--oracle-client-version\fR \fIver\fR"
+Specify the minimum Oracle client library (OCI) version with which the
+generated C++ code will be linked\. This information is used to enable
+version-specific optimizations and workarounds in the generated C++ code\. The
+version must be in the \fImajor\fR\fB\.\fR\fIminor\fR\fR form, for example,
+\fB11\.2\fR\. If this option is not specified, then \fB10\.1\fR or later is
+assumed\.
+.IP "\fB--oracle-warn-truncation\fR"
+Warn about SQL names that are longer than 30 characters and are therefore
+truncated\. Note that during database schema generation
+(\fB--generate-schema\fR) ODB detects when such truncations lead to name
+conflicts and issues diagnostics even without this option specified\.
+.IP "\fB--mssql-server-version\fR \fIver\fR"
+Specify the minimum SQL Server server version with which the generated C++
+code and schema will be used\. This information is used to enable
+version-specific optimizations and workarounds in the generated C++ code and
+schema\. The version must be in the \fImajor\fR\fB\.\fR\fIminor\fR\fR form,
+for example, \fB9\.0\fR (SQL Server 2005), \fB10\.5\fR (2008R2), or
+\fB11\.0\fR (2012)\. If this option is not specified, then \fB10\.0\fR (SQL
+Server 2008) or later is assumed\.
+.IP "\fB--mssql-short-limit\fR \fIsize\fR"
+Specify the short data size limit\. If a character, national character, or
+binary data type has a maximum length (in bytes) less than or equal to this
+limit, then it is treated as \fIshort data\fR, otherwise it is \fIlong
+data\fR\. For short data ODB pre-allocates an intermediate buffer of the
+maximum size and binds it directly to a parameter or result column\. This way
+the underlying API (ODBC) can read/write directly from/to this buffer\. In the
+case of long data, the data is read/written in chunks using the
+\fBSQLGetData()\fR/\fBSQLPutData()\fR ODBC functions\. While the long data
+approach reduces the amount of memory used by the application, it may require
+greater CPU resources\. The default short data limit is 1024 bytes\. When
+setting a custom short data limit, make sure that it is sufficiently large so
+that no object id in the application is treated as long data\.
+.\"
+.\" SQL NAME TRANSFORMATIONS
+.\"
+.SH SQL NAME TRANSFORMATIONS
+The ODB compiler provides a number of mechanisms for transforming
+automatically-derived SQL names, such as tables, columns, etc.,
+to match a specific naming convention. At the higher level, we can
+add a prefix to global names (tables and, for some databases,
+indexes and/or foreign keys) with the
+.B --table-prefix
+option. Similarly, we can specify custom suffixes for automatically-derived
+index
+.RB ( --index-suffix ;
+default is
+.BR _i ),
+foreign key
+.RB ( --fkey-suffix ;
+default is
+.BR _fk ),
+and sequence
+.RB ( --sequence-suffix ;
+default is
+.BR _seq )
+names. Finally, we can also convert all the names to upper or lower
+case with the
+.B --sql-name-case
+option (valid values are
+.B upper
+and
+.BR lower ).
+
+At the lower level we can specify a set of regular expressions to
+implement arbitrary transformations of the automatically-derived SQL
+names. If we want a particular regular expression only to apply to
+a specific name, for example, table or column, then we use one of the
+.B --\fIkind\fB-regex
+options, where
+.I kind
+can be
+.BR table ,
+.BR column ,
+.BR index ,
+.BR fkey ,
+.BR sequence ,
+or
+.BR statement .
+On the other hand, if we want our regular expressions to apply to all SQL
+names, then we use the
+.B --sql-name-regex
+option.
+
+The interaction between the higher and lower level transformations
+is as follows. Prefixes and suffixes are added first. Then the
+regular expression transformations are applied. Finally, if requested,
+the name is converted to upper or lower case. Note also that all of
+these transformations except for
+.B --table-prefix
+only apply to automatically-derived names. In other words, if a table,
+column, etc., name was explicitly specified with a pragma, then it
+is used as is, without applying any (except for the table prefix)
+transformations.
+
+The value for the
+.B --*-regex
+options is a Perl-like regular expression in the form
+.BI / pattern / replacement /\fR.
+Any character can be used as a delimiter instead of
+.B /
+and the delimiter can be escaped inside
+.I pattern
+and
+.I replacement
+with a backslash
+.RB ( \e ).
+You can also specify multiple regular expressions by repeating these
+options.
+
+All the regular expressions are tried in the order specified with the
+name-specific expressions (for example,
+.BR --table-regex)
+tried first followed by the generic expressions
+.RB ( --sql-name-regex ).
+The first expression that matches is used.
+
+As an example, consider a regular expression that transforms a class
+name in the form
+.B CFoo
+to a table name in the form
+.BR FOO:
+
+.B --table-regex '/C(.+)/\eU$1/'
+
+As a more interesting example, consider the transformation of class
+names that follow the upper camel case convention (for example,
+.BR FooBar )
+to table names that follow the underscore-separated, all upper case
+convention (for example,
+.BR FOO_BAR ).
+For this case we have to use separate expressions to handle one-word,
+two-word, etc., names:
+
+.B --table-regex '/([A-z][a-z]+)/\eU$1/'
+
+.B --table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\eU$1_$2/'
+
+See also the REGEX AND SHELL QUOTING section below.
+.\"
+.\" REGEX AND SHELL QUOTING
+.\"
+.SH REGEX AND SHELL QUOTING
+When entering a regular expression argument in the shell command line
+it is often necessary to use quoting (enclosing the argument in " "
+or ' ') in order to prevent the shell from interpreting certain
+characters, for example, spaces as argument separators and $ as
+variable expansions.
+
+Unfortunately it is hard to achieve this in a manner that is portable
+across POSIX shells, such as those found on GNU/Linux and UNIX, and
+Windows shell. For example, if you use " " for quoting you will get
+a wrong result with POSIX shells if your expression contains $. The
+standard way of dealing with this on POSIX systems is to use ' '
+instead. Unfortunately, Windows shell does not remove ' ' from
+arguments when they are passed to applications. As a result you may
+have to use ' ' for POSIX and " " for Windows ($ is not treated as
+a special character on Windows).
+
+Alternatively, you can save regular expression options into a file,
+one option per line, and use this file with the
+.B --options-file
+option. With this approach you don't need to worry about shell quoting.
+.\"
+.\" DIAGNOSTICS
+.\"
+.SH DIAGNOSTICS
+If the input file is not valid C++,
+.B odb
+will issue diagnostic messages to STDERR and exit with non-zero exit code.
+.\"
+.\" BUGS
+.\"
+.SH BUGS
+Send bug reports to the odb-users@codesynthesis.com mailing list.
+.\"
+.\" COPYRIGHT
+.\"
+.SH COPYRIGHT
+Copyright (c) 2009-2024 Code Synthesis Tools CC.
+
+Permission is granted to copy, distribute and/or modify this
+document under the terms of the GNU Free Documentation License,
+version 1.2; with no Invariant Sections, no Front-Cover Texts and
+no Back-Cover Texts. Copy of the license can be obtained from
+http://www.codesynthesis.com/licenses/fdl-1.3.txt
diff --git a/odb/doc/pregenerated/odb.xhtml b/odb/doc/pregenerated/odb.xhtml
new file mode 100644
index 0000000..de1a3df
--- /dev/null
+++ b/odb/doc/pregenerated/odb.xhtml
@@ -0,0 +1,978 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+
+<head>
+ <title>ODB 2.4.0 Compiler Command Line Manual</title>
+
+ <meta name="copyright" content="&#169; 2009-2024 Code Synthesis Tools CC"/>
+ <meta name="keywords" content="odb,object,relational,mapping,compiler,c++"/>
+ <meta name="description" content="ODB Compiler Command Line Manual"/>
+
+ <link rel="stylesheet" type="text/css" href="default.css" />
+
+<style type="text/css">
+
+ #synopsis {
+ list-style-type: none;
+ }
+
+ #synopsis li {
+ padding-top : 0.0em;
+ padding-bottom : 0.0em;
+ }
+
+ .options {
+ margin: 1em 0 1em 0;
+ }
+
+ .options dt {
+ margin: 1em 0 0 0;
+ }
+
+ .options dd {
+ margin: .1em 0 0 4.5em;
+ }
+
+</style>
+</head>
+
+<body>
+<div id="container">
+ <div id="content">
+
+ <h1>NAME</h1>
+
+ <p>odb - object-relational mapping (ORM) compiler for C++</p>
+
+ <h1>SYNOPSIS</h1>
+
+ <dl id="synopsis">
+ <dt><code><b>odb</b> [<i>options</i>] <i>file</i> [<i>file</i>...]</code></dt>
+ </dl>
+
+ <h1>DESCRIPTION</h1>
+
+ <p>Given a set of C++ classes in a header file, <code><b>odb</b></code>
+ generates C++ code that allows you to persist, query, and update objects
+ of these classes in a relational database (RDBMS). The relational
+ database that the generated code should target is specified with the
+ required <code><b>--database</b></code> option (see below).</p>
+
+ <p>For an input file in the form <code><b>name.hxx</b></code> (other
+ file extensions can be used instead of <code><b>.hxx</b></code>),
+ in the single-database mode (the default), the generated C++ files
+ by default have the following names:
+ <code><b>name-odb.hxx</b></code> (header file),
+ <code><b>name-odb.ixx</b></code> (inline file), and
+ <code><b>name-odb.cxx</b></code> (source file).
+
+ Additionally, if the <code><b>--generate-schema</b></code> option is
+ specified and the <code><b>sql</b></code> schema format is requested (see
+ <code><b>--schema-format</b></code>), the <code><b>name.sql</b></code>
+ database schema file is generated. If the <code><b>separate</b></code>
+ schema format is requested, the database creation code is generated
+ into the separate <code><b>name-schema.cxx</b></code> file.</p>
+
+ <p>In the multi-database mode (see the <code><b>--multi-database</b></code>
+ option below), the generated files corresponding to the
+ <code><b>common</b></code> database have the same names as in the
+ single-database mode. For other databases, the file names include
+ the database name:
+ <code><b>name-odb-</b><i>db</i><b>.hxx</b></code>,
+ <code><b>name-odb-</b><i>db</i><b>.ixx</b></code>,
+ <code><b>name-odb-</b><i>db</i><b>.cxx</b></code>,
+ <code><b>name-</b><i>db</i><b>.sql</b></code>, and
+ <code><b>name-schema-</b><i>db</i><b>.cxx</b></code>
+ (where <code><i>db</i></code> is the database name).</p>
+
+ <h1>OPTIONS</h1>
+ <dl class="options">
+ <dt><code><b>--help</b></code></dt>
+ <dd>Print usage information and exit.</dd>
+
+ <dt><code><b>--version</b></code></dt>
+ <dd>Print version and exit.</dd>
+
+ <dt><code><b>-I</b></code> <code><i>dir</i></code></dt>
+ <dd>Add <code><i>dir</i></code> to the beginning of the list of
+ directories to be searched for included header files.</dd>
+
+ <dt><code><b>-D</b></code> <code><i>name</i></code>[=<code><i>def</i></code>]</dt>
+ <dd>Define macro <code><i>name</i></code> with definition
+ <code><i>def</i></code>. If definition is omitted, define
+ <code><i>name</i></code> to be 1.</dd>
+
+ <dt><code><b>-U</b></code> <code><i>name</i></code></dt>
+ <dd>Cancel any previous definitions of macro <code><i>name</i></code>,
+ either built-in or provided with the <code><b>-D</b></code> option.</dd>
+
+ <dt><code><b>--database</b></code>|<code><b>-d</b></code> <code><i>db</i></code></dt>
+ <dd>Generate code for the <code><i>db</i></code> database. Valid values
+ are <code><b>mssql</b></code>, <code><b>mysql</b></code>,
+ <code><b>oracle</b></code>, <code><b>pgsql</b></code>,
+ <code><b>sqlite</b></code>, and <code><b>common</b></code> (multi-database
+ mode only).</dd>
+
+ <dt><code><b>--multi-database</b></code>|<code><b>-m</b></code> <code><i>type</i></code></dt>
+ <dd>Enable multi-database support and specify its type. Valid values for
+ this option are <code><b>static</b></code> and
+ <code><b>dynamic</b></code>.
+
+ <p>In the multi-database mode, options that determine the kind (for
+ example, <code><b>--schema-format</b></code>), names (for example,
+ <code><b>--odb-file-suffix</b></code>), 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,
+ <code><b>mysql:value</b></code>. This restricts the value of such an
+ option to only apply to generated files corresponding to this
+ database.</p></dd>
+
+ <dt><code><b>--default-database</b></code> <code><i>db</i></code></dt>
+ <dd>When static multi-database support is used, specify the database that
+ should be made the default. When dynamic multi-database support is used,
+ <code><b>common</b></code> is always made the default database.</dd>
+
+ <dt><code><b>--generate-query</b></code>|<code><b>-q</b></code></dt>
+ <dd>Generate query support code. Without this support you cannot use views
+ and can only load objects via their ids.</dd>
+
+ <dt><code><b>--generate-prepared</b></code></dt>
+ <dd>Generate prepared query execution support code.</dd>
+
+ <dt><code><b>--omit-unprepared</b></code></dt>
+ <dd>Omit un-prepared (once-off) query execution support code.</dd>
+
+ <dt><code><b>--generate-session</b></code>|<code><b>-e</b></code></dt>
+ <dd>Generate session support code. With this option session support will
+ be enabled by default for all the persistent classes except those for
+ which it was explicitly disabled using the <code><b>db session</b></code>
+ pragma.</dd>
+
+ <dt><code><b>--generate-schema</b></code>|<code><b>-s</b></code></dt>
+ <dd>Generate the database schema. The database schema contains SQL
+ statements that create database tables necessary to store persistent
+ classes defined in the file being compiled. Note that by applying this
+ schema, all the existing information stored in such tables will be lost.
+
+ <p>Depending on the database being used (<code><b>--database</b></code>
+ option), the schema is generated either as a standalone SQL file or
+ embedded into the generated C++ code. By default the SQL file is generated
+ for the MySQL, PostgreSQL, Oracle, and Microsoft SQL Server databases and
+ the schema is embedded into the C++ code for the SQLite database. Use the
+ <code><b>--schema-format</b></code> option to alter the default schema
+ format.</p>
+
+ <p>If database schema evolution support is enabled (that is, the object
+ model version is specified), then this option also triggers the generation
+ of database schema migration statements, again either as standalong SQL
+ files or embedded into the generated C++ code. You can suppress the
+ generation of schema migration statements by specifying the
+ <code><b>--suppress-migration</b></code> option.</p></dd>
+
+ <dt><code><b>--generate-schema-only</b></code></dt>
+ <dd>Generate only the database schema. Note that this option is only valid
+ when generating schema as a standalone SQL file (see
+ <code><b>--schema-format</b></code> for details).</dd>
+
+ <dt><code><b>--suppress-migration</b></code></dt>
+ <dd>Suppress the generation of database schema migration statements.</dd>
+
+ <dt><code><b>--suppress-schema-version</b></code></dt>
+ <dd>Suppress the generation of schema version table. If you specify this
+ option then you are also expected to manually specify the database schema
+ version and migration state at runtime using the
+ <code><b>odb::database::schema_version()</b></code> function.</dd>
+
+ <dt><code><b>--schema-version-table</b></code> <code><i>name</i></code></dt>
+ <dd>Specify the alternative schema version table name instead of the
+ default <code><b>schema_version</b></code>. If you specify this option
+ then you are also expected to manually specify the schema version table
+ name at runtime using the
+ <code><b>odb::database::schema_version_table()</b></code> function. The
+ table name can be qualified.</dd>
+
+ <dt><code><b>--schema-format</b></code> <code><i>format</i></code></dt>
+ <dd>Generate the database schema in the specified format. Pass
+ <code><b>sql</b></code> as <code><i>format</i></code> to generate the
+ database schema as a standalone SQL file or pass
+ <code><b>embedded</b></code> to embed the schema into the generated C++
+ code. The <code><b>separate</b></code> value is similar to
+ <code><b>embedded</b></code> except the schema creation code is generated
+ into a separate C++ file (<code><b>name-schema.cxx</b></code> by default).
+ This value is primarily useful if you want to place the schema creation
+ functionality into a separate program or library. Repeat this option to
+ generate the same database schema in multiple formats.</dd>
+
+ <dt><code><b>--omit-drop</b></code></dt>
+ <dd>Omit <code><b>DROP</b></code> statements from the generated database
+ schema.</dd>
+
+ <dt><code><b>--omit-create</b></code></dt>
+ <dd>Omit <code><b>CREATE</b></code> statements from the generated database
+ schema.</dd>
+
+ <dt><code><b>--schema-name</b></code> <code><i>name</i></code></dt>
+ <dd>Use <code><i>name</i></code> as the database schema name. Schema names
+ are primarily used to distinguish between multiple embedded schemas in the
+ schema catalog. They are not to be confused with database schemas
+ (database namespaces) which are specified with the
+ <code><b>--schema</b></code> option. If this option is not specified, the
+ empty name, which is the default schema name, is used.</dd>
+
+ <dt><code><b>--fkeys-deferrable-mode</b></code> <code><i>m</i></code></dt>
+ <dd>Use constraint checking mode <code><i>m</i></code> in foreign keys
+ generated for object relationships. Valid values for this option are
+ <code><b>not_deferrable</b></code>, <code><b>immediate</b></code>, and
+ <code><b>deferred</b></code> (default). MySQL and SQL Server do not
+ support deferrable foreign keys and for these databases such keys are
+ generated commented out. Other foreign keys generated by the ODB compiler
+ (such as the ones used to support containers and polymorphic hierarchies)
+ are always generated as not deferrable.
+
+ <p>Note also that if you use either <code><b>not_deferrable</b></code> or
+ <code><b>immediate</b></code> mode, then the order in which you persist,
+ update, and erase objects within a transaction becomes important.</p></dd>
+
+ <dt><code><b>--default-pointer</b></code> <code><i>ptr</i></code></dt>
+ <dd>Use <code><i>ptr</i></code> as the default pointer for persistent
+ objects and views. Objects and views that do not have a pointer assigned
+ with the <code><b>db pointer</b></code> pragma will use this pointer by
+ default. The value of this option can be '<code><b>*</b></code>' which
+ denotes the raw pointer and is the default, or qualified name of a smart
+ pointer class template, for example, <code><b>std::shared_ptr</b></code>.
+ In the latter case, the ODB compiler constructs the object or view pointer
+ by adding a single template argument of the object or view type to the
+ qualified name, for example
+ <code><b>std::shared_ptr&lt;object></b></code>. The ODB runtime uses
+ object and view pointers to return, and, in case of objects, pass and
+ cache dynamically allocated instances of object and view types.
+
+ <p>Except for the raw pointer and the standard smart pointers defined in
+ the <code><b>&lt;memory></b></code> header file, you are expected to
+ include the definition of the default pointer at the beginning of the
+ generated header file. There are two common ways to achieve this: you can
+ either include the necessary header in the file being compiled or you can
+ use the <code><b>--hxx-prologue</b></code> option to add the necessary
+ <code><b>#include</b></code> directive to the generated code.</p></dd>
+
+ <dt><code><b>--session-type</b></code> <code><i>type</i></code></dt>
+ <dd>Use <code><i>type</i></code> as the alternative session type instead
+ of the default <code><b>odb::session</b></code>. This option can be used
+ to specify a custom session implementation to be use by the persistent
+ classes. Note that you will also need to include the definition of the
+ custom session type into the generated header file. This is normally
+ achieved with the <code><b>--hxx-prologue*</b></code> options.</dd>
+
+ <dt><code><b>--profile</b></code>|<code><b>-p</b></code> <code><i>name</i></code></dt>
+ <dd>Specify a profile that should be used during compilation. A profile is
+ an options file. The ODB compiler first looks for a database-specific
+ version with the name constructed by appending the
+ <code><b>-</b></code><code><i>database</i></code><code><b>.options</b></code>
+ suffix to <code><i>name</i></code>, where <code><i>database</i></code> is
+ the database name as specified with the <code><b>--database</b></code>
+ option. If this file is not found, then the ODB compiler looks for a
+ database-independant version with the name constructed by appending just
+ the <code><b>.options</b></code> suffix.
+
+ <p>The profile options files are searched for in the same set of
+ directories as C++ headers included with the <code><b>#include
+ &lt;...></b></code> directive (built-in paths plus those specified with
+ the <code><b>-I</b></code> options). The options file is first searched
+ for in the directory itself and then in its <code><b>odb/</b></code>
+ subdirectory.</p>
+
+ <p>For the format of the options file refer to the
+ <code><b>--options-file</b></code> option below. You can repeat this
+ option to specify more than one profile.</p></dd>
+
+ <dt><code><b>--at-once</b></code></dt>
+ <dd>Generate code for all the input files as well as for all the files
+ that they include at once. The result is a single set of source/schema
+ files that contain all the generated code. If more than one input file is
+ specified together with this option, then the
+ <code><b>--input-name</b></code> option must also be specified in order to
+ provide the base name for the output files. In this case, the directory
+ part of such a base name is used as the location of the combined file.
+ This can be important for the <code><b>#include</b></code> directive
+ resolution.</dd>
+
+ <dt><code><b>--schema</b></code> <code><i>schema</i></code></dt>
+ <dd>Specify a database schema (database namespace) that should be assigned
+ to the persistent classes in the file being compiled. Database schemas are
+ not to be confused with database schema names (schema catalog names) which
+ are specified with the <code><b>--schema-name</b></code> option.</dd>
+
+ <dt><code><b>--export-symbol</b></code> <code><i>symbol</i></code></dt>
+ <dd>Insert <code><i>symbol</i></code> in places where DLL export/import
+ control statements (<code><b>__declspec(dllexport/dllimport)</b></code>)
+ are necessary. See also the <code><b>--extern-symbol</b></code> option
+ below.</dd>
+
+ <dt><code><b>--extern-symbol</b></code> <code><i>symbol</i></code></dt>
+ <dd>If <code><i>symbol</i></code> is defined, insert it in places where a
+ template instantiation must be declared <code><b>extern</b></code>. This
+ option is normally used together with <code><b>--export-symbol</b></code>
+ when both multi-database support and queries are enabled.</dd>
+
+ <dt><code><b>--std</b></code> <code><i>version</i></code></dt>
+ <dd>Specify the C++ standard that should be used during compilation. Valid
+ values are <code><b>c++98</b></code> (default), <code><b>c++11</b></code>,
+ <code><b>c++14</b></code>, <code><b>c++17</b></code>, and
+ <code><b>c++20</b></code>.</dd>
+
+ <dt><code><b>--warn-hard-add</b></code></dt>
+ <dd>Warn about hard-added data members.</dd>
+
+ <dt><code><b>--warn-hard-delete</b></code></dt>
+ <dd>Warn about hard-deleted data members and persistent classes.</dd>
+
+ <dt><code><b>--warn-hard</b></code></dt>
+ <dd>Warn about both hard-added and hard-deleted data members and
+ persistent classes.</dd>
+
+ <dt><code><b>--output-dir</b></code>|<code><b>-o</b></code> <code><i>dir</i></code></dt>
+ <dd>Write the generated files to <code><i>dir</i></code> instead of the
+ current directory.</dd>
+
+ <dt><code><b>--input-name</b></code> <code><i>name</i></code></dt>
+ <dd>Use <code><i>name</i></code> instead of the input file to derive the
+ names of the generated files. If the <code><b>--at-once</b></code> option
+ is specified, then the directory part of <code><i>name</i></code> is used
+ as the location of the combined file. Refer to the
+ <code><b>--at-once</b></code> option for details.</dd>
+
+ <dt><code><b>--changelog</b></code> <code><i>file</i></code></dt>
+ <dd>Read/write changelog from/to <code><i>file</i></code> instead of the
+ default changelog file. The default changelog file name is derived from
+ the input file name and it is placed into the same directory as the input
+ file. Note that the <code><b>--output-dir</b></code> option does not
+ affect the changelog file location. In other words, by default, the
+ changelog file is treated as another input rather than output even though
+ the ODB compiler may modify it. Use the <code><b>--changelog-in</b></code>
+ and <code><b>--changelog-out</b></code> options to specify different input
+ and output chaneglog files.</dd>
+
+ <dt><code><b>--changelog-in</b></code> <code><i>file</i></code></dt>
+ <dd>Read changelog from <code><i>file</i></code> instead of the default
+ changelog file. If this option is specified, then you must also specify
+ the output chanegelog file with <code><b>--changelog-out</b></code>.</dd>
+
+ <dt><code><b>--changelog-out</b></code> <code><i>file</i></code></dt>
+ <dd>Write changelog to <code><i>file</i></code> instead of the default
+ changelog file. If this option is specified, then you must also specify
+ the input chanegelog file with <code><b>--changelog-in</b></code>.</dd>
+
+ <dt><code><b>--changelog-dir</b></code> <code><i>dir</i></code></dt>
+ <dd>Use <code><i>dir</i></code> instead of the input file directory as the
+ changelog file directory. This directory is also added to changelog files
+ specified with the <code><b>--changelog</b></code>,
+ <code><b>--changelog-in</b></code>, and <code><b>--changelog-in</b></code>
+ options unless they are absolute paths.</dd>
+
+ <dt><code><b>--init-changelog</b></code></dt>
+ <dd>Force re-initialization of the changelog even if one exists (all the
+ existing change history will be lost). This option is primarily useful for
+ automated testing.</dd>
+
+ <dt><code><b>--odb-file-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> to construct the names of the generated
+ C++ files. In the single-database mode the default value for this option
+ is <code><b>-odb</b></code>. In the multi-database mode it is
+ <code><b>-odb</b></code> for the files corresponding to the
+ <code><b>common</b></code> database and <code><b>-odb-</b><i>db</i></code>
+ (where <code><i>db</i></code> is the database name) for other
+ databases.</dd>
+
+ <dt><code><b>--sql-file-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> to construct the name of the generated
+ schema SQL file. In the single-database mode by default no suffix is used.
+ In the multi-database mode the default value for this option is
+ <code><b>-</b><i>db</i></code> (where <code><i>db</i></code> is the
+ database name).</dd>
+
+ <dt><code><b>--schema-file-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> to construct the name of the generated
+ schema C++ source file. In the single-database mode the default value for
+ this option is <code><b>-schema</b></code>. In the multi-database mode it
+ is <code><b>-schema-</b><i>db</i></code> (where <code><i>db</i></code> is
+ the database name). See the <code><b>--schema-format</b></code> option for
+ details.</dd>
+
+ <dt><code><b>--changelog-file-suffix</b></code> <code><i>sfx</i></code></dt>
+ <dd>Use <code><i>sfx</i></code> to construct the name of the changelog
+ file. In the single-database mode by default no suffix is used. In the
+ multi-database mode the default value for this option is
+ <code><b>-</b><i>db</i></code> (where <code><i>db</i></code> is the
+ database name).</dd>
+
+ <dt><code><b>--hxx-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.hxx</b></code> to construct the name of the generated C++ header
+ file.</dd>
+
+ <dt><code><b>--ixx-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.ixx</b></code> to construct the name of the generated C++ inline
+ file.</dd>
+
+ <dt><code><b>--cxx-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.cxx</b></code> to construct the name of the generated C++ source
+ file.</dd>
+
+ <dt><code><b>--sql-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.sql</b></code> to construct the name of the generated database
+ schema file.</dd>
+
+ <dt><code><b>--changelog-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.xml</b></code> to construct the name of the changelog file.</dd>
+
+ <dt><code><b>--hxx-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated C++
+ header file.</dd>
+
+ <dt><code><b>--ixx-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated C++
+ inline file.</dd>
+
+ <dt><code><b>--cxx-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated C++
+ source file.</dd>
+
+ <dt><code><b>--schema-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated
+ schema C++ source file.</dd>
+
+ <dt><code><b>--sql-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated
+ database schema file.</dd>
+
+ <dt><code><b>--migration-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated
+ database migration file.</dd>
+
+ <dt><code><b>--sql-interlude</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> after all the <code><b>DROP</b></code>
+ and before any <code><b>CREATE</b></code> statements in the generated
+ database schema file.</dd>
+
+ <dt><code><b>--hxx-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated C++ header
+ file.</dd>
+
+ <dt><code><b>--ixx-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated C++ inline
+ file.</dd>
+
+ <dt><code><b>--cxx-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated C++ source
+ file.</dd>
+
+ <dt><code><b>--schema-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated schema C++
+ source file.</dd>
+
+ <dt><code><b>--sql-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated database
+ schema file.</dd>
+
+ <dt><code><b>--migration-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated database
+ migration file.</dd>
+
+ <dt><code><b>--hxx-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated C++ header file.</dd>
+
+ <dt><code><b>--ixx-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated C++ inline file.</dd>
+
+ <dt><code><b>--cxx-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated C++ source file.</dd>
+
+ <dt><code><b>--schema-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated schema C++ source file.</dd>
+
+ <dt><code><b>--sql-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated database schema file.</dd>
+
+ <dt><code><b>--migration-prologue-file</b></code> <code><i>f</i></code></dt>
+ <dd>Insert the content of file <code><i>f</i></code> at the beginning of
+ the generated database migration file.</dd>
+
+ <dt><code><b>--sql-interlude-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> after all the
+ <code><b>DROP</b></code> and before any <code><b>CREATE</b></code>
+ statements in the generated database schema file.</dd>
+
+ <dt><code><b>--hxx-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated C++ header file.</dd>
+
+ <dt><code><b>--ixx-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated C++ inline file.</dd>
+
+ <dt><code><b>--cxx-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated C++ source file.</dd>
+
+ <dt><code><b>--schema-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated schema C++ source file.</dd>
+
+ <dt><code><b>--sql-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated database schema file.</dd>
+
+ <dt><code><b>--migration-epilogue-file</b></code> <code><i>f</i></code></dt>
+ <dd>Insert the content of file <code><i>f</i></code> at the end of the
+ generated database migration file.</dd>
+
+ <dt><code><b>--odb-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Compile <code><i>text</i></code> before the input header file. This
+ option allows you to add additional declarations, such as custom traits
+ specializations, to the ODB compilation process.</dd>
+
+ <dt><code><b>--odb-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Compile <code><i>file</i></code> contents before the input header
+ file. Prologue files are compiled after all the prologue text fragments
+ (<code><b>--odb-prologue</b></code> option).</dd>
+
+ <dt><code><b>--odb-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Compile <code><i>text</i></code> after the input header file. This
+ option allows you to add additional declarations, such as custom traits
+ specializations, to the ODB compilation process.</dd>
+
+ <dt><code><b>--odb-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Compile <code><i>file</i></code> contents after the input header file.
+ Epilogue files are compiled after all the epilogue text fragments
+ (<code><b>--odb-epilogue</b></code> option).</dd>
+
+ <dt><code><b>--table-prefix</b></code> <code><i>prefix</i></code></dt>
+ <dd>Add <code><i>prefix</i></code> to table names and, for databases that
+ have global index and/or foreign key names, to those names as well. The
+ prefix is added to both names that were specified with the <code><b>db
+ table</b></code> and <code><b>db index</b></code> pragmas and those that
+ were automatically derived from class and data member names. If you
+ require a separator, such as an underscore, between the prefix and the
+ name, then you should include it into the prefix value.</dd>
+
+ <dt><code><b>--index-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>_i</b></code> to construct index names. The suffix is only added
+ to names that were automatically derived from data member names. If you
+ require a separator, such as an underscore, between the name and the
+ suffix, then you should include it into the suffix value.</dd>
+
+ <dt><code><b>--fkey-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>_fk</b></code> to construct foreign key names. If you require a
+ separator, such as an underscore, between the name and the suffix, then
+ you should include it into the suffix value.</dd>
+
+ <dt><code><b>--sequence-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>_seq</b></code> to construct sequence names. If you require a
+ separator, such as an underscore, between the name and the suffix, then
+ you should include it into the suffix value.</dd>
+
+ <dt><code><b>--sql-name-case</b></code> <code><i>case</i></code></dt>
+ <dd>Convert all automatically-derived SQL names to upper or lower case.
+ Valid values for this option are <code><b>upper</b></code> and
+ <code><b>lower</b></code>.</dd>
+
+ <dt><code><b>--table-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived table names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--column-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived column names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--index-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived index names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--fkey-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived foreign key names. See the SQL
+ NAME TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--sequence-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived sequence names. See the SQL
+ NAME TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--statement-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived prepared statement names. See
+ the SQL NAME TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--sql-name-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform all automatically-derived SQL names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--sql-name-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ SQL name <code><b>--*-regex</b></code> options. Use this option to find
+ out why your regular expressions don't do what you expected them to
+ do.</dd>
+
+ <dt><code><b>--accessor-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions used
+ to transform data member names to function names when searching for a
+ suitable accessor function. The argument to this option is a Perl-like
+ regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any
+ character can be used as a delimiter instead of '<code><b>/</b></code>'
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can specify multiple regular expressions by repeating this option.
+
+ <p>All the regular expressions are tried in the order specified and the
+ first expression that produces a suitable accessor function is used. Each
+ expression is tried twice: first with the actual member name and then with
+ the member's <i>public name</i> which is obtained by removing the common
+ member name decorations, such as leading and trailing underscores, the
+ <code><b>m_</b></code> prefix, etc. The ODB compiler also includes a
+ number of built-in expressions for commonly used accessor names, such as
+ <code><b>get_foo</b></code>, <code><b>getFoo</b></code>,
+ <code><b>getfoo</b></code>, and just <code><b>foo</b></code>. The built-in
+ expressions are tried last.</p>
+
+ <p>As an example, the following expression transforms data members with
+ public names in the form <code><b>foo</b></code> to accessor names in the
+ form <code><b>GetFoo</b></code>:</p>
+
+ <p class="code"><code><b>/(.+)/Get\u$1/</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p></dd>
+
+ <dt><code><b>--accessor-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ <code><b>--accessor-regex</b></code> option. Use this option to find out
+ why your regular expressions don't do what you expected them to do.</dd>
+
+ <dt><code><b>--modifier-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions used
+ to transform data member names to function names when searching for a
+ suitable modifier function. The argument to this option is a Perl-like
+ regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any
+ character can be used as a delimiter instead of '<code><b>/</b></code>'
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can specify multiple regular expressions by repeating this option.
+
+ <p>All the regular expressions are tried in the order specified and the
+ first expression that produces a suitable modifier function is used. Each
+ expression is tried twice: first with the actual member name and then with
+ the member's <i>public name</i> which is obtained by removing the common
+ member name decorations, such as leading and trailing underscores, the
+ <code><b>m_</b></code> prefix, etc. The ODB compiler also includes a
+ number of built-in expressions for commonly used modifier names, such as
+ <code><b>set_foo</b></code>, <code><b>setFoo</b></code>,
+ <code><b>setfoo</b></code>, and just <code><b>foo</b></code>. The built-in
+ expressions are tried last.</p>
+
+ <p>As an example, the following expression transforms data members with
+ public names in the form <code><b>foo</b></code> to modifier names in the
+ form <code><b>SetFoo</b></code>:</p>
+
+ <p class="code"><code><b>/(.+)/Set\u$1/</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p></dd>
+
+ <dt><code><b>--modifier-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ <code><b>--modifier-regex</b></code> option. Use this option to find out
+ why your regular expressions don't do what you expected them to do.</dd>
+
+ <dt><code><b>--include-with-brackets</b></code></dt>
+ <dd>Use angle brackets (&lt;>) instead of quotes ("") in the generated
+ <code><b>#include</b></code> directives.</dd>
+
+ <dt><code><b>--include-prefix</b></code> <code><i>prefix</i></code></dt>
+ <dd>Add <code><i>prefix</i></code> to the generated
+ <code><b>#include</b></code> directive paths.</dd>
+
+ <dt><code><b>--include-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions used
+ to transform generated <code><b>#include</b></code> directive paths. The
+ argument to this option is a Perl-like regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any
+ character can be used as a delimiter instead of '<code><b>/</b></code>'
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can specify multiple regular expressions by repeating this option. All
+ the regular expressions are tried in the order specified and the first
+ expression that matches is used.
+
+ <p>As an example, the following expression transforms include paths in the
+ form <code><b>foo/bar-odb.h</b></code> to paths in the form
+ <code><b>foo/generated/bar-odb.h</b></code>:</p>
+
+ <p
+ class="code"><code><b>%foo/(.+)-odb.h%foo/generated/$1-odb.h%</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p></dd>
+
+ <dt><code><b>--include-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ <code><b>--include-regex</b></code> option. Use this option to find out
+ why your regular expressions don't do what you expected them to do.</dd>
+
+ <dt><code><b>--guard-prefix</b></code> <code><i>prefix</i></code></dt>
+ <dd>Add <code><i>prefix</i></code> to the generated header inclusion
+ guards. The prefix is transformed to upper case and characters that are
+ illegal in a preprocessor macro name are replaced with underscores.</dd>
+
+ <dt><code><b>--show-sloc</b></code></dt>
+ <dd>Print the number of generated physical source lines of code
+ (SLOC).</dd>
+
+ <dt><code><b>--sloc-limit</b></code> <code><i>num</i></code></dt>
+ <dd>Check that the number of generated physical source lines of code
+ (SLOC) does not exceed <code><i>num</i></code>.</dd>
+
+ <dt><code><b>--options-file</b></code> <code><i>file</i></code></dt>
+ <dd>Read additional options from <code><i>file</i></code>. Each option
+ should appear on a separate line optionally followed by space or equal
+ sign (<code><b>=</b></code>) and an option value. Empty lines and lines
+ starting with <code><b>#</b></code> are ignored. Option values can be
+ enclosed in double (<code><b>"</b></code>) or single
+ (<code><b>'</b></code>) quotes to preserve leading and trailing
+ whitespaces as well as to specify empty values. If the value itself
+ contains trailing or leading quotes, enclose it with an extra pair of
+ quotes, for example <code><b>'"x"'</b></code>. Non-leading and
+ non-trailing quotes are interpreted as being part of the option value.
+
+ <p>The semantics of providing options in a file is equivalent to providing
+ the same set of options in the same order on the command line at the point
+ where the <code><b>--options-file</b></code> option is specified except
+ that the shell escaping and quoting is not required. Repeat this option to
+ specify more than one options file.</p></dd>
+
+ <dt><code><b>-x</b></code> <code><i>option</i></code></dt>
+ <dd>Pass <code><i>option</i></code> to the underlying C++ compiler
+ (<code><b>g++</b></code>). The <code><i>option</i></code> value that
+ doesn't start with '<code><b>-</b></code>' is considered the
+ <code><b>g++</b></code> executable name.</dd>
+
+ <dt><code><b>-v</b></code></dt>
+ <dd>Print the commands executed to run the stages of compilation.</dd>
+
+ <dt><code><b>--trace</b></code></dt>
+ <dd>Trace the compilation process.</dd>
+
+ <dt><code><b>--mysql-engine</b></code> <code><i>engine</i></code></dt>
+ <dd>Use <code><i>engine</i></code> instead of the default
+ <code><b>InnoDB</b></code> in the generated database schema file. For more
+ information on the storage engine options see the MySQL documentation. If
+ you would like to use the database-default engine, pass
+ <code><b>default</b></code> as the value for this option.</dd>
+
+ <dt><code><b>--sqlite-override-null</b></code></dt>
+ <dd>Make all columns in the generated database schema allow
+ <code><b>NULL</b></code> values. This is primarily useful in schema
+ migration since SQLite does not support dropping of columns. By making all
+ columns <code><b>NULL</b></code> we can later "delete" them by setting
+ their values to <code><b>NULL</b></code>. Note that this option overrides
+ even the <code><b>not_null</b></code> pragma.</dd>
+
+ <dt><code><b>--sqlite-lax-auto-id</b></code></dt>
+ <dd>Do not force monotonically increasing automatically-assigned object
+ ids. In this mode the generated database schema omits the
+ <code><b>AUTOINCREMENT</b></code> keyword which results in faster object
+ persistence but may lead to automatically-assigned ids not being in a
+ strictly ascending order. Refer to the SQLite documentation for
+ details.</dd>
+
+ <dt><code><b>--pgsql-server-version</b></code> <code><i>ver</i></code></dt>
+ <dd>Specify the minimum PostgreSQL server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++ code
+ and schema. The version must be in the
+ <code><i>major</i><b>.</b><i>minor</i></code> form, for example,
+ <code><b>9.1</b></code>. If this option is not specified, then
+ <code><b>7.4</b></code> or later is assumed.</dd>
+
+ <dt><code><b>--oracle-client-version</b></code> <code><i>ver</i></code></dt>
+ <dd>Specify the minimum Oracle client library (OCI) version with which the
+ generated C++ code will be linked. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++ code.
+ The version must be in the <code><i>major</i><b>.</b><i>minor</i></code>
+ form, for example, <code><b>11.2</b></code>. If this option is not
+ specified, then <code><b>10.1</b></code> or later is assumed.</dd>
+
+ <dt><code><b>--oracle-warn-truncation</b></code></dt>
+ <dd>Warn about SQL names that are longer than 30 characters and are
+ therefore truncated. Note that during database schema generation
+ (<code><b>--generate-schema</b></code>) ODB detects when such truncations
+ lead to name conflicts and issues diagnostics even without this option
+ specified.</dd>
+
+ <dt><code><b>--mssql-server-version</b></code> <code><i>ver</i></code></dt>
+ <dd>Specify the minimum SQL Server server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++ code
+ and schema. The version must be in the
+ <code><i>major</i><b>.</b><i>minor</i></code> form, for example,
+ <code><b>9.0</b></code> (SQL Server 2005), <code><b>10.5</b></code>
+ (2008R2), or <code><b>11.0</b></code> (2012). If this option is not
+ specified, then <code><b>10.0</b></code> (SQL Server 2008) or later is
+ assumed.</dd>
+
+ <dt><code><b>--mssql-short-limit</b></code> <code><i>size</i></code></dt>
+ <dd>Specify the short data size limit. If a character, national character,
+ or binary data type has a maximum length (in bytes) less than or equal to
+ this limit, then it is treated as <i>short data</i>, otherwise it is
+ <i>long data</i>. For short data ODB pre-allocates an intermediate buffer
+ of the maximum size and binds it directly to a parameter or result column.
+ This way the underlying API (ODBC) can read/write directly from/to this
+ buffer. In the case of long data, the data is read/written in chunks using
+ the <code><b>SQLGetData()</b></code>/<code><b>SQLPutData()</b></code> ODBC
+ functions. While the long data approach reduces the amount of memory used
+ by the application, it may require greater CPU resources. The default
+ short data limit is 1024 bytes. When setting a custom short data limit,
+ make sure that it is sufficiently large so that no object id in the
+ application is treated as long data.</dd>
+ </dl>
+
+ <h1>SQL NAME TRANSFORMATIONS</h1>
+
+ <p>The ODB compiler provides a number of mechanisms for transforming
+ automatically-derived SQL names, such as tables, columns, etc.,
+ to match a specific naming convention. At the higher level, we can
+ add a prefix to global names (tables and, for some databases,
+ indexes and/or foreign keys) with the <code><b>--table-prefix</b></code>
+ option. Similarly, we can specify custom suffixes for
+ automatically-derived
+ index (<code><b>--index-suffix</b></code>; default is <code><b>_i</b></code>),
+ foreign key (<code><b>--fkey-suffix</b></code>; default is <code><b>_fk</b></code>), and
+ sequence (<code><b>--sequence-suffix</b></code>; default is <code><b>_seq</b></code>)
+ names. Finally, we can also convert all the names to upper or lower
+ case with the <code><b>--sql-name-case</b></code> option (valid values
+ are <code><b>upper</b></code> and <code><b>lower</b></code>).</p>
+
+ <p>At the lower level we can specify a set of regular expressions to
+ implement arbitrary transformations of the automatically-derived SQL
+ names. If we want a particular regular expression only to apply to
+ a specific name, for example, table or column, then we use one of the
+ <code><b>--</b><i>kind</i><b>-regex</b></code> options, where
+ <code><i>kind</i></code> can be <code><b>table</b></code>,
+ <code><b>column</b></code>, <code><b>index</b></code>,
+ <code><b>fkey</b></code>, <code><b>sequence</b></code>, or
+ <code><b>statement</b></code>. On the other hand, if we want our
+ regular expressions to apply to all SQL names, then we use the
+ <code><b>--sql-name-regex</b></code> option.</p>
+
+ <p>The interaction between the higher and lower level transformations
+ is as follows. Prefixes and suffixes are added first. Then the
+ regular expression transformations are applied. Finally, if requested,
+ the name is converted to upper or lower case. Note also that all of
+ these transformations except for <code><b>--table-prefix</b></code>
+ only apply to automatically-derived names. In other words, if a table,
+ column, etc., name was explicitly specified with a pragma, then it
+ is used as is, without applying any (except for the table prefix)
+ transformations.</p>
+
+ <p>The value for the <code><b>--*-regex</b></code> options is a Perl-like
+ regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>.
+ Any character can be used as a delimiter instead of <code><b>/</b></code>
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can also specify multiple regular expressions by repeating these
+ options.</p>
+
+ <p>All the regular expressions are tried in the order specified with the
+ name-specific expressions (for example, <code><b>--table-regex</b></code>)
+ tried first followed by the generic expressions
+ (<code><b>--sql-name-regex</b></code>). The first expression that
+ matches is used.</p>
+
+ <p>As an example, consider a regular expression that transforms a class
+ name in the form <code><b>CFoo</b></code> to a table name in the
+ form <code><b>FOO</b></code>:</p>
+
+ <p><code><b>--table-regex '/C(.+)/\U$1/'</b></code></p>
+
+ <p>As a more interesting example, consider the transformation of class
+ names that follow the upper camel case convention (for example,
+ <code><b>FooBar</b></code>) to table names that follow the
+ underscore-separated, all upper case convention (for example,
+ <code><b>FOO_BAR</b></code>). For this case we have to use
+ separate expressions to handle one-word, two-word, etc.,
+ names:</p>
+
+ <p><code><b>--table-regex '/([A-z][a-z]+)/\U$1/'</b></code></p>
+ <p><code><b>--table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\U$1_$2/'</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p>
+
+ <h1>REGEX AND SHELL QUOTING</h1>
+
+ <p>When entering a regular expression argument in the shell
+ command line it is often necessary to use quoting (enclosing
+ the argument in <code><b>"&nbsp;"</b></code> or
+ <code><b>'&nbsp;'</b></code>) in order to prevent the shell
+ from interpreting certain characters, for example, spaces as
+ argument separators and <code><b>$</b></code> as variable
+ expansions.</p>
+
+ <p>Unfortunately it is hard to achieve this in a manner that is
+ portable across POSIX shells, such as those found on
+ GNU/Linux and UNIX, and Windows shell. For example, if you
+ use <code><b>"&nbsp;"</b></code> for quoting you will get a
+ wrong result with POSIX shells if your expression contains
+ <code><b>$</b></code>. The standard way of dealing with this
+ on POSIX systems is to use <code><b>'&nbsp;'</b></code> instead.
+ Unfortunately, Windows shell does not remove <code><b>'&nbsp;'</b></code>
+ from arguments when they are passed to applications. As a result you
+ may have to use <code><b>'&nbsp;'</b></code> for POSIX and
+ <code><b>"&nbsp;"</b></code> for Windows (<code><b>$</b></code> is
+ not treated as a special character on Windows).</p>
+
+ <p>Alternatively, you can save regular expression options into
+ a file, one option per line, and use this file with the
+ <code><b>--options-file</b></code> option. With this approach
+ you don't need to worry about shell quoting.</p>
+
+ <h1>DIAGNOSTICS</h1>
+
+ <p>If the input file is not valid C++, <code><b>odb</b></code>
+ will issue diagnostic messages to STDERR and exit with non-zero exit
+ code.</p>
+
+ <h1>BUGS</h1>
+
+ <p>Send bug reports to the
+ <a href="mailto:odb-users@codesynthesis.com">odb-users@codesynthesis.com</a> mailing list.</p>
+
+ </div>
+ <div id="footer">
+ Copyright &#169; 2009-2024 Code Synthesis Tools CC.
+
+ <div id="terms">
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the
+ <a href="http://codesynthesis.com/licenses/fdl-1.3.txt">GNU Free
+ Documentation License, version 1.3</a>; with no Invariant Sections,
+ no Front-Cover Texts and no Back-Cover Texts.
+ </div>
+ </div>
+</div>
+</body>
+</html>
diff --git a/odb/manifest b/odb/manifest
new file mode 100644
index 0000000..adef20c
--- /dev/null
+++ b/odb/manifest
@@ -0,0 +1,31 @@
+: 1
+name: odb
+version: 2.5.0-b.26.z
+summary: ODB compiler
+license: GPL-3.0-only
+topics: C++, ORM, source code generation, object persistence, \
+ relational database
+description-file: README
+changes-file: NEWS
+url: https://www.codesynthesis.com/products/odb/
+doc-url: https://www.codesynthesis.com/products/odb/doc/manual.xhtml
+src-url: https://git.codesynthesis.com/cgit/odb/odb/
+email: odb-users@codesynthesis.com
+build-warning-email: odb-builds@codesynthesis.com
+builds: default
+builds: -( +windows -gcc ) ; Requires MinGW GCC.
+builds: &gcc ; Requires GCC with plugin support enabled.
+builds: &gcc-5+ ; Requires GCC 5 or later.
+builds: -static ; Implementation uses plugins and requires -fPIC.
+requires: host
+requires: c++11
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+
+depends: libstudxml ^1.1.0-
+depends: libcutl ^1.11.0-
+
+#depends: libstudxml == 1.1.0-b.10
+#depends: libcutl == 1.11.0-b.9
+
+depends: * cli ^1.2.0- ? ($config.odb.develop)
diff --git a/odb/odb/.gitignore b/odb/odb/.gitignore
new file mode 100644
index 0000000..4fd410e
--- /dev/null
+++ b/odb/odb/.gitignore
@@ -0,0 +1,2 @@
+/odb
+/options.?xx
diff --git a/odb/odb/buildfile b/odb/odb/buildfile
new file mode 100644
index 0000000..34a6329
--- /dev/null
+++ b/odb/odb/buildfile
@@ -0,0 +1,161 @@
+# file : odb/buildfile
+# license : GNU GPL v3; see accompanying LICENSE file
+
+define plugin: libs
+
+plugin{*}:
+{
+ bin.lib.prefix = # No lib prefix.
+ backlink = true # Backlink in forwarded configs (next to exe).
+}
+
+# For now we use the .so extension everywhere except Windows (see
+# plugin_path() in odb.cxx for details).
+#
+if ($cxx.target.class != 'windows')
+ plugin{*}: extension = so
+
+# By default install the plugin next to the driver.
+#
+# On Windows this is the only sane option where we want the plugin (odb.dll)
+# to go into bin/ since failed that we won't be able to find libraries we
+# depend on.
+#
+# On other platforms another option is to install into the GCC's plugin
+# directory. This way the same driver can be used with multiple GCC versions
+# and is something that distributions packagers sometimes want to do.
+#
+# So at some point we should also make it configurable, including support for
+# installing into GCC's plugin directory.
+#
+# NOTE: see ODB_GCC_PLUGIN_DIR when adding this support.
+#
+plugin{*}: install = bin/
+
+import libs = libcutl%lib{cutl}
+import libs += libstudxml%lib{studxml}
+
+./: exe{odb} plugin{odb}
+
+# We need to make driver depend on plugin but not link it so that when, for
+# example, driver is imported, plugin is updated as well.
+#
+# We, however, don't want to install via the driver since the same driver
+# build could be used with multiple plugin builds (e.g., for different GCC
+# versions, which is something distribution packagers sometimes want to do).
+# @@ For this we will have to wait for operation-specific values support.
+#
+exe{odb}: cxx{odb}
+exe{odb}: libus{odb}: bin.whole = false
+exe{odb}: plugin{odb}: include = adhoc
+
+# Target metadata, see also --build2-metadata in odb.cxx.
+#
+# While ODB itself doesn't use any environment variables, it uses GCC
+# underneath which does (see "Environment Variables Affecting GCC").
+#
+exe{odb}:
+{
+ export.metadata = 1 odb
+ odb.name = [string] odb
+ odb.version = [string] $version
+ odb.checksum = [string] $version
+ odb.environment = [strings] CPATH CPLUS_INCLUDE_PATH GCC_EXEC_PREFIX COMPILER_PATH
+}
+
+plugin{odb}: libus{odb}
+
+switch $cxx.target.system
+{
+ # On Windows we have to link the import stub.
+ #
+ case 'mingw32'
+ plugin{odb}: cxx.libs += $plugin_dir/cc1plus.exe.a
+
+ # On Mac OS we have to allow undefined symbols.
+ #
+ case 'darwin'
+ plugin{odb}: cxx.loptions += -undefined dynamic_lookup
+}
+
+libus{odb}: {hxx ixx txx cxx}{** -odb -options -pregenerated/**} $libs
+
+# Build options.
+#
+# Note: escape backslashes in gxx_name.
+#
+cxx.poptions += "-I$plugin_dir/include"
+cxx.poptions += "-DODB_GXX_NAME=\"$regex.replace($gxx_name, '\\', '\\\\')\""
+cxx.poptions += -DODB_BUILD2 # @@ TMP while supporting other build systems.
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+libus{odb}: pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/pregenerated)" # Note: must come first.
+
+# Distribute pregenerated versions only in the consumption build.
+#
+pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+libus{odb}: {hxx ixx cxx}{options}: include = $develop
+
+if $develop
+ import! [metadata] cli = cli%exe{cli}
+
+# In the development build distribute regenerated {hxx ixx cxx}{options},
+# remapping their locations to the paths of the pregenerated versions (which
+# are only distributed in the consumption build; see above). This way we make
+# sure that the distributed files are always up-to-date.
+#
+<{hxx ixx cxx}{options}>: cli{options} $cli
+{
+ dist = ($develop ? pregenerated/odb/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb --guard-prefix ODB \
+ --generate-file-scanner --generate-specifier --generate-modifier \
+ --generate-description --suppress-undocumented \
+ --cxx-prologue '#include <odb/option-parsers.hxx>'
+
+ $cli $options -o $out_base $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ if diff $src_base/pregenerated/odb/options.hxx $path($>[0]) >- && \
+ diff $src_base/pregenerated/odb/options.ixx $path($>[1]) >- && \
+ diff $src_base/pregenerated/odb/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $src_base/pregenerated/odb/options.hxx
+ cp $path($>[1]) $src_base/pregenerated/odb/options.ixx
+ cp $path($>[2]) $src_base/pregenerated/odb/options.cxx
+}}
+
+#
+##
+
+# Pass the copyright notice extracted from the LICENSE file.
+#
+obj{odb}: cxx.poptions += -DODB_COPYRIGHT=\"$copyright\"
+
+# Don't install any of the plugin's headers.
+#
+{hxx ixx txx}{*}: install = false
diff --git a/odb/odb/common-query.cxx b/odb/odb/common-query.cxx
new file mode 100644
index 0000000..517c92c
--- /dev/null
+++ b/odb/odb/common-query.cxx
@@ -0,0 +1,1413 @@
+// file : odb/common-query.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/common-query.hxx>
+
+using namespace std;
+
+// query_utils
+//
+
+string query_utils::
+depth_suffix (size_t d)
+{
+ if (d != 0)
+ {
+ ostringstream os;
+ os << d;
+ return '_' + os.str ();
+ }
+
+ return string ();
+}
+
+// Collect nested (composite) types as generated by query_columns.
+//
+struct query_nested_types: object_columns_base, virtual context
+{
+ query_nested_types (bool ptr): ptr_ (ptr), in_ptr_ (false), depth_ (0) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // We don't want to traverse bases.
+ //
+ names (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (m != 0)
+ {
+ string name (prefix_ + public_name (*m));
+ name += in_ptr_ ? "_column_class" : "_class";
+ name += query_utils::depth_suffix (depth_);
+ name += '_';
+ types.push_back (name);
+
+ depth_++;
+ string p (prefix_);
+ prefix_ = name + "::";
+ object_columns_base::traverse_composite (m, c);
+ prefix_ = p;
+ depth_--;
+ }
+ else
+ object_columns_base::traverse_composite (m, c); // Base
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // The same logic as in query_columns.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ bool poly_ref (m.count ("polymorphic-ref"));
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ if (ptr_ || poly_ref)
+ object_columns_base::traverse_pointer (m, c);
+ else
+ {
+ in_ptr_ = true;
+ object_columns_base::traverse_pointer (m, c);
+ in_ptr_ = false;
+ }
+ }
+ }
+
+public:
+ strings types;
+
+protected:
+ bool ptr_;
+ bool in_ptr_; // True while we are "inside" an object pointer.
+ string prefix_;
+ size_t depth_;
+};
+
+void query_utils::
+inst_query_columns (bool decl,
+ bool ptr,
+ string const& type,
+ string const& alias,
+ semantics::class_& c)
+{
+ inst_header (decl);
+ os << (ptr ? "pointer_" : "") << "query_columns<" << endl
+ << " " << type << "," << endl
+ << " id_" << db << "," << endl
+ << " " << alias << " >;"
+ << endl;
+
+ // If we are generating extern declarations, we also have to generate
+ // them for all the nested (composite) structs. That's what VC++ needs.
+ //
+ if (decl)
+ {
+ query_nested_types t (ptr);
+ t.traverse (c);
+
+ for (strings::iterator i (t.types.begin ()); i != t.types.end (); ++i)
+ {
+ inst_header (decl, true); // Omit export, GCC doesn't like it.
+ os << (ptr ? "pointer_" : "") << "query_columns<" << endl
+ << " " << type << "," << endl
+ << " id_" << db << "," << endl
+ << " " << alias << " >::" << *i << ";"
+ << endl;
+ }
+ }
+}
+
+// query_tags
+//
+
+void query_tags::
+traverse (semantics::class_& c)
+{
+ if (object (c) || composite (c))
+ {
+ object_columns_base::traverse (c);
+ }
+ else if (view (c))
+ {
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ generate (i->alias);
+ }
+ }
+ }
+ // Otherwise it is a transient base (of a composite value).
+
+ if (nl_)
+ os << endl;
+}
+
+void query_tags::
+traverse_object (semantics::class_& c)
+{
+ names (c); // We don't want to traverse bases.
+}
+
+void query_tags::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Don't generate an empty struct if we don't have any pointers.
+ //
+ if (!has_a (c, test_pointer))
+ return;
+
+ if (nl_)
+ os << endl;
+
+ os << "struct " << public_name (*m) << "_tag" <<
+ query_utils::depth_suffix (depth_)
+ << "{";
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ os << "};";
+
+ nl_ = false;
+}
+
+void query_tags::
+traverse_pointer (semantics::data_member& m, semantics::class_&)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ generate (public_name (m));
+}
+
+void query_tags::
+generate (string const& name)
+{
+ os << "struct " << name << "_tag;";
+ nl_ = true;
+}
+
+// query_alias_traits
+//
+
+query_alias_traits::
+query_alias_traits (semantics::class_& c, bool decl)
+ : decl_ (decl), depth_ (0)
+{
+ scope_ = "access::";
+ scope_ += (object (c) ? "object_traits_impl" : "view_traits_impl");
+ scope_ += "< " + class_fq_name (c) + ", id_" + db.string () + " >";
+}
+
+void query_alias_traits::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_alias_traits::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ string old_scope (scope_);
+ scope_ += "::" + public_name (*m) + "_tag" +
+ query_utils::depth_suffix (depth_);
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+}
+
+void query_alias_traits::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ if (decl_)
+ generate_decl (public_name (m), c);
+ else
+ generate_def (m, c);
+}
+
+void query_alias_traits::
+generate_decl (string const& tag, semantics::class_& c)
+{
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+ semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+
+ if (poly_derived)
+ generate_decl (tag, *poly_base);
+
+ string const& fq_name (class_fq_name (c));
+
+ os << "template <>" << endl
+ << "struct " << exp << "alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>"
+ << "{";
+
+ if (poly_derived)
+ os << "typedef alias_traits<" << endl
+ << " " << class_fq_name (*poly_base) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>" << endl
+ << "base_traits;"
+ << endl;
+
+ // For dynamic multi-database support also generate common traits
+ // alias. Note that the tag type is the same since they all are
+ // derived from object_traits.
+ //
+ if (db != database::common && multi_dynamic)
+ os << "typedef alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_common," << endl
+ << " " << scope_ << "::" << tag << "_tag>" << endl
+ << "common_traits;"
+ << endl;
+
+ generate_decl_body (); // Table name, etc.
+
+ os << "};";
+}
+
+void query_alias_traits::
+generate_decl_body ()
+{
+}
+
+void query_alias_traits::
+generate_def (semantics::data_member&, semantics::class_&)
+{
+}
+
+void query_alias_traits::
+generate_def (string const&, semantics::class_&, string const&)
+{
+}
+
+// query_columns_base
+//
+
+query_columns_base::
+query_columns_base (semantics::class_& c, bool decl, bool inst)
+ : decl_ (decl), inst_ (inst), depth_ (0)
+{
+ string const& n (class_fq_name (c));
+
+ if (decl)
+ scope_ = "access::object_traits_impl< " + n + ", id_" +
+ db.string () + " >";
+ else
+ scope_ = "query_columns_base< " + n + ", id_" + db.string () + " >";
+}
+
+void query_columns_base::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_columns_base::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Don't generate an empty struct if we don't have any pointers.
+ //
+ if (!has_a (c, test_pointer))
+ return;
+
+ string name (public_name (*m));
+ string dsuffix (query_utils::depth_suffix (depth_));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl
+ << "struct " << name << "_base" << dsuffix << '_'
+ << "{";
+
+ string old_scope (scope_);
+ scope_ += "::" + name + "_tag" + dsuffix;
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+
+ os << "};";
+ }
+ else
+ {
+ string old_scope (scope_);
+ scope_ += "::" + name + "_base" + dsuffix + '_';
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+ }
+}
+
+void query_columns_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ string name (public_name (m));
+ string const& fq_name (class_fq_name (c));
+ bool inv (inverse (m, key_prefix_));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef" << endl
+ << "odb::alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << name << "_tag>" << endl
+ << name << "_alias_;"
+ << endl;
+
+ if (inv)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_type_ ;"
+ << endl
+ << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ else if (inst_)
+ {
+ generate_inst (m, c);
+ }
+ else
+ {
+ // Generate explicit template instantiation directive for the
+ // pointed-to pointer_query_columns.
+ //
+ if (multi_dynamic)
+ generate_inst (m, c);
+
+ if (inv)
+ os << const_ << scope_ << "::" << name << "_type_" << endl
+ << scope_ << "::" << name << ";"
+ << endl;
+ }
+}
+
+void query_columns_base::
+generate_inst (semantics::data_member& m, semantics::class_& c)
+{
+ string name (public_name (m));
+ string const& fq_name (class_fq_name (c));
+
+ string alias (scope_ + "::" + name + "_alias_");
+
+ // Instantiate base [pointer_]query_columns.
+ //
+ {
+ instance<query_columns_base_insts> b (true, inst_, alias, true);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ // If the pointed-to class has no pointers of its own then
+ // pointer_query_columns just derives from query_columns and
+ // that's what we need to instantiate.
+ //
+ inst_query_columns (inst_,
+ has_a (c, test_pointer | include_base),
+ fq_name,
+ alias,
+ c);
+}
+
+// query_columns
+//
+
+query_columns::
+query_columns (bool decl, bool ptr, semantics::class_& c)
+ : decl_ (decl), ptr_ (ptr), poly_ref_ (false), in_ptr_ (false),
+ fq_name_ (class_fq_name (c)),
+ resue_abstract_ (abstract (c) && !polymorphic (c)),
+ depth_ (0)
+{
+}
+
+void query_columns::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_columns::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Use _class_ instead of _type_ to avoid potential clashes between
+ // the class and member names.
+ //
+ string name (public_name (*m));
+ string suffix (in_ptr_ ? "_column_class" : "_class");
+
+ // Add depth to the nested composite to avoid potential name conflicts
+ // in situations like these:
+ //
+ // struct inner { ... };
+ // struct outer { inner value; };
+ // struct object { outer value; }
+ //
+ string dsuffix (query_utils::depth_suffix (depth_));
+ suffix += dsuffix;
+ suffix += '_';
+
+ depth_++;
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl
+ << "struct ";
+
+ // For some bizarre reason VC++ needs the export directive for
+ // a type nested in an (exported) template. This appears not
+ // to cause any problems for GCC.
+ //
+ // We only generate the export directive if we are also
+ // explicitly instantiating the query_columns templates.
+ //
+ if (multi_dynamic && !resue_abstract_)
+ os << exp;
+
+ os << name << suffix;
+
+ // Derive from the base in query_columns_base. It contains columns
+ // data for the pointer members.
+ //
+ if (!ptr_ && !poly_ref_ && has_a (c, test_pointer))
+ os << ": " << name << "_base" << dsuffix << '_';
+
+ os << "{";
+
+ if (!const_.empty ())
+ os << name << suffix << " ()" // Need user-defined default c-tor for
+ << "{" // initialization of static const.
+ << "}";
+
+ object_columns_base::traverse_composite (m, c);
+
+ os << "};";
+
+ if (!in_ptr_)
+ os << "static " << const_ << name << suffix << " " << name << ";"
+ << endl;
+ }
+ else
+ {
+ // Handle nested members first.
+ //
+ string old_scope (scope_);
+ scope_ += "::" + name + suffix;
+
+ object_columns_base::traverse_composite (m, c);
+
+ scope_ = old_scope;
+
+ // Composite member. Note that here we don't use suffix for the in-
+ // pointer case because the actual pointer column type derives from
+ // the composite column type (dual interface; see traverse_pointer()
+ // below).
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << const_ << "typename " << tmpl << "::" << name <<
+ (in_ptr_ ? string ("_type_") : suffix) << endl
+ << tmpl << "::" << name << ";"
+ << endl;
+ }
+
+ depth_--;
+}
+
+void query_columns::
+column_ctor (string const&, string const&, string const&)
+{
+}
+
+void query_columns::
+column_common (semantics::data_member& m,
+ string const& type,
+ string const&,
+ string const& suffix)
+{
+ string name (public_name (m));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef odb::query_column< " << type << " > " << name <<
+ suffix << ";"
+ << endl;
+ }
+ else
+ {
+ // Note that here we don't use suffix.
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << const_ << "typename " << tmpl << "::" << name << "_type_" << endl
+ << tmpl << "::" << name << ";"
+ << endl;
+ }
+}
+
+bool query_columns::
+traverse_column (semantics::data_member& m, string const& column, bool)
+{
+ semantics::names* hint;
+ semantics::type* t (&utype (m, hint));
+
+ // Unwrap it if it is a wrapper.
+ //
+ if (semantics::type* wt = wrapper (*t, hint))
+ t = &utype (*wt, hint);
+
+ column_common (m, t->fq_name (hint), column);
+
+ if (decl_)
+ {
+ string name (public_name (m));
+
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+
+ return true;
+}
+
+void query_columns::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // If this is for the pointer_query_columns and the member is not
+ // inverse, then create the normal member corresponding to the id
+ // column. This will allow the user to check it for NULL or to
+ // compare ids. In case this is for query_columns, then for the
+ // inverse member everything has been generated in query_columns_base.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ // If we ignore polymorphic references, then a view that uses a custom
+ // join condition based on id will use the id column from the base
+ // table. But the base table hasn't been joined yet. To resolve this
+ // we will generate the id member that points to our column.
+ //
+ poly_ref_ = m.count ("polymorphic-ref");
+
+ string name (public_name (m));
+
+ data_member_path& id (*id_member (c));
+ semantics::names* hint;
+ semantics::type& t (utype (id, hint));
+
+ if (composite_wrapper (t))
+ {
+ // Composite id.
+ //
+
+ // For pointer_query_columns and poly refs generate normal composite
+ // mapping.
+ //
+ if (ptr_ || poly_ref_)
+ object_columns_base::traverse_pointer (m, c);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ in_ptr_ = true;
+ object_columns_base::traverse_pointer (m, c);
+ in_ptr_ = false;
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " <<
+ name << "_column_class" << query_utils::depth_suffix (depth_) << '_'
+ << "{";
+
+ if (!const_.empty ())
+ os << name << "_type_ ()" // Need user-defined default c-tor for
+ << "{" // initialization of static const.
+ << "}";
+
+ os << "};";
+
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ }
+ else
+ {
+ // Simple id.
+ //
+ string type (t.fq_name (hint));
+ string col (column_name (m, key_prefix_, default_name_, column_prefix_));
+
+ // For pointer_query_columns and poly refs generate normal column mapping.
+ //
+ if (ptr_ || poly_ref_)
+ column_common (m, type, col);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ column_common (m, type, col, "_column_type_");
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " << name << "_column_type_"
+ << "{";
+
+ column_ctor (type, name + "_type_", name + "_column_type_");
+
+ os << "};";
+ }
+ }
+
+ if (decl_)
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+
+ poly_ref_ = false;
+}
+
+// query_columns_bases
+//
+
+void query_columns_bases::
+traverse (type& c)
+{
+ // Ignore transient bases. Not used for views.
+ //
+ if (!object (c))
+ return;
+
+ if (first_)
+ {
+ os << ":" << endl
+ << " ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " <<
+ class_fq_name (c) << ", id_" << db << ", ";
+
+ // If our base is polymorphic, then it has its own table/alias.
+ //
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " >";
+}
+
+// query_columns_base_aliases
+//
+
+void query_columns_base_aliases::
+traverse (type& c)
+{
+ // Ignore transient bases. Not used for views.
+ //
+ if (!object (c))
+ return;
+
+ string const& name (class_name (c));
+
+ os << "// " << name << endl
+ << "//" << endl
+ << "typedef " << (ptr_ ? "pointer_query_columns" : "query_columns") <<
+ "< " << class_fq_name (c) << ", id_" << db << ", ";
+
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " > " << name << ";"
+ << endl;
+}
+
+// query_columns_base_insts
+//
+
+query_columns_base_insts::
+query_columns_base_insts (bool test_ptr,
+ bool decl,
+ string const& alias,
+ bool poly)
+ : test_ptr_ (test_ptr), decl_ (decl), alias_ (alias), poly_ (poly)
+{
+ *this >> inherits_ >> *this;
+}
+
+query_columns_base_insts::
+query_columns_base_insts (query_columns_base_insts const& x)
+ : context (), // @@ -Wextra
+ test_ptr_ (x.test_ptr_),
+ decl_ (x.decl_),
+ alias_ (x.alias_),
+ poly_ (x.poly_)
+{
+ *this >> inherits_ >> *this;
+}
+
+void query_columns_base_insts::
+traverse (type& c)
+{
+ if (!object (c))
+ return;
+
+ bool poly (polymorphic (c));
+ if (poly && (poly != poly_))
+ return;
+
+ bool ptr (has_a (c, test_pointer | include_base));
+
+ string old_alias;
+ if (poly)
+ {
+ old_alias = alias_;
+ alias_ += "::base_traits";
+ }
+
+ // Instantiate bases recursively.
+ //
+ inherits (c, inherits_);
+
+ inst_query_columns (decl_,
+ test_ptr_ && ptr,
+ class_fq_name (c),
+ alias_,
+ c);
+
+ if (!test_ptr_ && ptr)
+ inst_query_columns (decl_, true, class_fq_name (c), alias_, c);
+
+ if (poly)
+ alias_ = old_alias;
+}
+
+// query_columns_type
+//
+
+void query_columns_type::
+traverse (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ if (ptr_)
+ {
+ os << "template <typename A>" << endl
+ << "struct pointer_query_columns< " << type << ", id_" << db << ", A >";
+
+ // If we don't have pointers (in the whole hierarchy), then
+ // pointer_query_columns and query_columns are the same.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ {
+ os << ":" << endl
+ << " query_columns< " << type << ", id_" << db << ", A >"
+ << "{"
+ << "};";
+ }
+ else
+ {
+ {
+ instance<query_columns_bases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ os << "{";
+
+ {
+ instance<query_columns_base_aliases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ {
+ bool true_ (true);
+ instance<query_columns> t (true_, ptr_, c); //@@ forwarding
+ t->traverse (c);
+ }
+
+ os << "};";
+
+ generate_impl (c);
+ }
+ }
+ else if (decl_)
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+
+ if (has_ptr)
+ {
+ // Generate aliases.
+ //
+ {
+ bool true_ (true); //@@ (im)perfect forwarding
+ instance<query_alias_traits> t (c, true_);
+ t->traverse (c);
+ }
+
+ // This class contains everything for inverse pointers and
+ // aliases for non-inverse ones. It doesn't depend on the
+ // table alias (A) template argument.
+ //
+ os << "template <>" << endl
+ << "struct " << exp << "query_columns_base< " << type << ", " <<
+ "id_" << db << " >"
+ << "{";
+
+ bool true_ (true); //@@ (im)perfect forwarding.
+ bool false_ (false);
+ instance<query_columns_base> t (c, true_, false_);
+ t->traverse (c);
+
+ os << "};";
+ }
+
+ os << "template <typename A>" << endl
+ << "struct query_columns< " << type << ", id_" << db << ", A >";
+
+ if (has_ptr)
+ os << ":" << endl
+ << " query_columns_base< " << type << ", id_" << db << " >";
+
+ {
+ instance<query_columns_bases> b (ptr_, !has_ptr);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ os << "{";
+
+ {
+ instance<query_columns_base_aliases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ {
+ instance<query_columns> t (decl_, ptr_, c);
+ t->traverse (c);
+ }
+
+ os << "};";
+
+ generate_impl (c);
+ }
+ else if (inst_)
+ {
+ // If we have the extern symbol, generate extern template declarations.
+ //
+ if (!ext.empty ())
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+ bool reuse_abst (abstract (c) && !polymorphic (c));
+
+ if (has_ptr || !reuse_abst)
+ {
+ os << "#ifdef " << ext << endl
+ << endl;
+
+ if (has_ptr)
+ {
+ bool true_ (true); //@@ (im)perfect forwarding.
+ bool false_ (false);
+
+ instance<query_columns_base> t (c, false_, true_);
+ t->traverse (c);
+ }
+
+ // Don't generate it for reuse-abstract classes.
+ //
+ if (!reuse_abst)
+ generate_inst (c);
+
+ os << "#endif // " << ext << endl
+ << endl;
+ }
+ }
+ }
+ else
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+
+ // Generate alias_traits specializations. While the class
+ // is generated even if our base has a pointer, there is
+ // not source code if we don't have pointers ourselves.
+ //
+ if (has_ptr)
+ {
+ bool false_ (false); //@@ (im)perfect forwarding
+ instance<query_alias_traits> t (c, false_);
+ t->traverse (c);
+ }
+
+ // query_columns_base
+ //
+ if (has_ptr)
+ {
+ bool false_ (false); //@@ (im)perfect forwarding.
+ instance<query_columns_base> t (c, false_, false_);
+ t->traverse (c);
+ }
+
+ // Explicit template instantiations. Don't generate it for reuse-
+ // abstract classes.
+ //
+ if (multi_dynamic && (!abstract (c) || polymorphic (c)))
+ generate_inst (c);
+ }
+}
+
+void query_columns_type::
+generate_impl (type& c)
+{
+ string guard;
+
+ // Exclude definitions (they will be explicitly instantiated once in
+ // the source file) unless we have the extern symbol. In this case
+ // the extern template declaration will make sure we don't get
+ // instantiations in multiple places and we will avoid the VC++
+ // warning C4661 (no definition provided).
+ //
+ if (multi_dynamic && ext.empty ())
+ {
+ guard = make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
+
+ os << "#ifdef " << guard << endl
+ << endl;
+ }
+
+ {
+ bool false_ (false);
+ instance<query_columns> t (false_, ptr_, c);
+ t->traverse (c);
+ }
+
+ if (!guard.empty ())
+ os << "#endif // " << guard << endl
+ << endl;
+}
+
+void query_columns_type::
+generate_inst (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ // Explicit template instantiations. Here is what we need to
+ // instantiate
+ //
+ // 1. Reuse inheritance bases all the way to the ultimate base.
+ // Unlike poly inheritance, reuse inheritance uses the table
+ // alias of the derived type. Note that bases can have object
+ // pointers of their own but their types have already been
+ // instantiated by step 3 below performed for the base object.
+ //
+ // 2. Object pointers. Note that while object pointers cannot have
+ // their own pointers, they can have reuse inheritance bases.
+ //
+ // 3. The query_columns class for the table itself.
+ //
+ // We also need to repeat these steps for pointer_query_columns
+ // since it is used by views.
+ //
+ string alias ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ // 1
+ //
+ {
+ instance<query_columns_base_insts> b (false, inst_, alias, false);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ // 2: Handled by query_columns_base (which is where we generate
+ // all the aliases for object pointers).
+ //
+
+ // 3
+ //
+ inst_query_columns (inst_, false, type, alias, c);
+
+ if (has_a (c, test_pointer | exclude_base))
+ inst_query_columns (inst_, true, type, alias, c);
+}
+
+// view_query_columns_type
+//
+
+void view_query_columns_type::
+traverse (type& c)
+{
+ if (decl_)
+ generate_decl (c);
+ else
+ generate_def (c);
+}
+
+void view_query_columns_type::
+generate_decl (type& c)
+{
+ string const& type (class_fq_name (c));
+ size_t obj_count (c.get<size_t> ("object-count"));
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ // Generate alias_traits specializations.
+ //
+ {
+ bool true_ (true); //@@ (im)perfect forwarding
+ instance<query_alias_traits> at (c, true_);
+
+ for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ at->generate_decl (i->alias, o);
+ }
+ }
+
+ // If we have the extern symbol, generate extern template declarations.
+ // Do it before query_columns since the inheritance will trigger
+ // instantiation and we won't be able to change visibility (GCC).
+ //
+ if (obj_count != 0 && multi_dynamic && !ext.empty ())
+ {
+ os << "#ifdef " << ext << endl
+ << endl;
+
+ generate_inst (c);
+
+ os << "#endif // " << ext << endl
+ << endl;
+ }
+
+ // query_columns
+ //
+ os << "struct " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_" << db << " >::query_columns";
+
+ if (obj_count > 1)
+ {
+ os << "{";
+
+ for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ bool alias (!i->alias.empty ());
+ semantics::class_& o (*i->obj);
+ string const& oname (alias ? i->alias : class_name (o));
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << "// " << oname << endl
+ << "//" << endl
+ << "typedef" << endl
+ << "odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ i->alias != table.uname ()))
+ {
+ os << " odb::alias_traits< " << otype << "," << endl
+ << " id_" << db << "," << endl
+ << " access::view_traits_impl< " << type << ", id_" << db <<
+ " >::" << i->alias << "_tag> >" << endl;
+ }
+ else
+ os << " odb::access::object_traits_impl< " << otype << ", id_" <<
+ db << " > >" << endl;
+
+ os << oname << ";"
+ << endl;
+ }
+
+ os << "};";
+ }
+ else
+ {
+ // For a single object view we generate a shortcut without
+ // an intermediate typedef.
+ //
+ view_object const* vo (0);
+ for (view_objects::const_iterator i (objs.begin ());
+ vo == 0 && i < objs.end ();
+ ++i)
+ {
+ if (i->kind == view_object::object)
+ vo = &*i;
+ }
+
+ bool alias (!vo->alias.empty ());
+ semantics::class_& o (*vo->obj);
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << ":" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ vo->alias != table.uname ()))
+ {
+ os << " odb::alias_traits<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl
+ << " access::view_traits_impl< " << type << ", id_" <<
+ db << " >::" << vo->alias << "_tag> >";
+ }
+ else
+ os << " odb::access::object_traits_impl< " << otype <<
+ ", id_" << db << " > >";
+
+ os << "{"
+ << "};";
+ }
+}
+
+void view_query_columns_type::
+generate_def (type& c)
+{
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ // Generate alias_traits specializations.
+ //
+ {
+ bool false_ (false); //@@ (im)perfect forwarding
+ instance<query_alias_traits> at (c, false_);
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ {
+ at->generate_def (i->alias, o, i->alias);
+ }
+ }
+ }
+
+ if (multi_dynamic)
+ generate_inst (c);
+}
+
+void view_query_columns_type::
+generate_inst (type& c)
+{
+ string const& type (class_fq_name (c));
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ // Instantiate [pointer_]query_columns.
+ //
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue; // Instantiated by object.
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ {
+ string const& otype (class_fq_name (o));
+ string alias ("odb::alias_traits<\n"
+ " " + otype + ",\n"
+ " id_" + db.string () + ",\n"
+ " " + traits + "::" + i->alias + "_tag>");
+
+ // Instantiate base [pointer_]query_columns.
+ //
+ {
+ instance<query_columns_base_insts> b (true, decl_, alias, true);
+ traversal::inherits i (*b);
+ inherits (o, i);
+ }
+
+ // If the pointed-to class has no pointers of its own then
+ // pointer_query_columns just derives from query_columns and
+ // that's what we need to instantiate.
+ //
+ inst_query_columns (decl_,
+ has_a (o, test_pointer | include_base),
+ otype,
+ alias,
+ o);
+ }
+ }
+}
diff --git a/odb/odb/common-query.hxx b/odb/odb/common-query.hxx
new file mode 100644
index 0000000..e90dd69
--- /dev/null
+++ b/odb/odb/common-query.hxx
@@ -0,0 +1,279 @@
+// file : odb/common-query.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_COMMON_QUERY_HXX
+#define ODB_COMMON_QUERY_HXX
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+
+//
+// Query-related generators.
+//
+
+struct query_utils: virtual context
+{
+ void
+ inst_query_columns (bool decl, // Extern declaration or instanatiation.
+ bool ptr, // pointer_query_columns or query_columns
+ string const& type, // Object fq-type.
+ string const& alias, // Table alias.
+ semantics::class_&); // Traverse for nested structs.
+
+ static string
+ depth_suffix (size_t);
+};
+
+// Generate query tags for pointers in this object.
+//
+struct query_tags: object_columns_base, virtual context
+{
+ typedef query_tags base;
+
+ query_tags (): nl_ (false), depth_ (0) {}
+
+ virtual void
+ traverse (semantics::class_&);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate (string const& name);
+
+private:
+ bool nl_;
+ size_t depth_;
+};
+
+// Generate alias_traits specializations for pointers in this objects.
+//
+struct query_alias_traits: object_columns_base, virtual context
+{
+ typedef query_alias_traits base;
+
+ query_alias_traits (semantics::class_&, bool decl);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_decl (string const& tag, semantics::class_&);
+
+ virtual void
+ generate_decl_body ();
+
+ virtual void
+ generate_def (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_def (string const& tag, semantics::class_&, string const& alias);
+
+protected:
+ bool decl_;
+ string scope_;
+ size_t depth_;
+};
+
+// Generate query columns in the query_columns_base class.
+//
+struct query_columns_base: object_columns_base, query_utils
+{
+ typedef query_columns_base base;
+
+ // If inst is true, then we generate extern template declarations
+ // in the header.
+ //
+ query_columns_base (semantics::class_&, bool decl, bool inst);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_inst (semantics::data_member&, semantics::class_&);
+
+protected:
+ bool decl_;
+ bool inst_;
+ string const_; // Const prefix or empty.
+ string scope_;
+ size_t depth_;
+};
+
+// Generate query columns in the query_columns or pointer_query_columns
+// class.
+//
+struct query_columns: object_columns_base, virtual context
+{
+ typedef query_columns base;
+
+ query_columns (bool decl, bool ptr, semantics::class_&);
+
+ virtual void
+ column_ctor (string const& type, string const& name, string const& base);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ column_common (semantics::data_member&,
+ string const& type,
+ string const& column,
+ string const& suffix = "_type_");
+
+ virtual bool
+ traverse_column (semantics::data_member&, string const&, bool);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+protected:
+ bool decl_;
+ bool ptr_;
+ bool poly_ref_;
+ string const_; // Const prefix or empty.
+ bool in_ptr_; // True while we are "inside" an object pointer.
+ string fq_name_;
+ bool resue_abstract_; // Object is reuse-abstract.
+ string scope_;
+ size_t depth_;
+};
+
+// Generate the list of base classes for the query_columns or
+// pointer_query_columns class.
+//
+struct query_columns_bases: traversal::class_, virtual context
+{
+ typedef query_columns_bases base;
+
+ query_columns_bases (bool ptr, bool first = true)
+ : ptr_ (ptr), first_ (first) {}
+
+ virtual void
+ traverse (type&);
+
+private:
+ bool ptr_;
+ bool first_;
+};
+
+// Generate the base class aliases (typedefs). These can be used to
+// resolve member ambiguities.
+//
+struct query_columns_base_aliases: traversal::class_, virtual context
+{
+ typedef query_columns_base_aliases base;
+
+ query_columns_base_aliases (bool ptr): ptr_ (ptr) {}
+
+ virtual void
+ traverse (type&);
+
+private:
+ bool ptr_;
+};
+
+// Generate explicit instantiations of base classes.
+//
+struct query_columns_base_insts: traversal::class_, query_utils
+{
+ typedef query_columns_base_insts base;
+
+ query_columns_base_insts (bool test_ptr,
+ bool decl,
+ string const& alias,
+ bool poly); // Traverse polymorphic bases.
+ query_columns_base_insts (query_columns_base_insts const&);
+
+ virtual void
+ traverse (type&);
+
+private:
+ bool test_ptr_;
+ bool decl_;
+ string alias_;
+ bool poly_;
+ traversal::inherits inherits_;
+};
+
+// Generate the query_columns_base/query_columns or pointer_query_columns
+// classes for objects.
+//
+struct query_columns_type: traversal::class_, query_utils
+{
+ typedef query_columns_type base;
+
+ // Depending on the ptr argument, generate query_columns or
+ // pointer_query_columns specialization. The latter is used
+ // for object pointers where we don't support nested pointers.
+ //
+ // If decl is false then generate definitions (only needed for
+ // query_columns so ptr should be false).
+ //
+ // If inst if true, then generate extern template declarations
+ // in the header (ptr and decl should be false).
+ //
+ query_columns_type (bool ptr, bool decl, bool inst)
+ : ptr_ (ptr), decl_ (decl), inst_ (inst) {}
+
+ virtual void
+ traverse (type&);
+
+ virtual void
+ generate_impl (type&);
+
+ virtual void
+ generate_inst (type&);
+
+public:
+ bool ptr_;
+ bool decl_;
+ bool inst_;
+};
+
+// Generate the query_columns class for views.
+//
+struct view_query_columns_type: traversal::class_, query_utils
+{
+ typedef view_query_columns_type base;
+
+ view_query_columns_type (bool decl): decl_ (decl) {}
+
+ virtual void
+ traverse (type&);
+
+ void
+ generate_decl (type&);
+
+ void
+ generate_def (type&);
+
+ void
+ generate_inst (type&);
+
+public:
+ bool decl_;
+};
+
+#endif // ODB_COMMON_QUERY_HXX
diff --git a/odb/odb/common.cxx b/odb/odb/common.cxx
new file mode 100644
index 0000000..63e49ad
--- /dev/null
+++ b/odb/odb/common.cxx
@@ -0,0 +1,584 @@
+// file : odb/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+
+using namespace std;
+
+//
+// object_members_base
+//
+
+void object_members_base::
+traverse_simple (semantics::data_member&)
+{
+}
+
+void object_members_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ if (!view_member (m)) // Not really "as if" pointed-to id member.
+ traverse_member (m, utype (*id_member (c)));
+}
+
+void object_members_base::
+traverse_composite (semantics::data_member*, semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_members_base::
+traverse_composite_wrapper (semantics::data_member* m,
+ semantics::class_& c,
+ semantics::type*)
+{
+ traverse_composite (m, c);
+}
+
+void object_members_base::
+traverse_container (semantics::data_member&, semantics::type&)
+{
+}
+
+void object_members_base::
+traverse_object (semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_members_base::
+traverse_view (semantics::class_& c)
+{
+ // A view has no bases.
+ //
+ names (c);
+}
+
+void object_members_base::
+traverse (semantics::class_& c)
+{
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ {
+ // Ignore transient bases.
+ //
+ return;
+ }
+ else if (k == class_composite)
+ {
+ if (member_scope_.empty ())
+ member_scope_.push_back (class_inheritance_chain ());
+
+ member_scope_.back ().push_back (&c);
+
+ traverse_composite_wrapper (0, c, 0);
+
+ member_scope_.back ().pop_back ();
+
+ if (member_scope_.back ().empty ())
+ member_scope_.pop_back ();
+
+ return;
+ }
+
+ if (top_level_)
+ top_level_ = false;
+ else
+ {
+ // Unless requested otherwise, don't go into bases if we are a derived
+ // type in a polymorphic hierarchy.
+ //
+ if (!traverse_poly_base_ && polymorphic (c))
+ return;
+ }
+
+ if (context::top_object == 0)
+ context::top_object = &c;
+
+ semantics::class_* prev (context::cur_object);
+ context::cur_object = &c;
+
+ if (member_scope_.empty ())
+ member_scope_.push_back (class_inheritance_chain ());
+
+ member_scope_.back ().push_back (&c);
+
+ if (build_table_prefix_)
+ {
+ // Don't reset the table prefix if we are traversing a base.
+ //
+ bool tb (false);
+
+ if (table_prefix_.level == 0)
+ {
+ table_prefix_ = table_prefix (c);
+ tb = true;
+ }
+
+ if (k == class_object)
+ traverse_object (c);
+ else
+ traverse_view (c);
+
+ if (tb)
+ table_prefix_ = table_prefix ();
+ }
+ else
+ {
+ if (k == class_object)
+ traverse_object (c);
+ else
+ traverse_view (c);
+ }
+
+ member_scope_.back ().pop_back ();
+
+ if (member_scope_.back ().empty ())
+ member_scope_.pop_back ();
+
+ context::cur_object = prev;
+
+ if (prev == 0)
+ context::top_object = 0;
+}
+
+void object_members_base::
+traverse_member (semantics::data_member& m, semantics::type& t)
+{
+ if (semantics::class_* comp = context::composite_wrapper (t))
+ {
+ member_scope_.push_back (class_inheritance_chain ());
+ member_scope_.back ().push_back (comp);
+
+ table_prefix old_table_prefix;
+ string old_flat_prefix, old_member_prefix;
+
+ if (build_flat_prefix_)
+ {
+ old_flat_prefix = flat_prefix_;
+ flat_prefix_ += public_name (m);
+ flat_prefix_ += '_';
+ }
+
+ if (build_member_prefix_)
+ {
+ old_member_prefix = member_prefix_;
+ member_prefix_ += m.name ();
+ member_prefix_ += '.';
+ }
+
+ if (build_table_prefix_)
+ {
+ old_table_prefix = table_prefix_;
+ table_prefix_.append (m);
+ }
+
+ traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0));
+
+ if (build_table_prefix_)
+ table_prefix_ = old_table_prefix;
+
+ if (build_flat_prefix_)
+ flat_prefix_ = old_flat_prefix;
+
+ if (build_member_prefix_)
+ member_prefix_ = old_member_prefix;
+
+ member_scope_.pop_back ();
+ }
+ else
+ traverse_simple (m);
+}
+
+bool object_members_base::
+section_test (data_member_path const& mp)
+{
+ // By default ignore members from the wrong section.
+ //
+ return section_ == 0 || *section_ == section (mp);
+}
+
+void object_members_base::member::
+traverse (semantics::data_member& m)
+{
+ if (transient (m))
+ return;
+
+ om_.member_path_.push_back (&m);
+
+ // Ignore members from the wrong section.
+ //
+ if (om_.section_test (om_.member_path_))
+ {
+ semantics::type& t (utype (m));
+
+ if (semantics::type* c = context::container (m))
+ om_.traverse_container (m, *c);
+ else if (semantics::class_* c = object_pointer (t))
+ om_.traverse_pointer (m, *c);
+ else
+ om_.traverse_member (m, t);
+ }
+
+ om_.member_path_.pop_back ();
+}
+
+//
+// object_columns_base
+//
+
+void object_columns_base::
+flush ()
+{
+}
+
+bool object_columns_base::
+traverse_column (semantics::data_member&, string const&, bool)
+{
+ return false;
+}
+
+void object_columns_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ if (!view_member (m)) // Not really "as if" pointed-to id column.
+ traverse_member (m, utype (*id_member (c)));
+}
+
+void object_columns_base::
+traverse_points_to (semantics::data_member& m, semantics::class_&)
+{
+ traverse_member (m, utype (m));
+}
+
+void object_columns_base::
+traverse_composite (semantics::data_member*, semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_columns_base::
+traverse_object (semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_columns_base::
+traverse_view (semantics::class_& c)
+{
+ // A view has no bases.
+ //
+ names (c);
+}
+
+void object_columns_base::
+traverse_pre (semantics::nameable&)
+{
+}
+
+void object_columns_base::
+traverse_post (semantics::nameable&)
+{
+}
+
+void object_columns_base::
+traverse (semantics::data_member& m)
+{
+ traverse_pre (m);
+
+ semantics::type& t (utype (m));
+ semantics::class_* c (object_pointer (t));
+ semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));
+
+ root_ = &m;
+
+ // It would seem natural to add m to member_path_ so that we don't
+ // have these two cases. However, for member path to work correctly
+ // with readonly() we also have to have corresponding member_scope,
+ // which is a whole different level of complexity.
+ //
+ root_id_ = member_path_.empty ()
+ ? context::id (m)
+ : context::id (member_path_) != 0;
+ root_op_ = (c != 0);
+ root_null_ = context::null (m);
+
+
+ if (root_op_)
+ traverse_pointer (m, *c);
+ else
+ traverse_member (m, *rt);
+
+ if (!first_ && composite_wrapper (*rt))
+ flush ();
+
+ root_ = 0;
+
+ traverse_post (m);
+}
+
+void object_columns_base::
+traverse (semantics::data_member& m,
+ semantics::type& t,
+ std::string const& kp,
+ std::string const& dn,
+ semantics::class_* to)
+{
+ traverse_pre (m);
+
+ semantics::class_* oto (context::top_object);
+
+ if (to != 0)
+ context::top_object = to;
+
+ semantics::class_* c (object_pointer (t));
+ semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));
+
+ root_ = &m;
+ root_id_ = (kp == "id");
+ root_op_ = (c != 0);
+ root_null_ = context::null (m, kp);
+
+ key_prefix_ = kp;
+ default_name_ = dn;
+
+ if (root_op_)
+ traverse_pointer (m, *c);
+ else
+ traverse_member (m, *rt);
+
+ key_prefix_.clear ();
+ default_name_.clear ();
+
+ if (!first_ && composite_wrapper (*rt))
+ flush ();
+
+ root_ = 0;
+ context::top_object = oto;
+
+ traverse_post (m);
+}
+
+void object_columns_base::
+traverse (semantics::class_& c)
+{
+ class_kind_type k (class_kind (c));
+
+ // Ignore transient bases.
+ //
+ if (k == class_other)
+ return;
+
+ bool f (top_level_);
+
+ if (top_level_)
+ {
+ traverse_pre (c);
+ top_level_ = false;
+ }
+ else
+ {
+ // Unless requested otherwise, don't go into bases if we are a derived
+ // type in a polymorphic hierarchy.
+ //
+ if (!traverse_poly_base_ && polymorphic (c))
+ return;
+ }
+
+ semantics::class_* prev (0);
+ if (k == class_object || k == class_view)
+ {
+ if (context::top_object == 0)
+ context::top_object = &c;
+
+ prev = context::cur_object;
+ context::cur_object = &c;
+ }
+
+ if (member_scope_.empty ())
+ member_scope_.push_back (class_inheritance_chain ());
+
+ member_scope_.back ().push_back (&c);
+
+ if (k == class_object)
+ traverse_object (c);
+ else if (k == class_view)
+ traverse_view (c);
+ else if (k == class_composite)
+ traverse_composite (0, c);
+
+ member_scope_.back ().pop_back ();
+
+ if (member_scope_.back ().empty ())
+ member_scope_.pop_back ();
+
+ if (k == class_object || k == class_view)
+ {
+ context::cur_object = prev;
+
+ if (prev == 0)
+ context::top_object = 0;
+ }
+
+ if (f)
+ {
+ if (!first_)
+ flush ();
+
+ traverse_post (c);
+ }
+}
+
+void object_columns_base::
+traverse_member (semantics::data_member& m, semantics::type& t)
+{
+ if (semantics::class_* comp = composite_wrapper (t))
+ {
+ member_scope_.push_back (class_inheritance_chain ());
+ member_scope_.back ().push_back (comp);
+
+ column_prefix old_prefix (column_prefix_);
+ column_prefix_.append (m, key_prefix_, default_name_);
+
+ // Save and clear the key prefix and default name.
+ //
+ string old_kp, old_dn;
+ old_kp.swap (key_prefix_);
+ old_dn.swap (default_name_);
+
+ traverse_composite (&m, *comp);
+
+ old_kp.swap (key_prefix_);
+ old_dn.swap (default_name_);
+
+ column_prefix_ = old_prefix;
+ member_scope_.pop_back ();
+ }
+ else
+ {
+ string name (column_name (m, key_prefix_, default_name_, column_prefix_));
+
+ if (traverse_column (m, name, first_))
+ {
+ if (first_)
+ first_ = false;
+ }
+ }
+}
+
+bool object_columns_base::
+section_test (data_member_path const& mp)
+{
+ // By default ignore members from the wrong section.
+ //
+ return section_ == 0 || *section_ == section (mp);
+}
+
+void object_columns_base::member::
+traverse (semantics::data_member& m)
+{
+ if (transient (m))
+ return;
+
+ // Container gets its own table, so nothing to do here.
+ //
+ if (container (m))
+ return;
+
+ oc_.member_path_.push_back (&m);
+
+ if (oc_.section_test (oc_.member_path_))
+ {
+ using semantics::class_;
+ semantics::type& t (utype (m));
+
+ if (class_* c = object_pointer (t))
+ oc_.traverse_pointer (m, *c);
+ else if (class_* c = points_to (m))
+ oc_.traverse_points_to (m, *c);
+ else
+ oc_.traverse_member (m, t);
+ }
+
+ oc_.member_path_.pop_back ();
+}
+
+//
+// object_columns_list
+//
+
+void object_columns_list::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore inverse object pointers.
+ //
+ if (!ignore_inverse_ || !inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+}
+
+bool object_columns_list::
+traverse_column (semantics::data_member& m, std::string const& name, bool)
+{
+ columns_.push_back (column (name, column_type (), m));
+ return true;
+}
+
+//
+// typedefs
+//
+
+void typedefs::
+traverse (semantics::typedefs& t)
+{
+ if (check (t))
+ traversal::typedefs::traverse (t);
+}
+
+bool typedefs::
+check (semantics::typedefs& t)
+{
+ // This typedef must be for a class template instantiation.
+ //
+ using semantics::class_instantiation;
+ class_instantiation* ci (dynamic_cast<class_instantiation*> (&t.type ()));
+
+ if (ci == 0)
+ return false;
+
+ // It must be an object, view, or composite value.
+ //
+ if (class_kind (*ci) == class_other)
+ return false;
+
+ // This typedef name should be the one that was used in the pragma.
+ //
+ using semantics::names;
+ tree type (ci->get<tree> ("tree-node"));
+
+ names* hint;
+ if (ci->count ("tree-hint"))
+ hint = ci->get<names*> ("tree-hint");
+ else
+ {
+ hint = unit.find_hint (type);
+ ci->set ("tree-hint", hint); // Cache it.
+ }
+
+ if (hint != &t)
+ return false;
+
+ // And the definition may have to be in the file we are compiling.
+ //
+ if (!included_)
+ {
+ if (!options.at_once () && class_file (*ci) != unit.file ())
+ return false;
+ }
+
+ return true;
+}
diff --git a/odb/odb/common.hxx b/odb/odb/common.hxx
new file mode 100644
index 0000000..149def7
--- /dev/null
+++ b/odb/odb/common.hxx
@@ -0,0 +1,468 @@
+// file : odb/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_COMMON_HXX
+#define ODB_COMMON_HXX
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/context.hxx>
+#include <odb/instance.hxx>
+
+// Traverse object members recursively by going into bases and
+// composite members.
+//
+struct object_members_base: traversal::class_, virtual context
+{
+ virtual void
+ traverse_simple (semantics::data_member&);
+
+ // Traverse object pointer. The second argument is the pointed-to
+ // class. When overriding this function, you will most likely want
+ // to call the base in order to traverse the pointer as a simple
+ // member (simple object id) or as composite (composite object id).
+ //
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members. The first argument is the data member and can
+ // be NULL if we are traversing the root type or a base. The second
+ // argument is the actual composite type, which is not necessarily
+ // the same as the member's type.
+ //
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ // More general version of the above function that allows detection
+ // of wrapped composite value. By default this function calls
+ // traverse_composite (m, comp) ignoring the wrapper type. Note that
+ // this function is called for all composite value (wrapped or not).
+ // If it is not wrapped, the wrapper argument will be NULL.
+ //
+ virtual void
+ traverse_composite_wrapper (semantics::data_member*,
+ semantics::class_& comp,
+ semantics::type* wrapper);
+
+ // The second argument is the actual container type in case the
+ // member type is a wrapper.
+ //
+ virtual void
+ traverse_container (semantics::data_member&, semantics::type&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members.
+ //
+ virtual void
+ traverse_object (semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // members.
+ //
+ virtual void
+ traverse_view (semantics::class_&);
+
+ virtual bool
+ section_test (data_member_path const&);
+
+public:
+ object_members_base (bool traverse_poly_base = false,
+ object_section* section = 0)
+ : section_ (section), top_level_ (true), member_ (*this)
+ {
+ init (false, false, false, traverse_poly_base);
+ }
+
+ object_members_base (bool build_flat_prefix,
+ bool build_table_prefix,
+ bool build_member_prefix,
+ bool traverse_poly_base = false,
+ object_section* section = 0)
+ : section_ (section), top_level_ (true), member_ (*this)
+ {
+ init (build_flat_prefix,
+ build_table_prefix,
+ build_member_prefix,
+ traverse_poly_base);
+ }
+
+ object_members_base (object_members_base const& x)
+ : context (), //@@ -Wextra
+ section_ (x.section_),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init (x.build_flat_prefix_,
+ x.build_table_prefix_,
+ x.build_member_prefix_,
+ x.traverse_poly_base_);
+ }
+
+ virtual void
+ traverse (semantics::class_&);
+
+protected:
+ string flat_prefix_;
+ table_prefix table_prefix_;
+ string member_prefix_;
+
+ data_member_path member_path_;
+ data_member_scope member_scope_;
+
+ object_section* section_;
+
+protected:
+ semantics::data_member*
+ id () const
+ {
+ assert (!member_path_.empty ());
+ return context::id (member_path_);
+ }
+
+private:
+ void
+ init (bool build_flat_prefix,
+ bool build_table_prefix,
+ bool build_member_prefix,
+ bool traverse_poly_base)
+ {
+ build_flat_prefix_ = build_flat_prefix;
+ build_table_prefix_ = build_table_prefix;
+ build_member_prefix_ = build_member_prefix;
+ traverse_poly_base_ = traverse_poly_base;
+
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+private:
+ virtual void
+ traverse_member (semantics::data_member&, semantics::type&);
+
+ struct member: traversal::data_member
+ {
+ member (object_members_base& om): om_ (om) {}
+
+ virtual void
+ traverse (semantics::data_member&);
+
+ public:
+ object_members_base& om_;
+ };
+
+ bool build_flat_prefix_;
+ bool build_table_prefix_;
+ bool build_member_prefix_;
+
+ bool traverse_poly_base_;
+
+ bool top_level_;
+
+ member member_;
+ traversal::names names_;
+ traversal::inherits inherits_;
+};
+
+// Traverse object columns recursively by going into composite members
+// and bases.
+//
+struct object_columns_base: traversal::class_, virtual context
+{
+ // Returning false means that the column has been ignored and the
+ // first flag should not be changed.
+ //
+ virtual bool
+ traverse_column (semantics::data_member&,
+ string const& name,
+ bool first);
+
+ // Traverse object pointer. The second argument is the pointed-to
+ // class. When overriding this function, you will most likely want
+ // to call the base in order to traverse the member as a column
+ // (simple object id) or columns (composite object id).
+ //
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ traverse_points_to (semantics::data_member&, semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members. The first argument is the data member and can
+ // be NULL if we are traversing the root type or a base. The second
+ // argument is the actual composite type, which is not necessarily
+ // the same as the member type.
+ //
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members.
+ //
+ virtual void
+ traverse_object (semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // members.
+ //
+ virtual void
+ traverse_view (semantics::class_&);
+
+ // Called after the last column, provided at least one column hasn't
+ // been ignored.
+ //
+ virtual void
+ flush ();
+
+ virtual bool
+ section_test (data_member_path const&);
+
+ // Start/end traversal callbacks.
+ //
+ virtual void
+ traverse_pre (semantics::nameable&);
+
+ virtual void
+ traverse_post (semantics::nameable&);
+
+public:
+ object_columns_base (bool first = true,
+ column_prefix const& cp = column_prefix (),
+ object_section* section = 0)
+ : column_prefix_ (cp),
+ section_ (section),
+ root_ (0),
+ traverse_poly_base_ (false),
+ first_ (first),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init ();
+ }
+
+ object_columns_base (bool first,
+ bool traverse_poly_base,
+ object_section* section = 0)
+ : section_ (section),
+ root_ (0),
+ traverse_poly_base_ (traverse_poly_base),
+ first_ (first),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init ();
+ }
+
+ object_columns_base (object_columns_base const& x)
+ : context (), //@@ -Wextra
+ column_prefix_ (x.column_prefix_),
+ section_ (x.section_),
+ root_ (0),
+ traverse_poly_base_ (x.traverse_poly_base_),
+ first_ (x.first_),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init ();
+ }
+
+ virtual void
+ traverse (semantics::class_&);
+
+ // Traverse a data member with type, which can be a simple or composite
+ // value type, or an object pointer (with a simple or composite id).
+ //
+ virtual void
+ traverse (semantics::data_member&);
+
+ virtual void
+ traverse (semantics::data_member& m, column_prefix const& cp)
+ {
+ column_prefix op (column_prefix_);
+ column_prefix_ = cp;
+ traverse (m);
+ column_prefix_ = op;
+ }
+
+ virtual void
+ traverse (data_member_path& mp)
+ {
+ data_member_path op (member_path_);
+ member_path_ = mp;
+ traverse (*mp.back (), column_prefix (mp));
+ member_path_ = op;
+ }
+
+ // Should only be used for containers.
+ //
+ virtual void
+ traverse (semantics::data_member&,
+ semantics::type&,
+ string const& key_prefix,
+ string const& default_name,
+ semantics::class_* top_object = 0); // If not 0, switch top object.
+
+protected:
+ string key_prefix_;
+ string default_name_;
+
+ column_prefix column_prefix_;
+
+ data_member_path member_path_;
+ data_member_scope member_scope_;
+
+ object_section* section_;
+
+protected:
+ semantics::data_member*
+ id () const
+ {
+ if (root_ != 0)
+ return root_id_ ? root_ : 0; // Cannot have ids below root.
+ else
+ {
+ assert (!member_path_.empty ());
+ return context::id (member_path_);
+ }
+ }
+
+ string
+ column_type ()
+ {
+ if (member_path_.empty ())
+ {
+ assert (root_ != 0);
+ return context::column_type (*root_, key_prefix_);
+ }
+ else
+ return context::column_type (
+ member_path_, key_prefix_, root_ != 0 && (root_id_ || root_op_));
+ }
+
+ bool
+ null () const
+ {
+ return (root_ != 0 && root_null_) || context::null (member_path_);
+ }
+
+private:
+ semantics::data_member* root_; // Root member if traversing from a member.
+ bool root_id_; // True if traversing root as object id.
+ bool root_op_; // True if traversing root as object pointer.
+ bool root_null_; // True if root is null-able.
+
+private:
+ void
+ init ()
+ {
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+private:
+ virtual void
+ traverse_member (semantics::data_member&, semantics::type&);
+
+ struct member: traversal::data_member, context
+ {
+ member (object_columns_base& oc): oc_ (oc) {}
+
+ virtual void
+ traverse (semantics::data_member&);
+
+ public:
+ object_columns_base& oc_;
+ };
+
+ bool traverse_poly_base_;
+
+ bool first_;
+ bool top_level_;
+
+ member member_;
+ traversal::names names_;
+ traversal::inherits inherits_;
+};
+
+struct object_columns_list: object_columns_base
+{
+ typedef object_columns_list base;
+
+ object_columns_list (bool ignore_inverse = true)
+ : ignore_inverse_ (ignore_inverse)
+ {
+ }
+
+ object_columns_list (column_prefix const& cp, bool ignore_inverse = true)
+ : object_columns_base (true, cp), ignore_inverse_ (ignore_inverse)
+ {
+ }
+
+ struct column
+ {
+ column (std::string const& n,
+ std::string const& t,
+ semantics::data_member& m)
+ : name (n), type (t), member (&m)
+ {
+ }
+
+ std::string name;
+ std::string type;
+ semantics::data_member* member;
+ };
+
+ typedef std::vector<column> columns;
+ typedef columns::const_iterator iterator;
+
+ iterator
+ begin () const {return columns_.begin ();}
+
+ iterator
+ end () const {return columns_.end ();}
+
+ columns::size_type
+ size () const {return columns_.size ();}
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual bool
+ traverse_column (semantics::data_member&, string const&, bool);
+
+private:
+ bool ignore_inverse_;
+ columns columns_;
+};
+
+// Traverse objects, views, and composite values that are class template
+// instantiations.
+//
+struct typedefs: traversal::typedefs, context
+{
+ typedefs (bool traverse_included)
+ : included_ (traverse_included)
+ {
+ }
+
+ virtual void
+ traverse (semantics::typedefs&);
+
+ // Returns true if we should traverse this typedef.
+ //
+ bool
+ check (semantics::typedefs&);
+
+private:
+ bool included_;
+};
+
+// Other common parts.
+//
+#include <odb/common-query.hxx>
+
+#endif // ODB_COMMON_HXX
diff --git a/odb/odb/context.cxx b/odb/odb/context.cxx
new file mode 100644
index 0000000..13fc1b3
--- /dev/null
+++ b/odb/odb/context.cxx
@@ -0,0 +1,3384 @@
+// file : odb/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <cctype> // std::toupper
+#include <cassert>
+#include <sstream>
+
+#include <odb/context.hxx>
+#include <odb/common.hxx>
+#include <odb/pragma.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/mssql/context.hxx>
+#include <odb/relational/mysql/context.hxx>
+#include <odb/relational/oracle/context.hxx>
+#include <odb/relational/pgsql/context.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+static inline void
+add_space (string& s)
+{
+ string::size_type n (s.size ());
+ if (n != 0 && s[n - 1] != ' ')
+ s += ' ';
+}
+
+//
+// custom_cxx_type
+//
+string custom_cxx_type::
+translate (string const& val, const cxx_tokens& expr)
+{
+ // Similar to member_access::translate() and a few other places.
+ //
+ string r;
+
+ cxx_tokens_lexer l;
+ l.start (expr);
+
+ string tl;
+ for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_NOT:
+ {
+ add_space (r);
+ r += '!';
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += context::strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ // Add space except for a few common cases.
+ //
+ if (ptt != CPP_NAME &&
+ ptt != CPP_OPEN_PAREN &&
+ ptt != CPP_OPEN_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_QUERY:
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl);
+
+ if (tt == CPP_CLOSE_PAREN)
+ r += val;
+ else
+ {
+ add_space (r);
+ r += "? ";
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl);
+ }
+
+ return r;
+}
+
+
+//
+// view_object
+//
+
+string view_object::
+name () const
+{
+ if (!alias.empty ())
+ return alias;
+
+ return kind == object ? context::class_name (*obj) : tbl_name.string ();
+}
+
+//
+// member_access
+//
+
+bool member_access::
+placeholder () const
+{
+ for (cxx_tokens::const_iterator i (expr.begin ()), e (expr.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ return true;
+ }
+ }
+ else
+ ++i;
+ }
+
+ return false;
+}
+
+string member_access::
+translate (string const& obj, string const& val, string const& db) const
+{
+ if (empty ())
+ {
+ error (loc) << "non-empty " << kind << " expression required" << endl;
+ throw operation_failed ();
+ }
+
+ // This code is similar to translate_expression() from relations/source.cxx.
+ //
+ string r;
+
+ cxx_tokens_lexer l;
+ l.start (expr);
+
+ string tl;
+ for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += context::strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ // Add space except for a few common cases.
+ //
+ if (ptt != CPP_NAME &&
+ ptt != CPP_OPEN_PAREN &&
+ ptt != CPP_OPEN_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_NOT:
+ case CPP_QUERY:
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl);
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ if (ptt == CPP_NOT)
+ {
+ if (db.empty ())
+ {
+ error (loc) << "database instance (!) not available in this "
+ << "context" << endl;
+ throw operation_failed ();
+ }
+
+ r += db;
+ }
+ else
+ r += val;
+ }
+ else
+ {
+ add_space (r);
+ r += (ptt == CPP_NOT ? "!" : "? ");
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ // Translate 'this'.
+ //
+ r += (tl == "this" ? obj : tl);
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl);
+ }
+
+ return r;
+}
+
+// Sections.
+//
+main_section_type main_section;
+
+bool main_section_type::
+compare (object_section const& s) const
+{
+ main_section_type const* ms (dynamic_cast<main_section_type const*> (&s));
+ return ms != 0 && *this == *ms;
+}
+
+bool user_section::
+compare (object_section const& s) const
+{
+ user_section const* us (dynamic_cast<user_section const*> (&s));
+ return us != 0 && *this == *us;
+}
+
+user_section* user_section::
+total_base () const
+{
+ if (base != 0)
+ {
+ semantics::class_* poly_root (context::polymorphic (*object));
+ if (poly_root != 0 && poly_root != *object)
+ return base;
+ }
+
+ return 0;
+}
+
+size_t user_sections::
+count (unsigned short f) const
+{
+ size_t r (0);
+
+ semantics::class_* poly_root (context::polymorphic (*object));
+ bool poly_derived (poly_root != 0 && poly_root != object);
+
+ if (poly_derived && (f & count_total) != 0)
+ r = context::polymorphic_base (*object).get<user_sections> (
+ "user-sections").count (f);
+
+ for (const_iterator i (begin ()); i != end (); ++i)
+ {
+ // Skip special sections unless we were explicitly asked to count them.
+ //
+ if (i->special == user_section::special_version &&
+ (f & count_special_version) == 0)
+ continue;
+
+ // Skip non-versioned sections if we are only interested in the
+ // versioned ones.
+ //
+ if ((f & count_versioned_only) != 0 &&
+ !context::added (*i->member) && !context::deleted (*i->member))
+ continue;
+
+ bool ovd (i->base != 0 && poly_derived);
+
+ if (i->load != user_section::load_eager)
+ {
+ if (i->load_empty ())
+ {
+ if ((f & count_load_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue; // Count each section only once.
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_load) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ if (i->update_empty ())
+ {
+ if ((f & count_update_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_update) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (i->optimistic ())
+ {
+ if ((f & count_optimistic) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ return r;
+}
+
+//
+// context
+//
+
+namespace
+{
+ char const* keywords[] =
+ {
+ "NULL",
+ "and",
+ "asm",
+ "auto",
+ "bitand",
+ "bitor",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "compl",
+ "const",
+ "const_cast",
+ "continue",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "dynamic_cast",
+ "else",
+ "end_eq",
+ "enum",
+ "explicit",
+ "export",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "inline",
+ "int",
+ "long",
+ "mutable",
+ "namespace",
+ "new",
+ "not",
+ "not_eq",
+ "operator",
+ "or",
+ "or_eq",
+ "private",
+ "protected",
+ "public",
+ "register",
+ "reinterpret_cast",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "static_cast",
+ "struct",
+ "switch",
+ "template",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typeid",
+ "typename",
+ "union",
+ "unsigned",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "wchar_t",
+ "while",
+ "xor",
+ "xor_eq"
+ };
+}
+
+unique_ptr<context>
+create_context (ostream& os,
+ semantics::unit& unit,
+ options const& ops,
+ features& f,
+ semantics::relational::model* m)
+{
+ unique_ptr<context> r;
+
+ switch (ops.database ()[0])
+ {
+ case database::common:
+ {
+ r.reset (new context (os, unit, ops, f));
+ break;
+ }
+ case database::mssql:
+ {
+ r.reset (new relational::mssql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::mysql:
+ {
+ r.reset (new relational::mysql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::oracle:
+ {
+ r.reset (new relational::oracle::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::pgsql:
+ {
+ r.reset (new relational::pgsql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::sqlite:
+ {
+ r.reset (new relational::sqlite::context (os, unit, ops, f, m));
+ break;
+ }
+ }
+
+ return r;
+}
+
+context::
+~context ()
+{
+ if (current_ == this)
+ current_ = 0;
+}
+
+context::
+context (ostream& os_,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ data_ptr d)
+ : data_ (d ? d : data_ptr (new (shared) data (os_))),
+ extra (data_->extra_),
+ os (data_->os_),
+ unit (u),
+ options (ops),
+ features (f),
+ db (options.database ()[0]),
+ in_comment (data_->in_comment_),
+ exp (data_->exp_),
+ ext (data_->ext_),
+ keyword_set (data_->keyword_set_),
+ include_regex (data_->include_regex_),
+ accessor_regex (data_->accessor_regex_),
+ modifier_regex (data_->modifier_regex_),
+ embedded_schema (
+ ops.generate_schema () &&
+ ops.schema_format ()[db].count (schema_format::embedded)),
+ separate_schema (
+ ops.generate_schema () &&
+ ops.schema_format ()[db].count (schema_format::separate)),
+ multi_static (ops.multi_database () == multi_database::static_),
+ multi_dynamic (ops.multi_database () == multi_database::dynamic),
+ force_versioned (false),
+ top_object (data_->top_object_),
+ cur_object (data_->cur_object_)
+{
+ assert (current_ == 0);
+ current_ = this;
+
+ // Write boolean values as true/false.
+ //
+ os.setf (ios_base::boolalpha);
+
+ // Export control.
+ //
+ if (!ops.export_symbol ()[db].empty ())
+ exp = ops.export_symbol ()[db] + " ";
+
+ ext = ops.extern_symbol ()[db];
+
+ for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i)
+ data_->keyword_set_.insert (keywords[i]);
+
+ // SQL name regex.
+ //
+ if (ops.table_regex ().count (db) != 0)
+ {
+ strings const& s (ops.table_regex ()[db]);
+ data_->sql_name_regex_[sql_name_table].assign (s.begin (), s.end ());
+ }
+
+ if (ops.column_regex ().count (db) != 0)
+ {
+ strings const& s (ops.column_regex ()[db]);
+ data_->sql_name_regex_[sql_name_column].assign (s.begin (), s.end ());
+ }
+
+ if (ops.index_regex ().count (db) != 0)
+ {
+ strings const& s (ops.index_regex ()[db]);
+ data_->sql_name_regex_[sql_name_index].assign (s.begin (), s.end ());
+ }
+
+ if (ops.fkey_regex ().count (db) != 0)
+ {
+ strings const& s (ops.fkey_regex ()[db]);
+ data_->sql_name_regex_[sql_name_fkey].assign (s.begin (), s.end ());
+ }
+
+ if (ops.sequence_regex ().count (db) != 0)
+ {
+ strings const& s (ops.sequence_regex ()[db]);
+ data_->sql_name_regex_[sql_name_sequence].assign (s.begin (), s.end ());
+ }
+
+ if (ops.statement_regex ().count (db) != 0)
+ {
+ strings const& s (ops.statement_regex ()[db]);
+ data_->sql_name_regex_[sql_name_statement].assign (s.begin (), s.end ());
+ }
+
+ if (ops.sql_name_regex ().count (db) != 0)
+ {
+ strings const& s (ops.sql_name_regex ()[db]);
+ data_->sql_name_regex_[sql_name_all].assign (s.begin (), s.end ());
+ }
+
+ // Include regex.
+ //
+ for (strings::const_iterator i (ops.include_regex ().begin ());
+ i != ops.include_regex ().end (); ++i)
+ data_->include_regex_.push_back (regexsub (*i));
+
+ // Common accessor/modifier naming variants. Try the user-supplied and
+ // more specific ones first.
+ //
+ for (strings::const_iterator i (ops.accessor_regex ().begin ());
+ i != ops.accessor_regex ().end (); ++i)
+ data_->accessor_regex_.push_back (regexsub (*i));
+
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get_$1/")); // get_foo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get\\u$1/")); // getFoo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get$1/")); // getfoo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
+
+ for (strings::const_iterator i (ops.modifier_regex ().begin ());
+ i != ops.modifier_regex ().end (); ++i)
+ data_->modifier_regex_.push_back (regexsub (*i));
+
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set_$1/")); // set_foo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set\\u$1/")); // setFoo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set$1/")); // setfoo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
+}
+
+context::
+context ()
+ : data_ (current ().data_),
+ extra (current ().extra),
+ os (current ().os),
+ unit (current ().unit),
+ options (current ().options),
+ features (current ().features),
+ db (current ().db),
+ in_comment (current ().in_comment),
+ exp (current ().exp),
+ ext (current ().ext),
+ keyword_set (current ().keyword_set),
+ include_regex (current ().include_regex),
+ accessor_regex (current ().accessor_regex),
+ modifier_regex (current ().modifier_regex),
+ embedded_schema (current ().embedded_schema),
+ separate_schema (current ().separate_schema),
+ multi_static (current ().multi_static),
+ multi_dynamic (current ().multi_dynamic),
+ force_versioned (current ().force_versioned),
+ top_object (current ().top_object),
+ cur_object (current ().cur_object)
+{
+}
+
+context* context::current_;
+
+semantics::data_member* context::
+id (data_member_path const& mp)
+{
+ semantics::data_member* idf (mp.front ());
+
+ if (!id (*idf))
+ return 0;
+
+ // This is for special ids, such as polymorphic-ref, which
+ // don't have "id-member" set (and we want to keep it that
+ // way since it is not really a full-fledged id).
+ //
+ if (idf->get<string> ("id").empty ()) // Not a nested id.
+ return idf;
+
+ const data_member_path& id (
+ *id_member (
+ dynamic_cast<semantics::class_&> (idf->scope ())));
+
+ // Now we need to make sure id is a prefix of mp;
+ //
+ return mp.sub (id) ? idf : 0;
+}
+
+semantics::data_member* context::
+object_pointer (data_member_path const& mp)
+{
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ if (object_pointer (utype (**i)))
+ return *i;
+ }
+
+ return 0;
+}
+
+bool context::
+readonly (data_member_path const& mp, data_member_scope const& ms)
+{
+ assert (mp.size () == ms.size ());
+
+ data_member_scope::const_reverse_iterator si (ms.rbegin ());
+
+ for (data_member_path::const_reverse_iterator pi (mp.rbegin ());
+ pi != mp.rend ();
+ ++pi, ++si)
+ {
+ semantics::data_member& m (**pi);
+
+ if (m.count ("readonly"))
+ return true;
+
+ // Check if any of the classes in the inheritance chain for the
+ // class containing this member are readonly.
+ //
+ class_inheritance_chain const& ic (*si);
+
+ assert (ic.back () == &m.scope ());
+
+ for (class_inheritance_chain::const_reverse_iterator ci (ic.rbegin ());
+ ci != ic.rend ();
+ ++ci)
+ {
+ semantics::class_& c (**ci);
+
+ if (c.count ("readonly"))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool context::
+readonly (semantics::data_member& m)
+{
+ if (m.count ("readonly"))
+ return true;
+
+ // Check if the whole class (object or composite value) is marked
+ // as readonly.
+ //
+ if (m.scope ().count ("readonly"))
+ return true;
+
+ return false;
+}
+
+bool context::
+null (data_member_path const& mp) const
+{
+ // Outer members can override the null-ability of the inner ones. So
+ // start from the most outer member.
+ //
+ for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i)
+ {
+ if (null (**i))
+ return true;
+ }
+
+ return false;
+}
+
+bool context::
+null (semantics::data_member& m) const
+{
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (object_pointer (t))
+ {
+ // By default pointers can be null.
+ //
+ if (m.count ("null"))
+ return true;
+
+ if (!m.count ("not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ // Everything else by default is not null.
+ //
+ if (m.count ("null"))
+ return true;
+
+ if (!m.count ("not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ semantics::type* pt;
+
+ // Check if this type is a wrapper.
+ //
+ if (t.get<bool> ("wrapper"))
+ {
+ // First see if it is null by default.
+ //
+ if (t.get<bool> ("wrapper-null-handler") &&
+ t.get<bool> ("wrapper-null-default"))
+ return true;
+
+ // Otherwise, check the wrapped type.
+ //
+ pt = t.get<semantics::type*> ("wrapper-type");
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ pt = &utype (*pt, hint);
+
+ if (pt->count ("null"))
+ return true;
+
+ if (pt->count ("not-null"))
+ return false;
+ }
+ else
+ pt = &t;
+ }
+ }
+
+ return false;
+ }
+}
+
+bool context::
+null (semantics::data_member& m, string const& kp) const
+{
+ if (kp.empty ())
+ return null (m);
+
+ semantics::type& c (utype (m));
+ semantics::type& t (utype (m, kp));
+
+ if (object_pointer (t))
+ {
+ if (m.count (kp + "-null"))
+ return true;
+
+ if (!m.count (kp + "-not-null"))
+ {
+ if (c.count (kp + "-null"))
+ return true;
+
+ if (!c.count (kp + "-not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ else
+ {
+ if (m.count (kp + "-null"))
+ return true;
+
+ if (!m.count (kp + "-not-null"))
+ {
+ if (c.count (kp + "-null"))
+ return true;
+
+ if (!c.count (kp + "-not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ semantics::type* pt;
+
+ // Check if this type is a wrapper.
+ //
+ if (t.get<bool> ("wrapper"))
+ {
+ // First see if it is null by default.
+ //
+ if (t.get<bool> ("wrapper-null-handler") &&
+ t.get<bool> ("wrapper-null-default"))
+ return true;
+
+ // Otherwise, check the wrapped type.
+ //
+ pt = t.get<semantics::type*> ("wrapper-type");
+ pt = &utype (*pt);
+
+ if (pt->count ("null"))
+ return true;
+
+ if (pt->count ("not-null"))
+ return false;
+ }
+ else
+ pt = &t;
+ }
+ }
+ }
+
+ return false;
+ }
+}
+
+size_t context::
+polymorphic_depth (semantics::class_& c)
+{
+ if (c.count ("polymorphic-depth"))
+ return c.get<size_t> ("polymorphic-depth");
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ using semantics::class_;
+
+ class_* root (polymorphic (c));
+ assert (root != 0);
+
+ size_t r (1); // One for the root.
+
+ for (class_* b (&c); b != root; b = &polymorphic_base (*b))
+ ++r;
+
+ c.set ("polymorphic-depth", r);
+ return r;
+}
+
+context::class_kind_type context::
+class_kind (semantics::class_& c)
+{
+ if (object (c))
+ return class_object;
+ else if (view (c))
+ return class_view;
+ else if (composite (c))
+ return class_composite;
+ else
+ return class_other;
+}
+
+string context::
+class_name (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<semantics::names*> ("tree-hint")->name ()
+ : c.name ();
+}
+
+string context::
+class_fq_name (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.fq_name (c.get<semantics::names*> ("tree-hint"))
+ : c.fq_name ();
+}
+
+semantics::scope& context::
+class_scope (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<semantics::names*> ("tree-hint")->scope ()
+ : c.scope ();
+}
+
+semantics::path context::
+class_file (semantics::class_& c)
+{
+ // If we have an explicit definition location, use that.
+ //
+ if (c.count ("definition"))
+ return semantics::path (LOCATION_FILE (c.get<location_t> ("definition")));
+ //
+ // Otherwise, if it is a template instantiation, use the location
+ // of the qualifier.
+ //
+ else if (c.is_a<semantics::class_instantiation> ())
+ return semantics::path (LOCATION_FILE (c.get<location_t> ("location")));
+ else
+ return c.file ();
+}
+
+location_t context::
+class_location (semantics::class_& c)
+{
+ return c.count ("definition")
+ ? c.get<location_t> ("definition")
+ : class_real_location (c);
+}
+
+location_t context::
+class_real_location (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<location_t> ("location")
+ : real_source_location (TYPE_NAME (c.tree_node ()));
+}
+
+string context::
+upcase (string const& s)
+{
+ string r;
+ string::size_type n (s.size ());
+
+ r.reserve (n);
+
+ for (string::size_type i (0); i < n; ++i)
+ r.push_back (toupper (s[i]));
+
+ return r;
+}
+
+void context::
+diverge (streambuf* sb)
+{
+ data_->os_stack_.push (data_->os_.rdbuf ());
+ data_->os_.rdbuf (sb);
+}
+
+void context::
+restore ()
+{
+ data_->os_.rdbuf (data_->os_stack_.top ());
+ data_->os_stack_.pop ();
+}
+
+semantics::type& context::
+utype (semantics::type& t)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ return q->base_type ();
+ else
+ return t;
+}
+
+semantics::type& context::
+utype (semantics::type& t, semantics::names*& hint)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ {
+ hint = q->qualifies ().hint ();
+ return q->base_type ();
+ }
+ else
+ return t;
+}
+
+semantics::type& context::
+utype (semantics::data_member& m,
+ semantics::names*& hint,
+ string const& kp,
+ const custom_cxx_type** translation)
+{
+ semantics::type* t (0);
+
+ if (kp.empty ())
+ {
+ t = &m.type ();
+
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (t))
+ {
+ hint = q->qualifies ().hint ();
+ t = &q->base_type ();
+ }
+ else
+ hint = m.belongs ().hint ();
+ }
+ else
+ {
+ if (m.count (kp + "-tree-type"))
+ t = indirect_type (m, kp, hint);
+ else
+ {
+ t = &utype (m);
+
+ // "See through" wrappers.
+ //
+ if (semantics::type* wt = wrapper (*t))
+ t = indirect_type (utype (*wt), kp, hint);
+ else
+ t = indirect_type (*t, kp, hint);
+ }
+ }
+
+ // Do we need to map this type?
+ //
+ // @@ Need to cache the result on the member.
+ //
+ if (translation != 0)
+ *translation = 0;
+
+ for (semantics::scope* s (&m.scope ());; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ if (namespace_* ns = dynamic_cast<namespace_*> (s))
+ {
+ if (ns->extension ())
+ s = &ns->original ();
+ }
+
+ if (s->count ("custom-cxx-type-map"))
+ {
+ typedef custom_cxx_type_map map;
+
+ map& m (s->get<map> ("custom-cxx-type-map"));
+ map::const_iterator i (m.find (t));
+
+ if (i != m.end ())
+ {
+ hint = i->second->as_hint;
+ t = i->second->as;
+
+ if (translation != 0)
+ *translation = i->second;
+
+ // Currently we only support one level of mapping, but I am
+ // sure someone will want multiple levels.
+ //
+ break;
+ }
+ }
+
+ if (!s->named_p () || s->global_scope ())
+ break;
+ }
+
+ return *t;
+}
+
+bool context::
+const_type (semantics::type& t)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ return q->const_ ();
+
+ return false;
+}
+
+string context::
+type_ref_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var,
+ bool decay)
+{
+ using semantics::array;
+ string r;
+
+ // Note that trailing const syntax is used for a reason (consider
+ // t == const foo*). We may also have to decay then top-level array.
+ //
+ array* a;
+ if (decay && (a = dynamic_cast<array*> (&utype (t))) != 0)
+ {
+ semantics::type& bt (a->base_type ());
+ hint = a->contains ().hint ();
+
+ if (bt.is_a<array> ())
+ {
+ // If we need to add/strip const or no name was used in the
+ // declaration, then create an array declaration (e.g., for
+ // char x[2][3] we will have char const (*x)[3]).
+ //
+ if (mc != const_type (t) || hint == 0)
+ return type_val_type (bt, 0, mc, "(*" + var + ")");
+ }
+
+ // Array base type is always cvr-unqualified.
+ //
+ if (mc)
+ r = bt.fq_name (hint) + " const";
+ else
+ r = bt.fq_name (hint);
+
+ r += '*';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ r += '&';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
+string context::
+type_val_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var)
+{
+ using semantics::array;
+ string r;
+
+ // Arrays are a complicated case. Firstly, we may need to add/strip const
+ // to/from the base type. Secondly, the array dimensions are written after
+ // the variable name. All this is further complicated by multiple dimensions.
+ // Thanks, Dennis!
+ //
+ if (array* a = dynamic_cast<array*> (&utype (t)))
+ {
+ semantics::type& bt (a->base_type ());
+
+ // If we don't need to add/strip const and a name was used in the
+ // declaration, then use that name.
+ //
+ if (mc == const_type (t) && hint != 0)
+ {
+ r = t.fq_name (hint);
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ // Otherwise, construct the array declaration.
+ //
+ string v (var);
+ v += '[';
+ ostringstream ostr;
+ ostr << a->size ();
+ v += ostr.str ();
+
+ if (a->size () > 0xFFFFFFFF)
+ v += "ULL";
+ else if (a->size () > 2147483647)
+ v += "U";
+
+ v += ']';
+
+ r = type_val_type (bt, a->contains ().hint (), mc, v);
+ }
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
+void context::
+set_member (semantics::data_member& m,
+ const string& obj,
+ const string& val,
+ const string& db,
+ const string& type)
+{
+ member_access& ma (m.get<member_access> ("set"));
+
+ // If this is a custom expression, output the location of where
+ // it came from.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (ma.placeholder ())
+ {
+ // Cast the database to the concrete type this code is generated
+ // for. This way the user is free to use either the concrete or
+ // the common.
+ //
+ string d;
+ if (!db.empty ())
+ d = "static_cast<" + context::db.string () + "::database&> (" + db + ")";
+
+ os << ma.translate (obj, val, d) << ";";
+ }
+ else
+ {
+ // If this member is const and we have a synthesized direct access,
+ // then cast away constness. Otherwise, we assume that the user-
+ // provided expression handles this.
+ //
+ bool cast (!type.empty () && ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << type << "& > (" << endl;
+
+ os << ma.translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << " = " << val << ";";
+ }
+}
+
+void context::
+inc_member (semantics::data_member& m,
+ const string& obj,
+ const string& gobj,
+ const string& type)
+{
+ member_access& ma (m.get<member_access> ("set"));
+
+ // If this is a custom expression, output the location of where
+ // it came from.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (ma.placeholder ())
+ {
+ member_access& gma (m.get<member_access> ("get"));
+
+ if (!gma.synthesized)
+ os << "// From " << location_string (gma.loc, true) << endl;
+
+ os << ma.translate (obj, gma.translate (gobj) + " + 1") << ";";
+ }
+ else
+ {
+ // If this member is const and we have a synthesized direct access,
+ // then cast away constness. Otherwise, we assume that the user-
+ // provided expression handles this.
+ //
+ os << "++";
+
+ bool cast (ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << type << "& > (" << endl;
+
+ os << ma.translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << ";";
+ }
+}
+
+void context::
+resolve_data_members (data_member_path& r,
+ semantics::class_& c,
+ const string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+{
+ using semantics::class_;
+ using semantics::data_member;
+
+ // The name was already verified to be syntactically correct so
+ // we don't need to do any extra error checking in this area.
+ //
+ lex.start (name);
+
+ try
+ {
+ string tl;
+ cpp_ttype tt (lex.next (tl));
+
+ data_member& m (c.lookup<data_member> (tl, class_::include_hidden));
+
+ r.push_back (&m);
+
+ if (container (m))
+ return;
+
+ // Resolve nested members if any.
+ //
+ for (tt = lex.next (tl); tt == CPP_DOT; tt = lex.next (tl))
+ {
+ lex.next (tl); // Get CPP_NAME.
+
+ data_member& om (*r.back ());
+
+ // Check that the outer member is composite and also unwrap it while
+ // at it.
+ //
+ class_* comp (composite_wrapper (utype (om)));
+ if (comp == 0)
+ {
+ error (l) << "data member '" << om.name () << "' is not composite"
+ << endl;
+ throw operation_failed ();
+ }
+
+ data_member& nm (
+ comp->lookup<data_member> (tl, class_::include_hidden));
+
+ r.push_back (&nm);
+
+ if (container (nm))
+ return;
+ }
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (l) << "name '" << e.name << "' does not refer to a data member"
+ << endl;
+ else
+ error (l) << "unable to resolve data member '" << e.name << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (l) << "data member name '" << e.first.name () << "' is ambiguous"
+ << endl;
+
+ info (e.first.named ().location ()) << "could resolve to " <<
+ "this data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve " <<
+ "to this data member" << endl;
+
+ throw operation_failed ();
+ }
+}
+
+bool context::
+composite_ (semantics::class_& c)
+{
+ bool r (c.count ("value") && !c.count ("simple") && !c.count ("container"));
+ c.set ("composite-value", r);
+ return r;
+}
+
+// context::table_prefix
+//
+context::table_prefix::
+table_prefix (semantics::class_& c)
+ : level (1)
+{
+ context& ctx (context::current ());
+
+ ns_schema = ctx.schema (class_scope (c));
+ ns_prefix = ctx.table_name_prefix (class_scope (c));
+ prefix = ctx.table_name (c, &derived);
+ prefix += "_";
+}
+
+void context::table_prefix::
+append (semantics::data_member& m)
+{
+ assert (level > 0);
+
+ context& ctx (context::current ());
+
+ // If a custom table prefix was specified, then ignore the top-level
+ // table prefix (this corresponds to a container directly inside an
+ // object) but keep the schema unless the alternative schema is fully
+ // qualified.
+ //
+ if (m.count ("table"))
+ {
+ qname p, n (m.get<qname> ("table"));
+
+ if (n.fully_qualified ())
+ p = n.qualifier ();
+ else
+ {
+ if (n.qualified ())
+ {
+ p = ns_schema;
+ p.append (n.qualifier ());
+ }
+ else
+ p = prefix.qualifier ();
+ }
+
+ if (level == 1)
+ {
+ p.append (ns_prefix);
+ derived = false;
+ }
+ else
+ p.append (prefix.uname ());
+
+ p += n.uname ();
+ prefix.swap (p);
+ }
+ // Otherwise use the member name and add an underscore unless it is
+ // already there.
+ //
+ else
+ {
+ string name (ctx.public_name_db (m));
+ size_t n (name.size ());
+
+ prefix += name;
+
+ if (n != 0 && name[n - 1] != '_')
+ prefix += "_";
+
+ derived = true;
+ }
+
+ level++;
+}
+
+qname context::
+schema (semantics::scope& s) const
+{
+ if (s.count ("qualified-schema"))
+ return s.get<qname> ("qualified-schema");
+
+ qname r;
+
+ for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (ps));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ bool sf (ns->count ("schema"));
+ bool tf (ns->count ("table"));
+
+ if (tf)
+ {
+ qname n (ns->get<qname> ("table"));
+ tf = n.qualified ();
+
+ // If we have both schema and qualified table prefix, see which
+ // takes precedence based on order.
+ //
+
+ if (tf && sf)
+ {
+ if (ns->get<location_t> ("table-location") >
+ ns->get<location_t> ("schema-location"))
+ sf = false;
+ else
+ tf = false;
+ }
+ }
+
+ if (sf || tf)
+ {
+ qname n (
+ sf
+ ? ns->get<qname> ("schema")
+ : ns->get<qname> ("table").qualifier ());
+ n.append (r);
+ n.swap (r);
+ }
+
+ if (r.fully_qualified () ||
+ ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // If we are still not fully qualified, add the schema that was
+ // specified on the command line.
+ //
+ if (!r.fully_qualified () && options.schema ().count (db) != 0)
+ {
+ qname n (options.schema ()[db]);
+ n.append (r);
+ n.swap (r);
+ }
+
+ s.set ("qualified-schema", r);
+ return r;
+}
+
+string context::
+table_name_prefix (semantics::scope& s) const
+{
+ if (s.count ("table-prefix"))
+ return s.get<string> ("table-prefix");
+
+ string r;
+
+ for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (ps));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (ns->count ("table"))
+ {
+ qname n (ns->get<qname> ("table"));
+ r = n.uname () + r;
+ }
+
+ if (ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // Add the prefix that was specified on the command line.
+ //
+ if (options.table_prefix ().count (db) != 0)
+ r = options.table_prefix ()[db] + r;
+
+ s.set ("table-prefix", r);
+ return r;
+}
+
+qname context::
+table_name (semantics::class_& c, bool* pd) const
+{
+ if (c.count ("qualified-table"))
+ return c.get<qname> ("qualified-table");
+
+ qname r;
+ bool sf (c.count ("schema"));
+ bool derived;
+
+ if (c.count ("table"))
+ {
+ r = c.get<qname> ("table");
+
+ if (sf)
+ {
+ // If we have both schema and qualified table, see which takes
+ // precedence based on order. If the table is unqualifed, then
+ // add the schema.
+ //
+ sf = !r.qualified () ||
+ c.get<location_t> ("table-location") <
+ c.get<location_t> ("schema-location");
+ }
+
+ derived = false;
+ }
+ else
+ {
+ r = class_name (c);
+ derived = true;
+ }
+
+ if (sf)
+ {
+ qname n (c.get<qname> ("schema"));
+ n.append (r.uname ());
+ n.swap (r);
+ }
+
+ // Unless we are fully qualified, add any schemas that were
+ // specified on the namespaces and/or with the command line
+ // option.
+ //
+ if (!r.fully_qualified ())
+ {
+ qname n (schema (class_scope (c)));
+ n.append (r);
+ n.swap (r);
+ }
+
+ // Add the table prefix if any.
+ //
+ r.uname () = table_name_prefix (class_scope (c)) + r.uname ();
+
+ if (derived)
+ r.uname () = transform_name (r.uname (), sql_name_table);
+
+ c.set ("qualified-table", r);
+
+ if (pd != 0)
+ *pd = derived;
+
+ return r;
+}
+
+qname context::
+table_name (semantics::class_& obj, data_member_path const& mp) const
+{
+ table_prefix tp (obj);
+
+ if (mp.size () == 1)
+ {
+ // Container directly in the object.
+ //
+ return table_name (*mp.back (), tp);
+ }
+ else
+ {
+ data_member_path::const_iterator i (mp.begin ());
+
+ // The last member is the container.
+ //
+ for (data_member_path::const_iterator e (mp.end () - 1); i != e; ++i)
+ tp.append (**i);
+
+ return table_name (**i, tp);
+ }
+}
+
+// The table prefix passed as the second argument must include the table
+// prefix specified on namespaces and with the --table-prefix option.
+//
+qname context::
+table_name (semantics::data_member& m, table_prefix const& p) const
+{
+ assert (p.level > 0);
+ qname r;
+ string rn;
+ bool derived; // Any of the components in the table name is derived.
+
+ // If a custom table name was specified, then ignore the top-level
+ // table prefix (this corresponds to a container directly inside an
+ // object). If the container table is unqualifed, then we use the
+ // object schema. If it is fully qualified, then we use that name.
+ // Finally, if it is qualified but not fully qualifed, then we
+ // append the object's namespace schema.
+ //
+ if (m.count ("table"))
+ {
+ qname n (m.get<qname> ("table"));
+
+ if (n.fully_qualified ())
+ r = n.qualifier ();
+ else
+ {
+ if (n.qualified ())
+ {
+ r = p.ns_schema;
+ r.append (n.qualifier ());
+ }
+ else
+ r = p.prefix.qualifier ();
+ }
+
+ if (p.level == 1)
+ {
+ rn = p.ns_prefix;
+ derived = false;
+ }
+ else
+ {
+ rn = p.prefix.uname ();
+ derived = p.derived;
+ }
+
+ rn += n.uname ();
+ }
+ else
+ {
+ r = p.prefix.qualifier ();
+ rn = p.prefix.uname () + public_name_db (m);
+ derived = true;
+ }
+
+ if (derived)
+ r.append (transform_name (rn, sql_name_table));
+ else
+ r.append (rn);
+
+ return r;
+}
+
+string context::
+table_options (semantics::class_& c)
+{
+ string r;
+
+ // Accumulate options from class.
+ //
+ // @@ Should we also get them from bases?
+ //
+ // @@ Note for some databases (like SQLite), options are seperated with
+ // comma, not space. Likely the same issue in the column_options().
+ //
+ if (c.count ("options"))
+ {
+ strings const& o (c.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+table_options (semantics::data_member& m, semantics::type& c)
+{
+ string r;
+
+ // Accumulate options from container and member.
+ //
+ // @@ Currently there is no way to assign options to the container type. If
+ // we use the value specifier, then it ends up being treated as a value
+ // type. To support this we will probably need to invent the container
+ // specifier.
+ //
+ if (c.count ("options"))
+ {
+ strings const& o (c.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count ("options"))
+ {
+ strings const& o (m.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+// context::column_prefix
+//
+context::column_prefix::
+column_prefix (data_member_path const& mp, bool l)
+ : derived (false), underscore (false)
+{
+ if (mp.size () < (l ? 1 : 2))
+ return;
+
+ for (data_member_path::const_iterator i (mp.begin ()),
+ e (mp.end () - (l ? 0 : 1)); i != e; ++i)
+ append (**i);
+}
+
+void context::column_prefix::
+append (semantics::data_member& m, string const& kp, string const& dn)
+{
+ bool d;
+ context& ctx (context::current ());
+
+ if (kp.empty ())
+ prefix += ctx.column_name (m, d);
+ else
+ prefix += ctx.column_name (m, kp, dn, d);
+
+ // If the user provided the column prefix, then use it verbatime.
+ // Otherwise, append the underscore, unless it is already there.
+ //
+ underscore = false;
+ if (d)
+ {
+ size_t n (prefix.size ());
+
+ if (n != 0 && prefix[n - 1] != '_')
+ {
+ prefix += '_';
+ underscore = true;
+ }
+ }
+
+ derived = derived || d;
+}
+
+string context::
+column_name (semantics::data_member& m, bool& derived) const
+{
+ derived = !m.count ("column");
+ return derived
+ ? public_name_db (m)
+ : m.get<table_column> ("column").column;
+}
+
+string context::
+column_name (semantics::data_member& m, column_prefix const& cp) const
+{
+ bool d;
+ const string& cn (column_name (m, d));
+ string n (cp.prefix);
+
+ if (cn.empty () && cp.underscore)
+ n.resize (n.size () - 1); // Strip underscore that was auto added.
+
+ n += cn;
+
+ // If any component is derived, then run it through the SQL name regex.
+ //
+ if (d || cp.derived)
+ n = transform_name (n, sql_name_column);
+
+ return n;
+}
+
+string context::
+column_name (semantics::data_member& m,
+ string const& p,
+ string const& d,
+ bool& derived) const
+{
+ if (p.empty () && d.empty ())
+ return column_name (m, derived);
+
+ // A container column name can be specified for the member or for the
+ // container type.
+ //
+ string key (p + "-column");
+ derived = false;
+
+ if (m.count (key))
+ return m.get<string> (key);
+ else
+ {
+ semantics::type& t (utype (m));
+
+ if (t.count (key))
+ return t.get<string> (key);
+ }
+
+ derived = true;
+ return d;
+}
+
+string context::
+column_name (semantics::data_member& m,
+ string const& kp,
+ string const& dn,
+ column_prefix const& cp) const
+{
+ bool d;
+ const string& cn (column_name (m, kp, dn, d));
+ string n (cp.prefix);
+
+ if (cn.empty () && cp.underscore)
+ n.resize (n.size () - 1); // Strip underscore that was auto-added.
+
+ n += cn;
+
+ // If any component is derived, the run it through the SQL name regex.
+ //
+ if (d || cp.derived)
+ n = transform_name (n, sql_name_column);
+
+ return n;
+}
+
+string context::
+column_name (data_member_path const& mp) const
+{
+ return column_name (*mp.back (), column_prefix (mp));
+}
+
+string context::
+column_type (const data_member_path& mp, string const& kp, bool id)
+{
+ if (kp.empty ())
+ {
+ // Return the id type if this member is or is a part of an object id
+ // or pointer to object.
+ //
+ return mp.back ()->get<string> (
+ id || context::id (mp) || object_pointer (mp)
+ ? "column-id-type"
+ : "column-type");
+ }
+ else
+ return indirect_value<string> (*mp.back (), kp + "-column-type");
+}
+
+string context::
+column_type (semantics::data_member& m, string const& kp)
+{
+ return kp.empty ()
+ ? m.get<string> ("column-type")
+ : indirect_value<string> (m, kp + "-column-type");
+}
+
+string context::
+column_options (semantics::data_member& m)
+{
+ // Accumulate options from both type and member.
+ //
+ semantics::type* t (&utype (m));
+
+ if (semantics::class_* p = object_pointer (*t))
+ t = &utype (*id_member (*p));
+
+ if (semantics::type* w = wrapper (*t))
+ t = w;
+
+ string r;
+
+ if (t->count ("options"))
+ {
+ strings const& o (t->get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count ("options"))
+ {
+ strings const& o (m.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+column_options (semantics::data_member& m, string const& kp)
+{
+ if (kp.empty ())
+ return column_options (m);
+
+ string k (kp + "-options");
+
+ // Accumulate options from type, container, and member.
+ //
+ semantics::type& c (utype (m));
+ semantics::type* t (&utype (m, kp));
+
+ if (semantics::class_* p = object_pointer (*t))
+ t = &utype (*id_member (*p));
+
+ if (semantics::type* w = wrapper (*t))
+ t = w;
+
+ string r;
+
+ if (t->count ("options"))
+ {
+ strings const& o (t->get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (c.count (k))
+ {
+ strings const& o (c.get<strings> (k));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count (k))
+ {
+ strings const& o (m.get<strings> (k));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+context::type_map_type::const_iterator context::type_map_type::
+find (semantics::type& t, semantics::names* hint)
+{
+ const_iterator e (end ()), i (e);
+
+ // First check the hinted name. This allows us to handle things like
+ // size_t which is nice to map to the same type irrespective of the
+ // actual type. Since this type can be an alias for the one we are
+ // interested in, go into nested hints.
+ //
+ for (; hint != 0 && i == e; hint = hint->hint ())
+ i = base::find (t.fq_name (hint));
+
+ // If the hinted name didn't work, try the primary name (e.g.,
+ // ::std::string) instead of a user typedef (e.g., my_string).
+ //
+ if (i == e)
+ i = base::find (t.fq_name ());
+
+ return i;
+}
+
+string context::
+database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+{
+ using semantics::enum_;
+
+ // By default map an enum as its underlying type.
+ //
+ if (enum_* e = dynamic_cast<enum_*> (&t))
+ return database_type_impl (
+ e->underlying_type (), e->underlying_type_hint (), id, null);
+
+ // Built-in type mapping.
+ //
+ type_map_type::const_iterator i (data_->type_map_.find (t, hint));
+ if (i != data_->type_map_.end ())
+ {
+ if (null != 0)
+ *null = i->second.null;
+ return id ? i->second.id_type : i->second.type;
+ }
+
+ return string ();
+}
+
+static string
+public_name_impl (semantics::data_member& m)
+{
+ string s (m.name ());
+ size_t n (s.size ());
+
+ // Do basic processing: remove trailing and leading underscores
+ // as well as the 'm_' prefix.
+ //
+ // @@ What if the resulting names conflict?
+ //
+ size_t b (0), e (n - 1);
+
+ if (n > 2 && s[0] == 'm' && s[1] == '_')
+ b += 2;
+
+ for (; b <= e && s[b] == '_'; b++) ;
+ for (; e >= b && s[e] == '_'; e--) ;
+
+ return b > e ? s : string (s, b, e - b + 1);
+}
+
+string context::
+public_name_db (semantics::data_member& m) const
+{
+ return public_name_impl (m);
+}
+
+string context::
+compose_name (string const& prefix, string const& name)
+{
+ string r (prefix);
+ size_t n (r.size ());
+
+ // Add an underscore unless one is already in the prefix or
+ // the name is empty. Similarly, remove it if it is there but
+ // the name is empty.
+ //
+ if (n != 0)
+ {
+ if (r[n - 1] != '_')
+ {
+ if (!name.empty ())
+ r += '_';
+ }
+ else
+ {
+ if (name.empty ())
+ r.resize (n - 1);
+ }
+ }
+
+ r += name;
+ return r;
+}
+
+string context::
+transform_name (string const& name, sql_name_type type) const
+{
+ string r;
+
+ if (!data_->sql_name_regex_[type].empty () ||
+ !data_->sql_name_regex_[sql_name_all].empty ())
+ {
+ bool t (options.sql_name_regex_trace ());
+
+ if (t)
+ cerr << "name: '" << name << "'" << endl;
+
+ bool found (false);
+
+ // First try the type-specific transformations, if that didn't work,
+ // try common transformations.
+ //
+ for (unsigned short j (0); !found && j < 2; ++j)
+ {
+ regex_mapping const& rm = data_->sql_name_regex_[
+ j == 0 ? type : sql_name_all];
+
+ for (regex_mapping::const_iterator i (rm.begin ()); i != rm.end (); ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (i->match (name))
+ {
+ r = i->replace (name);
+ found = true;
+
+ if (t)
+ cerr << "'" << r << "' : ";
+ }
+
+ if (t)
+ cerr << (found ? '+' : '-') << endl;
+
+ if (found)
+ break;
+ }
+ }
+
+ if (!found)
+ r = name;
+ }
+ else
+ r = name;
+
+ if (options.sql_name_case ().count (db) != 0)
+ {
+ switch (options.sql_name_case ()[db])
+ {
+ case name_case::upper:
+ {
+ r = data_->sql_name_upper_.replace (r);
+ break;
+ }
+ case name_case::lower:
+ {
+ r = data_->sql_name_lower_.replace (r);
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+public_name (semantics::data_member& m, bool e) const
+{
+ return e ? escape (public_name_impl (m)) : public_name_impl (m);
+}
+
+string context::
+flat_name (string const& fq)
+{
+ string r;
+ r.reserve (fq.size ());
+
+ for (string::size_type i (0), n (fq.size ()); i < n; ++i)
+ {
+ char c (fq[i]);
+
+ if (c == ':')
+ {
+ if (!r.empty ())
+ r += '_';
+ ++i; // Skip the second ':'.
+ }
+ else
+ r += c;
+ }
+
+ return r;
+}
+
+string context::
+escape (string const& name) const
+{
+ typedef string::size_type size;
+
+ string r;
+ size n (name.size ());
+
+ // In most common cases we will have that many characters.
+ //
+ r.reserve (n);
+
+ for (size i (0); i < n; ++i)
+ {
+ char c (name[i]);
+
+ if (i == 0)
+ {
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '_'))
+ r = (c >= '0' && c <= '9') ? "cxx_" : "cxx";
+ }
+
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '_'))
+ r += '_';
+ else
+ r += c;
+ }
+
+ if (r.empty ())
+ r = "cxx";
+
+ // Custom reserved words.
+ //
+ /*
+ reserved_name_map_type::const_iterator i (reserved_name_map.find (r));
+
+ if (i != reserved_name_map.end ())
+ {
+ if (!i->second.empty ())
+ return i->second;
+ else
+ r += L'_';
+ }
+ */
+
+ // Keywords
+ //
+ if (keyword_set.find (r) != keyword_set.end ())
+ {
+ r += '_';
+
+ // Re-run custom words.
+ //
+ /*
+ i = reserved_name_map.find (r);
+
+ if (i != reserved_name_map.end ())
+ {
+ if (!i->second.empty ())
+ return i->second;
+ else
+ r += L'_';
+ }
+ */
+ }
+
+ return r;
+}
+
+string context::
+make_guard (string const& s) const
+{
+ // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything
+ // to upper case.
+ //
+ string r;
+ for (string::size_type i (0), n (s.size ()); i < n - 1; ++i)
+ {
+ char c1 (s[i]);
+ char c2 (s[i + 1]);
+
+ r += toupper (c1);
+
+ if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2))
+ r += "_";
+ }
+ r += toupper (s[s.size () - 1]);
+
+ return escape (r);
+}
+
+static string
+charlit (unsigned int u)
+{
+ string r ("\\x");
+ bool lead (true);
+
+ for (short i (7); i >= 0; --i)
+ {
+ unsigned int x ((u >> (i * 4)) & 0x0F);
+
+ if (lead)
+ {
+ if (x == 0)
+ continue;
+
+ lead = false;
+ }
+
+ r += static_cast<char> (x < 10 ? ('0' + x) : ('A' + x - 10));
+ }
+
+ return r;
+}
+
+static string
+strlit_ascii (string const& str)
+{
+ string r;
+ string::size_type n (str.size ());
+
+ // In most common cases we will have that many chars.
+ //
+ r.reserve (n + 2);
+
+ r += '"';
+
+ bool escape (false);
+
+ for (string::size_type i (0); i < n; ++i)
+ {
+ unsigned int u (static_cast<unsigned int> (str[i]));
+
+ // [128 - ] - unrepresentable
+ // 127 - \x7F
+ // [32 - 126] - as is
+ // [0 - 31] - \X or \xXX
+ //
+
+ if (u < 32 || u == 127)
+ {
+ switch (u)
+ {
+ case '\n':
+ {
+ r += "\\n";
+ break;
+ }
+ case '\t':
+ {
+ r += "\\t";
+ break;
+ }
+ case '\v':
+ {
+ r += "\\v";
+ break;
+ }
+ case '\b':
+ {
+ r += "\\b";
+ break;
+ }
+ case '\r':
+ {
+ r += "\\r";
+ break;
+ }
+ case '\f':
+ {
+ r += "\\f";
+ break;
+ }
+ case '\a':
+ {
+ r += "\\a";
+ break;
+ }
+ default:
+ {
+ r += charlit (u);
+ escape = true;
+ break;
+ }
+ }
+ }
+ else if (u < 127)
+ {
+ if (escape)
+ {
+ // Close and open the string so there are no clashes.
+ //
+ r += '"';
+ r += '"';
+
+ escape = false;
+ }
+
+ switch (u)
+ {
+ case '"':
+ {
+ r += "\\\"";
+ break;
+ }
+ case '\\':
+ {
+ r += "\\\\";
+ break;
+ }
+ default:
+ {
+ r += static_cast<char> (u);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // @@ Unrepresentable character.
+ //
+ r += '?';
+ }
+ }
+
+ r += '"';
+
+ return r;
+}
+
+string context::
+strlit (string const& str)
+{
+ return strlit_ascii (str);
+}
+
+void context::
+inst_header (bool decl, bool omit_exp)
+{
+ if (decl && !ext.empty ())
+ os << ext << " ";
+
+ os << "template struct";
+
+ if (!omit_exp && !exp.empty ())
+ {
+ // If we are generating an explicit instantiation directive rather
+ // than the extern template declaration, then omit the export symbol
+ // if we already have it in the header (i.e., extern symbol specified
+ // and defined). If we don't do that, then we get GCC warnings saying
+ // that the second set of visibility attributes is ignored.
+ //
+ if (!decl && !ext.empty ())
+ os << endl
+ << "#ifndef " << ext << endl
+ << options.export_symbol ()[db] << endl
+ << "#endif" << endl;
+ else
+ os << " " << exp;
+ }
+ else
+ os << " ";
+}
+
+namespace
+{
+ struct column_count_impl: object_members_base
+ {
+ column_count_impl (object_section* section = 0)
+ : object_members_base (false, section)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (m))
+ {
+ using semantics::class_;
+
+ column_count_type cc;
+
+ if (class_* root = polymorphic (c))
+ {
+ // For a polymorphic class we are going to load all the members
+ // from all the bases (i.e., equivalent to the first statement
+ // in the list of SELECT statements generated for the object).
+ // So our count should be the same as the first value in the
+ // generated column_counts array.
+ //
+ for (class_* b (&c);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b, section_));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+ cc.soft += ccb.soft;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (c, section_);
+
+ c_.total += cc.total - cc.separate_load;
+
+ if (added (member_path_) != 0 || deleted (member_path_) != 0)
+ c_.soft += cc.total;
+ else
+ c_.soft += cc.soft;
+ }
+ else
+ {
+ size_t t (c_.total);
+
+ object_members_base::traverse_pointer (m, c);
+
+ if (context::inverse (m))
+ {
+ size_t n (c_.total - t);
+
+ c_.inverse += n;
+
+ if (separate_update (member_path_))
+ c_.separate_update -= n;
+ }
+ }
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ c_.total++;
+
+ bool ro (context::readonly (member_path_, member_scope_));
+
+ if (id ())
+ c_.id++;
+ else if (ro)
+ c_.readonly++;
+ else if (context::version (m))
+ c_.optimistic_managed++;
+
+ // For now discriminator can only be a simple value.
+ //
+ if (discriminator (m))
+ c_.discriminator++;
+
+ {
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then don't count.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0)
+ c_.added++;
+
+ if (dv != 0)
+ c_.deleted++;
+
+ if (av != 0 || dv != 0)
+ c_.soft++;
+ }
+
+ if (separate_load (member_path_))
+ c_.separate_load++;
+
+ if (separate_update (member_path_) && !ro)
+ c_.separate_update++;
+ }
+
+ context::column_count_type c_;
+ };
+}
+
+context::column_count_type context::
+column_count (semantics::class_& c, object_section* s)
+{
+ if (s == 0)
+ {
+ // Whole class.
+ //
+ if (!c.count ("column-count"))
+ {
+ column_count_impl t;
+ t.traverse (c);
+ c.set ("column-count", t.c_);
+ }
+
+ return c.get<column_count_type> ("column-count");
+ }
+ else
+ {
+ column_count_impl t (s);
+ t.traverse (c);
+ return t.c_;
+ }
+}
+
+namespace
+{
+ struct has_a_impl: object_members_base
+ {
+ has_a_impl (unsigned short flags, object_section* s)
+ : object_members_base ((flags & context::include_base) != 0, s),
+ r_ (0),
+ flags_ (flags)
+ {
+ }
+
+ size_t
+ result () const
+ {
+ return r_;
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section if requested.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ ((flags_ & include_eager_load) != 0 &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ // Ignore polymorphic id references; they are represented as
+ // pointers but are normally handled in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ if (context::is_a (member_path_, member_scope_, flags_))
+ r_++;
+
+ // No need to go inside.
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member&)
+ {
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ if (context::is_a (member_path_, member_scope_, flags_))
+ r_++;
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ // Ignore versioned containers if so requested.
+ //
+ if ((flags_ & exclude_versioned) != 0 && versioned (m))
+ return;
+
+ // We don't cross the container boundaries (separate table).
+ //
+ unsigned short f (flags_ & (context::test_container |
+ context::test_straight_container |
+ context::test_inverse_container |
+ context::test_readonly_container |
+ context::test_readwrite_container |
+ context::test_smart_container));
+
+ if (context::is_a (member_path_,
+ member_scope_,
+ f,
+ context::container_vt (m),
+ "value"))
+ r_++;
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if ((flags_ & context::exclude_base) == 0)
+ inherits (c);
+
+ names (c);
+ }
+
+ private:
+ bool
+ check_soft ()
+ {
+ if ((flags_ & exclude_added) != 0 || (flags_ & exclude_deleted) != 0)
+ {
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then don't exclude.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if ((av != 0 && (flags_ & exclude_added) != 0) ||
+ (dv != 0 && (flags_ & exclude_deleted) != 0))
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ size_t r_;
+ unsigned short flags_;
+ };
+}
+
+bool context::
+is_a (data_member_path const& mp,
+ data_member_scope const& ms,
+ unsigned short f,
+ semantics::type& t,
+ string const& kp)
+{
+ bool r (false);
+
+ semantics::data_member& m (*mp.back ());
+
+ if (f & test_pointer)
+ r = r || object_pointer (t);
+
+ if (f & test_eager_pointer)
+ r = r || (object_pointer (t) && !lazy_pointer (t));
+
+ if (f & test_lazy_pointer)
+ r = r || (object_pointer (t) && lazy_pointer (t));
+
+ semantics::type* c;
+ if ((f & (test_container |
+ test_straight_container |
+ test_inverse_container |
+ test_readonly_container |
+ test_readwrite_container |
+ test_smart_container)) != 0 &&
+ (c = container (m)) != 0)
+ {
+ if (f & test_container)
+ r = r || true;
+
+ if (f & test_straight_container)
+ r = r || !inverse (m, kp);
+
+ if (f & test_inverse_container)
+ r = r || inverse (m, kp);
+
+ if (f & test_readonly_container)
+ r = r || readonly (mp, ms);
+
+ if (f & test_readwrite_container)
+ r = r || (!inverse (m, kp) && !readonly (mp, ms));
+
+ if (f & test_smart_container)
+ r = r || (!inverse (m, kp) && !unordered (m) && container_smart (*c));
+ }
+
+ return r;
+}
+
+size_t context::
+has_a (semantics::class_& c, unsigned short flags, object_section* s)
+{
+ has_a_impl impl (flags, s);
+ impl.dispatch (c);
+ return impl.result ();
+}
+
+string context::
+process_include_path (string const& ip, bool prefix, char open)
+{
+ bool t (options.include_regex_trace ());
+ string p (prefix ? options.include_prefix () : string ());
+
+ if (!p.empty () && p[p.size () - 1] != '/')
+ p.append ("/");
+
+ string path (p + ip), r;
+
+ if (t)
+ cerr << "include: '" << path << "'" << endl;
+
+ bool found (false);
+
+ for (regex_mapping::const_iterator i (include_regex.begin ());
+ i != include_regex.end (); ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (i->match (path))
+ {
+ r = i->replace (path);
+ found = true;
+
+ if (t)
+ cerr << "'" << r << "' : ";
+ }
+
+ if (t)
+ cerr << (found ? '+' : '-') << endl;
+
+ if (found)
+ break;
+ }
+
+ if (!found)
+ r = path;
+
+ // Add brackets or quotes unless the path already has them.
+ //
+ if (!r.empty () && r[0] != '"' && r[0] != '<')
+ {
+ bool b (open == '<' || (open == '\0' && options.include_with_brackets ()));
+ char op (b ? '<' : '"'), cl (b ? '>' : '"');
+ r = op + r + cl;
+ }
+
+ return r;
+}
diff --git a/odb/odb/context.hxx b/odb/odb/context.hxx
new file mode 100644
index 0000000..ec4505b
--- /dev/null
+++ b/odb/odb/context.hxx
@@ -0,0 +1,1941 @@
+// file : odb/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CONTEXT_HXX
+#define ODB_CONTEXT_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <map>
+#include <set>
+#include <list>
+#include <stack>
+#include <vector>
+#include <string>
+#include <memory> // std::unique_ptr
+#include <ostream>
+#include <cstddef> // std::size_t
+#include <iostream>
+
+#include <libcutl/re.hxx>
+#include <libcutl/shared-ptr.hxx>
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/location.hxx>
+#include <odb/cxx-token.hxx>
+#include <odb/semantics.hxx>
+#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/traversal.hxx>
+
+using std::endl;
+using std::cerr;
+
+// Regex.
+//
+using cutl::re::regex;
+using cutl::re::regexsub;
+typedef cutl::re::format regex_format;
+
+typedef std::vector<regexsub> regex_mapping;
+
+// Forward-declarations.
+//
+class cxx_string_lexer;
+
+// Generic exception thrown to indicate a failure when diagnostics
+// has already been issued (to stderr).
+//
+class operation_failed {};
+
+// Keep this enum synchronized with the one in libodb/odb/pointer-traits.hxx.
+//
+enum pointer_kind
+{
+ pk_raw,
+ pk_unique,
+ pk_shared,
+ pk_weak
+};
+
+// Keep this enum synchronized with the one in libodb/odb/container-traits.hxx.
+//
+enum container_kind
+{
+ ck_ordered,
+ ck_set,
+ ck_multiset,
+ ck_map,
+ ck_multimap
+};
+
+// The same as class_kind in libodb/odb/traits.hxx.
+//
+enum class_kind
+{
+ class_object,
+ class_view,
+ class_composite,
+ class_other
+};
+
+// Data member path.
+//
+// If it is a direct member of an object, then we will have just
+// one member. However, if this is a member inside a composite
+// value, then we will have a "path" constructed out of members
+// that lead all the way from the object member to the innermost
+// composite value member.
+//
+struct data_member_path: std::vector<semantics::data_member*>
+{
+ data_member_path () {}
+
+ explicit
+ data_member_path (semantics::data_member& m) {push_back (&m);}
+
+ // Return true if this is a sub-path of (or equal to) the
+ // specified path.
+ //
+ bool
+ sub (const data_member_path& p) const
+ {
+ size_t n (p.size ());
+
+ if (n > size ())
+ return false;
+
+ for (size_t i (0); i != n; ++i)
+ if ((*this)[i] != p[i])
+ return false;
+
+ return true;
+ }
+};
+
+// Class inheritance chain, from the most derived to base.
+//
+typedef std::vector<semantics::class_*> class_inheritance_chain;
+
+// A list of inheritance chains for a data member in an object.
+// The first entry in this list would correspond to the object.
+// All subsequent entries, if any, correspond to composite
+// values.
+//
+typedef std::vector<class_inheritance_chain> data_member_scope;
+
+//
+// Semantic graph context types.
+//
+
+// Custom C++ type mapping.
+//
+struct custom_cxx_type
+{
+ custom_cxx_type (): type_node (0), as_node (0) {}
+
+ std::string
+ translate_to (std::string const& v) const {return translate (v, to);}
+
+ std::string
+ translate_from (std::string const& v) const {return translate (v, from);}
+
+ tree type_node;
+ std::string type_name;
+ semantics::type* type;
+ semantics::names* type_hint;
+
+ tree as_node;
+ std::string as_name;
+ semantics::type* as;
+ semantics::names* as_hint;
+
+ cxx_tokens to;
+ bool to_move; // Single (?), so can move.
+
+ cxx_tokens from;
+ bool from_move; // Single (?), so can move.
+
+ location_t loc;
+ tree scope; // Scope for which this mapping is defined.
+
+private:
+ static std::string
+ translate (std::string const&, const cxx_tokens&);
+};
+
+typedef std::vector<custom_cxx_type> custom_cxx_types;
+typedef std::map<semantics::type*, custom_cxx_type*> custom_cxx_type_map;
+
+
+// Object or view pointer.
+//
+struct class_pointer
+{
+ std::string name;
+ tree scope;
+ location_t loc;
+};
+
+//
+//
+struct default_value
+{
+ enum kind_type
+ {
+ reset, // Default value reset.
+ null,
+ boolean, // Literal contains value (true or false).
+ integer, // Integer number. Literal contains sign.
+ floating, // Floating-point number.
+ string, // Literal contains value.
+ enumerator // Literal is the name, enum_value is the tree node.
+ };
+
+ kind_type kind;
+ std::string literal;
+
+ union
+ {
+ tree enum_value;
+ unsigned long long int_value;
+ double float_value;
+ };
+};
+
+// Database potentially-qualified and unqualifed names.
+//
+using semantics::relational::qname;
+using semantics::relational::uname;
+
+// Object or table associated with the view.
+//
+struct view_object
+{
+ // Return a diagnostic name for this association. It is either the
+ // alias, unqualified object name, or string representation of the
+ // table name.
+ //
+ std::string
+ name () const;
+
+ enum kind_type { object, table };
+ enum join_type { left, right, full, inner, cross };
+
+ kind_type kind;
+ join_type join;
+ tree obj_node; // Tree node if kind is object.
+ std::string obj_name; // Name as specified in the pragma if kind is object.
+ qname tbl_name; // Table name if kind is table.
+ std::string alias;
+ tree scope;
+ location_t loc;
+ semantics::class_* obj;
+ semantics::data_member* ptr; // Corresponding object pointer, if any.
+
+ cxx_tokens cond; // Join condition tokens.
+};
+
+typedef std::vector<view_object> view_objects;
+
+// The view_alias_map does not contain entries for tables.
+//
+typedef std::map<std::string, view_object*> view_alias_map;
+typedef std::map<semantics::class_*, view_object*> view_object_map;
+
+// Collection of relationships via which the objects are joined.
+// We need this information to figure out which alias/table
+// names to use for columns corresponding to inverse object
+// pointers inside objects that this view may be loading.
+//
+// The first object is the pointer (i.e., the one containing
+// this data member) while the other is the pointee. In other
+// words, first -> second. We always store the direct (i.e.,
+// non-inverse) side of the relationship. Note also that there
+// could be multiple objects joined using the same relationship.
+//
+typedef
+std::multimap<data_member_path, std::pair<view_object*, view_object*> >
+view_relationship_map;
+
+//
+//
+struct view_query
+{
+ view_query (): distinct (false), for_update (false) {}
+
+ enum kind_type
+ {
+ runtime,
+ complete_select, // SELECT query.
+ complete_execute, // Stored procedure call.
+ condition
+ };
+
+ kind_type kind;
+ std::string literal;
+ cxx_tokens expr;
+ tree scope;
+ location_t loc;
+
+ // Result modifiers (only for condition).
+ //
+ bool distinct; // SELECT DISTINCT
+ bool for_update; // SELECT FOR UPDATE
+};
+
+//
+//
+struct table_column
+{
+ qname table;
+ std::string column;
+ bool expr; // True if column is an expression, and therefore should not
+ // be quoted.
+
+ table_column () {}
+
+ explicit
+ table_column (const std::string& c): column (c), expr (false) {}
+};
+
+//
+//
+struct column_expr_part
+{
+ enum kind_type
+ {
+ literal,
+ reference
+ };
+
+ kind_type kind;
+ std::string value;
+ qname table; // Table name/alias for references.
+ data_member_path member_path; // Path to member for references.
+
+ // Scope and location of this pragma. Used to resolve the member name.
+ //
+ tree scope;
+ location_t loc;
+};
+
+struct column_expr: std::vector<column_expr_part>
+{
+ location_t loc;
+};
+
+//
+//
+struct member_access
+{
+ member_access (const location& l, const char* k, bool s)
+ : loc (l), kind (k), synthesized (s), by_value (false) {}
+
+ // Return true of we have the (?) placeholder.
+ //
+ bool
+ placeholder () const;
+
+ // Return true if this is a synthesized expression that goes
+ // directly for the member.
+ //
+ bool
+ direct () const
+ {
+ return synthesized && expr.size () == 3; // this.member
+ }
+
+ bool
+ empty () const
+ {
+ return expr.empty ();
+ }
+
+ // Issues diagnostics and throws operation_failed if expression is
+ // empty.
+ //
+ std::string
+ translate (std::string const& obj,
+ std::string const& val = std::string (),
+ std::string const& db = std::string ()) const;
+
+ location loc;
+ const char* kind; // accessor/modifier; used for diagnostics.
+ bool synthesized; // If true, then this is a synthesized expression.
+ cxx_tokens expr;
+ bool by_value; // True if accessor returns by value. False doesn't
+ // necessarily mean that it is by reference.
+};
+
+//
+//
+struct model_version
+{
+ unsigned long long base;
+ unsigned long long current;
+ bool open;
+};
+
+// Sections.
+//
+struct object_section
+{
+ virtual bool
+ compare (object_section const&) const = 0;
+
+ virtual bool
+ separate_load () const = 0;
+
+ virtual bool
+ separate_update () const = 0;
+};
+
+inline bool
+operator== (object_section const& x, object_section const& y)
+{
+ return x.compare (y);
+}
+
+inline bool
+operator!= (object_section const& x, object_section const& y)
+{
+ return !x.compare (y);
+}
+
+// Main section.
+//
+struct main_section_type: object_section
+{
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return false;}
+
+ virtual bool
+ separate_update () const {return false;}
+};
+
+inline bool
+operator== (main_section_type const&, main_section_type const&)
+{
+ return true; // There is only one main section.
+}
+
+extern main_section_type main_section;
+
+// User-defined section.
+//
+struct user_section: object_section
+{
+ enum load_type
+ {
+ load_eager,
+ load_lazy
+ };
+
+ enum update_type
+ {
+ update_always,
+ update_change,
+ update_manual
+ };
+
+ enum special_type
+ {
+ special_ordinary,
+ special_version // Fake section for optimistic version update.
+ };
+
+ user_section (semantics::data_member& m,
+ semantics::class_& o,
+ std::size_t i,
+ load_type l,
+ update_type u,
+ special_type s = special_ordinary)
+ : member (&m), object (&o), base (0), index (i),
+ load (l), update (u), special (s),
+ total (0), inverse (0), readonly (0), versioned (false),
+ containers (false), readwrite_containers (false),
+ versioned_containers (false), readwrite_versioned_containers (false) {}
+
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return load != load_eager;}
+
+ virtual bool
+ separate_update () const
+ {
+ // A separately-loaded section is always separately-updated since
+ // it might not be loaded when update is requested.
+ //
+ return separate_load () || update != update_always;
+ }
+
+ bool
+ load_empty () const;
+
+ bool
+ update_empty () const;
+
+ bool
+ empty () const
+ {
+ return load_empty () && update_empty ();
+ }
+
+ // A section is optimistic if the object that contains it is optimistic.
+ // For polymorphic hierarchies, only sections contained in the root are
+ // considered optimistic.
+ //
+ bool
+ optimistic () const;
+
+ semantics::data_member* member; // Data member of this section.
+ semantics::class_* object; // Object containing this section.
+ user_section* base; // Base of this section.
+ std::size_t index; // Index of this sections.
+
+ load_type load;
+ update_type update;
+ special_type special;
+
+ // Column counts.
+ //
+ std::size_t total;
+ std::size_t inverse;
+ std::size_t readonly;
+
+ bool versioned;
+
+ bool containers;
+ bool readwrite_containers;
+
+ bool versioned_containers;
+ bool readwrite_versioned_containers;
+
+ // Total counts across overrides.
+ //
+ std::size_t
+ total_total () const
+ {
+ user_section* b (total_base ());
+ return total + (b == 0 ? 0 : b->total_total ());
+ }
+
+ std::size_t
+ total_inverse () const
+ {
+ user_section* b (total_base ());
+ return inverse + (b == 0 ? 0 : b->total_inverse ());
+ }
+
+ std::size_t
+ total_readonly () const
+ {
+ user_section* b (total_base ());
+ return readonly + (b == 0 ? 0 : b->total_readonly ());
+ }
+
+ bool
+ total_containers ()
+ {
+ user_section* b (total_base ());
+ return containers || (b != 0 && b->total_containers ());
+ }
+
+ bool
+ total_readwrite_containers ()
+ {
+ user_section* b (total_base ());
+ return readwrite_containers ||
+ (b != 0 && b->total_readwrite_containers ());
+ }
+
+private:
+ user_section*
+ total_base () const;
+};
+
+inline bool
+operator== (user_section const& x, user_section const& y)
+{
+ return x.member == y.member;
+}
+
+// Using list for pointer for element stability (see user_section::base).
+//
+struct user_sections: std::list<user_section>
+{
+ // Count sections that have something to load.
+ //
+ static unsigned short const count_load = 0x01;
+
+ // Count sections that are non-eager but have nothing to load.
+ //
+ static unsigned short const count_load_empty = 0x02;
+
+ // Count sections that have something to update.
+ //
+ static unsigned short const count_update = 0x04;
+
+ // Count sections that have nothing to update.
+ //
+ static unsigned short const count_update_empty = 0x08;
+
+ // Count sections that are optimistic.
+ //
+ static unsigned short const count_optimistic = 0x10;
+
+ // Modifiers:
+ //
+
+ // Don't exclude fake optimistic version update section from the count.
+ //
+ static unsigned short const count_special_version = 0x20;
+
+ // Only count versioned sections.
+ //
+ static unsigned short const count_versioned_only = 0x40;
+
+
+ // Count all sections, but excluding special.
+ //
+ static unsigned short const count_all = count_update |
+ count_update_empty;
+
+ static unsigned short const count_new = 0x1000;
+ static unsigned short const count_override = 0x2000;
+ static unsigned short const count_total = 0x4000;
+
+ std::size_t
+ count (unsigned short flags) const;
+
+ user_sections (semantics::class_& o): object (&o) {};
+ semantics::class_* object;
+};
+
+// Context.
+//
+class context
+{
+public:
+ typedef std::size_t size_t;
+ typedef std::string string;
+ typedef std::vector<string> strings;
+ typedef std::ostream ostream;
+
+ typedef ::options options_type;
+
+ static string
+ upcase (string const&);
+
+public:
+ // Return cvr-unqualified base of the type, or type itself, if it is
+ // not qualified.
+ //
+ static semantics::type&
+ utype (semantics::type&);
+
+ // The same as above, but also returns the name hint for the unqualified
+ // type. If the original type is already unqualified, then the hint
+ // argument is not modified.
+ //
+ static semantics::type&
+ utype (semantics::type&, semantics::names*& hint);
+
+ // The same for a member's type but also do custom C++ type translation.
+ //
+ static semantics::type&
+ utype (semantics::data_member& m, const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, string (), translation);
+ }
+
+ static semantics::type&
+ utype (semantics::data_member& m,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, key_prefix, translation);
+ }
+
+ // In addition to the unqualified type, this version also returns the
+ // name hint for this type. If the member type is already unqualified,
+ // then the hint is from the belongs edge. Otherwise, it is from the
+ // qualifies edge.
+ //
+ static semantics::type&
+ utype (semantics::data_member&,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0);
+
+ static semantics::type&
+ utype (const data_member_path& mp, const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), key_prefix, translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), hint, key_prefix, translation);
+ }
+
+ // For arrays this function returns true if the (innermost) element
+ // type is const.
+ //
+ static bool
+ const_type (semantics::type&);
+
+ static bool
+ const_member (semantics::data_member& m) {return const_type (m.type ());}
+
+ // Form a reference type for a not mapped, actual member type. If
+ // make_const is true, then add top-level const qualifier, unless
+ // it is already there. If it is false, then strip it if it is
+ // already there. If var is not empty, then embed the variable
+ // name into the type (e.g., char (*v)[3]). If decay_array is
+ // false then don't decay the (top-level) array to a pointer.
+ //
+ static string
+ member_ref_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true)
+ {
+ return type_ref_type (
+ m.type (), m.belongs ().hint (), make_const, var, decay_array);
+ }
+
+ static string
+ type_ref_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true);
+
+ // Form a value type for a not mapped, actual member type. If make_const
+ // is true, then add top-level const qualifier, unless it is already
+ // there. If it is false, then strip it if it is already there. If var is
+ // not empty, then embed the variable name into the type (e.g., char v[3]).
+ //
+ static string
+ member_val_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "")
+ {
+ return type_val_type (m.type (), m.belongs ().hint (), make_const, var);
+ }
+
+ static string
+ type_val_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "");
+
+ // Member access helpers. Database can be empty. If type is not empty,
+ // then it should be the non-cvr type of the member (e.g., id_type).
+ //
+ void
+ set_member (semantics::data_member& m,
+ const std::string& obj,
+ const std::string& val,
+ const std::string& db,
+ const std::string& type = "");
+
+ void
+ inc_member (semantics::data_member& m,
+ const std::string& sobj, // Set expression object.
+ const std::string& gobj, // Get expression object.
+ const std::string& type = "");
+
+public:
+ // Resolve data member name in the form "a.b.c" to the data member path,
+ // issuing diagnostics and throwing operation_filed in case of an error.
+ // This function stops if it encounters a container leaving lex usable
+ // to continue parsing.
+ //
+ void
+ resolve_data_members (data_member_path& append,
+ semantics::class_& scope,
+ const std::string& name,
+ const location&,
+ cxx_string_lexer&);
+
+ data_member_path
+ resolve_data_members (semantics::class_& scope,
+ const std::string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+ {
+ data_member_path r;
+ resolve_data_members (r, scope, name, l, lex);
+ return r;
+ }
+
+ // Predicates.
+ //
+public:
+ static bool
+ object (semantics::type& t)
+ {
+ return t.count ("object");
+ }
+
+ static bool
+ view (semantics::type& t)
+ {
+ return t.count ("view");
+ }
+
+ // Direct member of a view.
+ //
+ static bool
+ view_member (semantics::data_member& m)
+ {
+ return view (dynamic_cast<semantics::class_&> (m.scope ()));
+ }
+
+ // Check whether the type is a wrapper. Return the wrapped type if
+ // it is a wrapper and NULL otherwise. Note that the returned type
+ // may be cvr-qualified.
+ //
+ static semantics::type*
+ wrapper (semantics::type& t)
+ {
+ return t.count ("wrapper") && t.get<bool> ("wrapper")
+ ? t.get<semantics::type*> ("wrapper-type")
+ : 0;
+ }
+
+ static semantics::type*
+ wrapper (semantics::type& t, semantics::names*& hint)
+ {
+ if (t.count ("wrapper") && t.get<bool> ("wrapper"))
+ {
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ return t.get<semantics::type*> ("wrapper-type");
+ }
+ else
+ return 0;
+ }
+
+ // Composite value type is a class type that was explicitly marked
+ // as value type and there was no database type mapping provided for
+ // it by the user (specifying the database type makes the value type
+ // simple).
+ //
+ static bool
+ composite (semantics::class_& c)
+ {
+ if (c.count ("composite-value"))
+ return c.get<bool> ("composite-value");
+ else
+ return composite_ (c);
+ }
+
+ // Return the class object if this type is a composite value type
+ // and NULL otherwise.
+ //
+ static semantics::class_*
+ composite (semantics::type& t)
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&t));
+ return c != 0 && composite (*c) ? c : 0;
+ }
+
+ // As above but also "sees through" wrappers.
+ //
+ static semantics::class_*
+ composite_wrapper (semantics::type& t)
+ {
+ if (semantics::class_* c = composite (t))
+ return c;
+ else if (semantics::type* wt = wrapper (t))
+ return composite (utype (*wt));
+ else
+ return 0;
+ }
+
+ // Check if a data member is a container. "Sees through" wrappers and
+ // returns the actual container type or NULL if not a container.
+ //
+ // We require data member as input instead of the type because the
+ // same type (e.g., vector<char>) can be used for both container
+ // and simple value members.
+ //
+ static semantics::type*
+ container (semantics::data_member& m)
+ {
+ // The same type can be used as both a container and a simple value.
+ //
+ if (m.count ("simple"))
+ return 0;
+
+ semantics::type* t (&utype (m));
+
+ if (semantics::type* wt = wrapper (*t))
+ t = &utype (*wt);
+
+ return t->count ("container-kind") ? t : 0;
+ }
+
+ static semantics::class_*
+ object_pointer (semantics::type& t)
+ {
+ return t.get<semantics::class_*> ("element-type", 0);
+ }
+
+ // If this data member is or is part of an object pointer, then
+ // return the member that is the pointer. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ object_pointer (data_member_path const&);
+
+ static semantics::class_*
+ points_to (semantics::data_member& m)
+ {
+ return m.get<semantics::class_*> ("points-to", 0);
+ }
+
+ static bool
+ abstract (semantics::class_& c)
+ {
+ // If a class is abstract in the C++ sense then it is also abstract in
+ // the database sense.
+ //
+ return c.abstract () || c.count ("abstract");
+ }
+
+ static bool
+ session (semantics::class_& c)
+ {
+ return c.get<bool> ("session");
+ }
+
+ static bool
+ transient (semantics::data_member& m)
+ {
+ return m.count ("transient");
+ }
+
+ // Return the deletion version or 0 if not soft-deleted.
+ //
+ static unsigned long long
+ deleted (semantics::class_& c, location_t* l = 0)
+ {
+ unsigned long long v (c.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = c.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (semantics::data_member& m, location_t* l = 0)
+ {
+ unsigned long long v (m.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = m.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (data_member_path const& mp, location_t* l = 0)
+ {
+ unsigned long long r (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+
+ if (l != 0)
+ *l = (*i)->get<location_t> ("deleted-location");
+ }
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ deleted_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ // Return the addition version or 0 if not soft-added.
+ //
+ static unsigned long long
+ added (semantics::class_& c) // Used for composite only.
+ {
+ return c.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (semantics::data_member& m)
+ {
+ return m.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (data_member_path const& mp)
+ {
+ unsigned long long r (0);
+
+ // Find the latest version since this member was added.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ r = v;
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ added_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the latest version since this member was added.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ static bool
+ id (semantics::data_member& m)
+ {
+ return m.count ("id");
+ }
+
+ // If this data member is or is part of an id member, then return
+ // the member that is marked as the id. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ id (data_member_path const&);
+
+ static bool
+ auto_ (semantics::data_member& m)
+ {
+ return id (m) && m.count ("auto");
+ }
+
+ // Must be a path returned by id(). In other words, it assumes
+ // the path is to the id member.
+ //
+ static bool
+ auto_ (data_member_path& mp)
+ {
+ return mp.front ()->count ("auto");
+ }
+
+ // The member scope is used to override readonly status when a readonly
+ // class (object or composite value) inherits from a readwrite base.
+ //
+ static bool
+ readonly (data_member_path const&, data_member_scope const&);
+
+ static bool
+ readonly (semantics::data_member&);
+
+ static bool
+ readonly (semantics::class_& c)
+ {
+ return c.count ("readonly");
+ }
+
+ // Null-able.
+ //
+ bool
+ null (data_member_path const&) const;
+
+ bool
+ null (semantics::data_member&) const;
+
+ bool
+ null (semantics::data_member&, string const& key_prefix) const;
+
+ // Optimistic concurrency.
+ //
+ static semantics::data_member*
+ optimistic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::data_member*> ("optimistic-member", 0);
+ }
+
+ static bool
+ version (semantics::data_member& m)
+ {
+ return m.count ("version");
+ }
+
+ static bool
+ version (const data_member_path& mp)
+ {
+ return mp.size () == 1 && mp.back ()->count ("version");
+ }
+
+ // Polymorphic inheritance. Return root of the hierarchy or NULL if
+ // not polymorphic.
+ //
+ static semantics::class_*
+ polymorphic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::class_*> ("polymorphic-root", 0);
+ }
+
+ static semantics::class_&
+ polymorphic_base (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return *c.get<semantics::class_*> ("polymorphic-base");
+ }
+
+ static size_t
+ polymorphic_depth (semantics::class_&);
+
+ static bool
+ discriminator (semantics::data_member& m)
+ {
+ return m.count ("discriminator");
+ }
+
+ static semantics::data_member*
+ discriminator (semantics::class_& c)
+ {
+ // Set by type processor.
+ //
+ return c.get<semantics::data_member*> ("discriminator", 0);
+ }
+
+ // Model version.
+ //
+ bool
+ versioned () const
+ {
+ return unit.count ("model-version") != 0;
+ }
+
+ model_version const&
+ version () const
+ {
+ return unit.get<model_version> ("model-version");
+ }
+
+ // Versioned object, view, or composite.
+ //
+ static bool
+ versioned (semantics::class_& c)
+ {
+ // Set by processor.
+ //
+ return c.count ("versioned") != 0;
+ }
+
+ // Versioned container.
+ //
+ static bool
+ versioned (semantics::data_member& m)
+ {
+ // Set by processor.
+ //
+ return container (m)->count ("versioned");
+ }
+
+ // Object sections.
+ //
+ static object_section&
+ section (semantics::data_member& m)
+ {
+ object_section* s (m.get<object_section*> ("section", 0));
+ return s == 0 ? main_section : *s;
+ }
+
+ static object_section&
+ section (data_member_path const& mp)
+ {
+ // The direct member of the object specifies the section. If the
+ // path is empty (which can happen, for example, for a container
+ // element), assume it is the main section.
+ //
+ //
+ return mp.empty () ? main_section : section (*mp.front ());
+ }
+
+ // Member belongs to a section that is loaded separately.
+ //
+ static bool
+ separate_load (semantics::data_member& m)
+ {
+ return section (m).separate_load ();
+ }
+
+ static bool
+ separate_load (data_member_path const& mp)
+ {
+ return section (mp).separate_load ();
+ }
+
+ // Member belongs to a section that is updated separately.
+ //
+ static bool
+ separate_update (semantics::data_member& m)
+ {
+ return section (m).separate_update ();
+ }
+
+ static bool
+ separate_update (data_member_path const& mp)
+ {
+ return section (mp).separate_update ();
+ }
+
+ //
+ //
+ typedef ::class_kind class_kind_type;
+
+ static class_kind_type
+ class_kind (semantics::class_&);
+
+ // Return class names. For ordinary classes, this will be the class
+ // name itself. For class template instantiations this will be the
+ // typedef name used in the pragma.
+ //
+ static string
+ class_name (semantics::class_&);
+
+ static string
+ class_fq_name (semantics::class_&);
+
+ // Return class scope. For ordinary classes, this will be the scope
+ // where the class is defined. For class template instantiations this
+ // will be the scope of the typedef name used in the pragma.
+ //
+ static semantics::scope&
+ class_scope (semantics::class_&);
+
+ // Return the class file. For ordinary classes, this will be the file
+ // where the class is defined. For class template instantiations this
+ // will be the file containing the pragma.
+ //
+ static semantics::path
+ class_file (semantics::class_&);
+
+ // Return the location (as location_t; useful for comparison) of
+ // an "ODB class" (i.e., an object, view, or composite value),
+ // taking into account things like definition point overrides,
+ // etc.
+ //
+ location_t
+ class_location (semantics::class_&);
+
+ // Same as above, but returns "real" location, that is, ignoring
+ // the definition point overrides.
+ //
+ location_t
+ class_real_location (semantics::class_&);
+
+ // Database names and types.
+ //
+public:
+ // Schema name for a namespace.
+ //
+ qname
+ schema (semantics::scope&) const;
+
+ // Table name prefix for a namespace.
+ //
+ string
+ table_name_prefix (semantics::scope&) const;
+
+ //
+ //
+ struct table_prefix
+ {
+ table_prefix (): level (0), derived (false) {}
+ table_prefix (semantics::class_&);
+
+ void
+ append (semantics::data_member&);
+
+ qname ns_schema; // Object's namespace schema.
+ string ns_prefix; // Object's namespace table prefix.
+ qname prefix;
+ size_t level;
+ bool derived; // One of the components in the prefix was derived.
+ };
+
+ qname
+ table_name (semantics::class_&, bool* derived = 0) const;
+
+ qname
+ table_name (semantics::class_&, data_member_path const&) const;
+
+ // Table name for the container member. The table prefix passed as the
+ // second argument must include the table prefix specified with the
+ // --table-prefix option.
+ //
+ qname
+ table_name (semantics::data_member&, table_prefix const&) const;
+
+ string
+ table_options (semantics::class_&);
+
+ // Table options for the container member.
+ //
+ string
+ table_options (semantics::data_member&, semantics::type& ct);
+
+ //
+ //
+ struct column_prefix
+ {
+ column_prefix (): derived (false), underscore (false) {}
+
+ column_prefix (semantics::data_member& m,
+ string const& key_prefix = string (),
+ string const& default_name = string ())
+ : derived (false), underscore (false)
+ {
+ append (m, key_prefix, default_name);
+ }
+
+ // If the last argument is true, the prefix will include the last member
+ // in the path.
+ //
+ column_prefix (data_member_path const&, bool last = false);
+
+ bool
+ empty () const {return prefix.empty ();}
+
+ void
+ append (semantics::data_member&,
+ string const& key_prefix = string (),
+ string const& default_name = string ());
+
+ string prefix;
+ bool derived; // One of the components in the prefix was derived.
+ bool underscore; // Trailing underscore was automatically added.
+ };
+
+ string
+ column_name (semantics::data_member&, bool& derived) const;
+
+ string
+ column_name (semantics::data_member&, column_prefix const&) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ bool& derived) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ column_prefix const&) const;
+
+ string
+ column_name (data_member_path const&) const;
+
+ //
+ //
+ string
+ column_type (const data_member_path&,
+ string const& key_prefix = string (),
+ bool id = false); // Pass true if this type is object id other
+ // than because of the members in the path.
+ string
+ column_type (semantics::data_member&, string const& key_prefix = string ());
+
+ string
+ column_options (semantics::data_member&);
+
+ string
+ column_options (semantics::data_member&, string const& key_prefix);
+
+ // Cleaned-up member name that can be used for database names.
+ //
+ string
+ public_name_db (semantics::data_member&) const;
+
+ // Compose the name by inserting/removing an underscore, as necessary.
+ //
+ static string
+ compose_name (string const& prefix, string const& name);
+
+ // SQL name transformations.
+ //
+ enum sql_name_type
+ {
+ sql_name_all,
+ sql_name_table,
+ sql_name_column,
+ sql_name_index,
+ sql_name_fkey,
+ sql_name_sequence,
+ sql_name_statement,
+ sql_name_count
+ };
+
+ string
+ transform_name (string const& name, sql_name_type) const;
+
+ // C++ names.
+ //
+public:
+ // Cleaned-up and potentially escaped member name that can be used
+ // in public C++ interfaces.
+ //
+ string
+ public_name (semantics::data_member&, bool escape = true) const;
+
+ // "Flatten" fully-qualified C++ name by replacing '::' with '_'
+ // and removing leading '::', if any.
+ //
+ static string
+ flat_name (string const& fqname);
+
+ // Escape C++ keywords, reserved names, and illegal characters.
+ //
+ string
+ escape (string const&) const;
+
+ // Make C++ include guard name by split words, e.g., "FooBar" to
+ // "Foo_Bar" and converting everything to upper case.
+ //
+ string
+ make_guard (string const&) const;
+
+ // Return a string literal that can be used in C++ source code. It
+ // includes "".
+ //
+ static string
+ strlit (string const&);
+
+public:
+ // Generate explicit instantiation headers with all the necessary
+ // extern and export symbols.
+ //
+ void
+ inst_header (bool decl, bool omit_exp = false);
+
+ // Counts and other information.
+ //
+public:
+ struct column_count_type
+ {
+ column_count_type ()
+ : total (0),
+ id (0),
+ inverse (0),
+ readonly (0),
+ optimistic_managed (0),
+ discriminator (0),
+ added (0),
+ deleted (0),
+ soft (0),
+ separate_load (0),
+ separate_update (0)
+ {
+ }
+
+ size_t total;
+ size_t id;
+ size_t inverse;
+ size_t readonly;
+ size_t optimistic_managed;
+ size_t discriminator;
+
+ size_t added; // Soft-added.
+ size_t deleted; // Soft-deleted.
+ size_t soft; // Soft-added/deleted (a column can be both).
+
+ size_t separate_load;
+ size_t separate_update; // Only readwrite.
+ };
+
+ static column_count_type
+ column_count (semantics::class_&, object_section* = 0);
+
+ static data_member_path*
+ id_member (semantics::class_& c)
+ {
+ // Set by the processor. May not be there for reuse-abstract
+ // classes or classes without object id.
+ //
+ return c.count ("id-member") ? &c.get<data_member_path> ("id-member") : 0;
+ }
+
+ // Object pointer information.
+ //
+public:
+ typedef ::pointer_kind pointer_kind_type;
+
+ pointer_kind_type
+ pointer_kind (semantics::type& p)
+ {
+ return p.get<pointer_kind_type> ("pointer-kind");
+ }
+
+ bool
+ lazy_pointer (semantics::type& p)
+ {
+ return p.get<bool> ("pointer-lazy");
+ }
+
+ bool
+ weak_pointer (semantics::type& p)
+ {
+ return pointer_kind (p) == pk_weak;
+ }
+
+ static data_member_path*
+ inverse (semantics::data_member& m)
+ {
+ return object_pointer (utype (m)) && m.count ("inverse")
+ ? &m.get<data_member_path> ("inverse")
+ : 0;
+ }
+
+ data_member_path*
+ inverse (semantics::data_member& m, string const& key_prefix)
+ {
+ if (key_prefix.empty ())
+ return inverse (m);
+
+ if (!object_pointer (utype (m, key_prefix)))
+ return 0;
+
+ string k (key_prefix + "-inverse");
+ return m.count (k) ? &m.get<data_member_path> (k) : 0;
+ }
+
+ // Container information.
+ //
+public:
+ typedef ::container_kind container_kind_type;
+
+ static container_kind_type
+ container_kind (semantics::type& c)
+ {
+ return c.get<container_kind_type> ("container-kind");
+ }
+
+ static bool
+ container_smart (semantics::type& c)
+ {
+ return c.get<bool> ("container-smart");
+ }
+
+ static semantics::type&
+ container_idt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "id", trans);
+ }
+
+ static semantics::type&
+ container_vt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "value", trans);
+ }
+
+ static semantics::type&
+ container_it (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "index", trans);
+ }
+
+ static semantics::type&
+ container_kt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "key", trans);
+ }
+
+ static bool
+ unordered (semantics::data_member& m)
+ {
+ if (m.count ("unordered"))
+ return true;
+
+ if (semantics::type* c = container (m))
+ return c->count ("unordered");
+
+ return false;
+ }
+
+ // The 'is a' and 'has a' tests. The has_a() test currently does not
+ // cross the container boundaries.
+ //
+public:
+ static unsigned short const test_pointer = 0x01;
+ static unsigned short const test_eager_pointer = 0x02;
+ static unsigned short const test_lazy_pointer = 0x04;
+ static unsigned short const test_container = 0x08;
+ static unsigned short const test_straight_container = 0x10;
+ static unsigned short const test_inverse_container = 0x20;
+ static unsigned short const test_readonly_container = 0x40;
+ static unsigned short const test_readwrite_container = 0x80;
+ static unsigned short const test_smart_container = 0x100;
+
+ // Exclude versioned containers.
+ //
+ static unsigned short const exclude_versioned = 0x200;
+
+ // Treat eager loaded members as belonging to the main section.
+ // If this flag is specified, then section must be main_section.
+ //
+ static unsigned short const include_eager_load = 0x800;
+
+ // Exclude added/deleted members.
+ //
+ static unsigned short const exclude_added = 0x1000;
+ static unsigned short const exclude_deleted = 0x2000;
+
+ // By default the test goes into bases for non-polymorphic
+ // hierarchies and doesn't go for polymorphic. The following
+ // flags can be used to alter this behavior.
+ //
+ static unsigned short const exclude_base = 0x4000;
+ static unsigned short const include_base = 0x8000;
+
+ bool
+ is_a (data_member_path const& mp,
+ data_member_scope const& ms,
+ unsigned short flags)
+ {
+ return is_a (mp, ms, flags, utype (*mp.back ()), "");
+ }
+
+ bool
+ is_a (data_member_path const&,
+ data_member_scope const&,
+ unsigned short flags,
+ semantics::type&,
+ string const& key_prefix);
+
+ // Return the number of matching entities. Can be used as a just
+ // a bool value (0 means no match).
+ //
+ size_t
+ has_a (semantics::class_&, unsigned short flags, object_section* = 0);
+
+public:
+ // Process include path by adding the prefix, putting it through
+ // the include regex list, and adding opening and closing include
+ // characters ("" or <>) if necessary. The prefix argument indicates
+ // whether the include prefix specified with the --include-prefix
+ // option should be added. The open argument can be used to specify
+ // the opening character. It can have three values: ", <, or \0. In
+ // case of \0, the character is determined based on the value of the
+ // --include-with-bracket option.
+ //
+ string
+ process_include_path (string const&, bool prefix = true, char open = '\0');
+
+ // Diverge output.
+ //
+public:
+ void
+ diverge (std::ostream& os)
+ {
+ diverge (os.rdbuf ());
+ }
+
+ void
+ diverge (std::streambuf* sb);
+
+ void
+ restore ();
+
+ // Implementation details.
+ //
+private:
+ static bool
+ composite_ (semantics::class_&);
+
+ template <typename X>
+ static X
+ indirect_value (semantics::context const& c, string const& key)
+ {
+ typedef X (*func) ();
+ std::type_info const& ti (c.type_info (key));
+
+ if (ti == typeid (func))
+ return c.get<func> (key) ();
+ else
+ return c.get<X> (key);
+ }
+
+ static semantics::type*
+ indirect_type (semantics::context const& c,
+ string const& kp,
+ semantics::names*& hint)
+ {
+ typedef semantics::type* (*func) (semantics::names*&);
+
+ string const tk (kp + "-tree-type");
+ std::type_info const& ti (c.type_info (tk));
+
+ if (ti == typeid (func))
+ return c.get<func> (tk) (hint);
+ else
+ {
+ hint = c.get<semantics::names*> (kp + "-tree-hint");
+ return c.get<semantics::type*> (tk);
+ }
+ }
+
+public:
+ typedef std::set<string> keyword_set_type;
+
+ struct db_type_type
+ {
+ db_type_type () {}
+ db_type_type (string const& t, string const& it, bool n)
+ : type (t), id_type (it), null (n)
+ {
+ }
+
+ string type;
+ string id_type;
+ bool null;
+ };
+
+ struct type_map_type: std::map<string, db_type_type>
+ {
+ typedef std::map<string, db_type_type> base;
+
+ const_iterator
+ find (semantics::type&, semantics::names* hint);
+ };
+
+protected:
+ struct data
+ {
+ virtual
+ ~data () {}
+
+ data (std::ostream& os)
+ : extra_ (0),
+ os_ (os.rdbuf ()),
+ in_comment_ (false),
+ top_object_ (0),
+ cur_object_ (0),
+ sql_name_upper_ ("(.+)", "\\U$1"),
+ sql_name_lower_ ("(.+)", "\\L$1")
+ {
+ }
+
+ public:
+ void* extra_;
+
+ std::ostream os_;
+ std::stack<std::streambuf*> os_stack_;
+
+ bool in_comment_;
+
+ semantics::class_* top_object_;
+ semantics::class_* cur_object_;
+
+ string exp_;
+ string ext_;
+
+ keyword_set_type keyword_set_;
+ type_map_type type_map_;
+
+ regex_mapping sql_name_regex_[sql_name_count];
+ regexsub sql_name_upper_;
+ regexsub sql_name_lower_;
+
+ regex_mapping include_regex_;
+ regex_mapping accessor_regex_;
+ regex_mapping modifier_regex_;
+ };
+
+ typedef cutl::shared_ptr<data> data_ptr;
+ data_ptr data_;
+
+public:
+ typedef ::features features_type;
+
+ void*& extra; // Extra data that may need to be shared by a sub-system.
+
+ std::ostream& os;
+ semantics::unit& unit;
+ options_type const& options;
+ features_type& features;
+ database const db;
+
+ bool& in_comment;
+
+ string& exp; // Export symbol (with trailing space if specified).
+ string& ext; // Extern symbol.
+
+ keyword_set_type const& keyword_set;
+
+ regex_mapping const& include_regex;
+ regex_mapping const& accessor_regex;
+ regex_mapping const& modifier_regex;
+
+ bool embedded_schema;
+ bool separate_schema;
+
+ bool multi_static;
+ bool multi_dynamic;
+
+ bool force_versioned; // Force statement processing for debugging.
+
+ // Outermost object or view currently being traversed.
+ //
+ semantics::class_*& top_object;
+
+ // Object or view currently being traversed. It can be the same as
+ // top_object or it can a base of top_object.
+ //
+ semantics::class_*& cur_object;
+
+ // Per-database customizable functionality.
+ //
+protected:
+ // Return empty string if there is no mapping. The type passed is
+ // already cvr-unqualified. The null out argument indicates whether
+ // the column should allow NULL values by default.
+ //
+ string
+ database_type (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null = 0)
+ {
+ return current ().database_type_impl (t, hint, id, null);
+ }
+
+ // The default implementation uses the type map (populated by the database-
+ // specific context implementation) to come up with a mapping.
+ //
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+public:
+ typedef context root_context;
+
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ data_ptr = data_ptr ());
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+private:
+ static context* current_;
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+ context&
+ operator= (context const&) = delete;
+#else
+private:
+ context&
+ operator= (context const&);
+#endif
+};
+
+// Create concrete database context.
+//
+std::unique_ptr<context>
+create_context (std::ostream&,
+ semantics::unit&,
+ options const&,
+ features&,
+ semantics::relational::model*);
+
+// Checks if scope Y names any of X.
+//
+template <typename X, typename Y>
+bool
+has (Y& y)
+{
+ for (semantics::scope::names_iterator i (y.names_begin ()),
+ e (y.names_end ()); i != e; ++i)
+ if (i->named (). template is_a<X> ())
+ return true;
+
+ return false;
+}
+
+#include <odb/context.ixx>
+
+#endif // ODB_CONTEXT_HXX
diff --git a/odb/odb/context.ixx b/odb/odb/context.ixx
new file mode 100644
index 0000000..5018743
--- /dev/null
+++ b/odb/odb/context.ixx
@@ -0,0 +1,26 @@
+// file : odb/context.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+inline bool user_section::
+load_empty () const
+{
+ return !separate_load () || (total == 0 && !containers && !optimistic ());
+}
+
+inline bool user_section::
+update_empty () const
+{
+ return total == inverse + readonly && !readwrite_containers;
+}
+
+inline bool user_section::
+optimistic () const
+{
+ if (!context::optimistic (*object))
+ return false;
+ else
+ {
+ semantics::class_* poly_root (context::polymorphic (*object));
+ return poly_root == 0 || poly_root == object;
+ }
+}
diff --git a/odb/odb/cxx-lexer.cxx b/odb/odb/cxx-lexer.cxx
new file mode 100644
index 0000000..67493b7
--- /dev/null
+++ b/odb/odb/cxx-lexer.cxx
@@ -0,0 +1,364 @@
+// file : odb/cxx-lexer.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <new> // std::bad_alloc
+#include <cassert>
+#include <iostream>
+
+#include <odb/cxx-lexer.hxx>
+
+using namespace std;
+
+//
+// cxx_lexer
+//
+
+// Token spelling. See cpplib.h for details.
+//
+#define OP(e, s) s ,
+#define TK(e, s) #e ,
+char const* cxx_lexer::token_spelling[N_TTYPES + 1] = { TTYPE_TABLE "KEYWORD"};
+#undef OP
+#undef TK
+
+cxx_lexer::
+~cxx_lexer ()
+{
+}
+
+//
+// cxx_tokens_lexer
+//
+
+void cxx_tokens_lexer::
+start (cxx_tokens const& ts, location_t start_loc)
+{
+ tokens_ = &ts;
+ cur_ = ts.begin ();
+ loc_ = start_loc;
+}
+
+cpp_ttype cxx_tokens_lexer::
+next (std::string& token, tree* node)
+{
+ if (cur_ != tokens_->end ())
+ {
+ loc_ = cur_->loc;
+ token = cur_->literal;
+ if (node != 0)
+ *node = cur_->node;
+ return static_cast<cpp_ttype> (cur_++->type);
+ }
+ else
+ return CPP_EOF;
+}
+
+location_t cxx_tokens_lexer::
+location () const
+{
+ return loc_;
+}
+
+//
+// cxx_pragma_lexer
+//
+
+void cxx_pragma_lexer::
+start ()
+{
+ token_ = &token_data_;
+ type_ = &type_data_;
+}
+
+string cxx_pragma_lexer::
+start (tree& token, cpp_ttype& type)
+{
+ token_ = &token;
+ type_ = &type;
+
+ return translate ();
+}
+
+cpp_ttype cxx_pragma_lexer::
+next (string& token, tree* node)
+{
+ *type_ = pragma_lex (token_);
+
+ // See if this is a keyword using the C++ parser machinery and
+ // the current C++ dialect.
+ //
+ if (*type_ == CPP_NAME &&
+#if BUILDING_GCC_MAJOR >= 8
+ IDENTIFIER_KEYWORD_P (*token_)
+#else
+ C_IS_RESERVED_WORD (*token_)
+#endif
+ )
+ *type_ = CPP_KEYWORD;
+
+ if (node != 0 && node != token_)
+ *node = *token_;
+
+ token = translate ();
+ return *type_;
+}
+
+location_t cxx_pragma_lexer::
+location () const
+{
+ // Starting from GCC 6 the input location seem to require the same
+ // translation as what we do in real_source_location().
+ //
+#if BUILDING_GCC_MAJOR >= 6
+ return linemap_resolve_location (
+ line_table, input_location, LRK_MACRO_EXPANSION_POINT, 0);
+#else
+ return input_location;
+#endif
+}
+
+string cxx_pragma_lexer::
+translate ()
+{
+ string r;
+
+ if (*type_ == CPP_NAME || *type_ == CPP_KEYWORD)
+ r = IDENTIFIER_POINTER (*token_);
+ else if (*type_ == CPP_STRING)
+ r = TREE_STRING_POINTER (*token_);
+
+ return r;
+}
+
+//
+// cxx_string_lexer
+//
+
+// Diagnostics callback.
+//
+extern "C" bool
+cpp_diagnostic_callback (
+ cpp_reader* reader,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level level,
+#else
+ int level,
+#endif
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_warning_reason,
+#else
+ int /*reason*/, // Added in GCC 4.6.0.
+#endif
+#endif
+#if BUILDING_GCC_MAJOR <= 5
+ location_t,
+ unsigned int,
+#else
+ rich_location*,
+#endif
+ char const* msg,
+ va_list *ap)
+{
+ char const* kind (0);
+ switch (level)
+ {
+ case CPP_DL_NOTE:
+ case CPP_DL_WARNING_SYSHDR:
+ case CPP_DL_WARNING:
+ case CPP_DL_PEDWARN:
+ // Ignore these.
+ break;
+ case CPP_DL_ERROR:
+ case CPP_DL_FATAL:
+ kind = "error";
+ break;
+ case CPP_DL_ICE:
+ kind = "ice";
+ break;
+ default:
+ kind = "unknown";
+ break;
+ }
+
+ if (kind != 0)
+ {
+ fprintf (stderr, "%s: ", kind);
+ vfprintf (stderr, msg, *ap);
+ fprintf (stderr, "\n");
+
+ // By resetting the callback we indicate to cxx_string_lexer that there
+ // was an error.
+ //
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_get_callbacks (reader)->diagnostic = 0;
+#else
+ cpp_get_callbacks (reader)->error = 0;
+#endif
+ return true;
+ }
+
+ return false;
+}
+
+cxx_string_lexer::
+cxx_string_lexer ()
+ : reader_ (0)
+{
+#if BUILDING_GCC_MAJOR >= 5
+ linemap_init (&line_map_, UNKNOWN_LOCATION);
+#else
+ linemap_init (&line_map_);
+#endif
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ line_map_.round_alloc_size = ggc_round_alloc_size;
+#endif
+
+ linemap_add (&line_map_, LC_ENTER, 0, "<memory>", 0);
+
+ reader_ = cpp_create_reader (
+ cxx_dialect == cxx0x // Nothing new for C++14.
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ ? CLK_CXX11
+#else
+ ? CLK_CXX0X
+#endif
+ : CLK_CXX98,
+ 0,
+ &line_map_);
+
+ if (reader_ == 0)
+ throw bad_alloc ();
+
+ callbacks_ = cpp_get_callbacks (reader_);
+}
+
+cxx_string_lexer::
+~cxx_string_lexer ()
+{
+ if (reader_ != 0)
+ cpp_destroy (reader_);
+
+ // Was removed as "dead code" in GCC 4.7.0.
+ //
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ linemap_free (&line_map_);
+#endif
+}
+
+void cxx_string_lexer::
+start (string const& data)
+{
+ // The previous lexing session should have popped the buffer.
+ //
+ assert (cpp_get_buffer (reader_) == 0);
+
+#if BUILDING_GCC_MAJOR >= 9
+ callbacks_->diagnostic = &cpp_diagnostic_callback;
+#else
+ callbacks_->error = &cpp_diagnostic_callback;
+#endif
+
+ data_ = data;
+ buf_ = data;
+ buf_ += '\n';
+ loc_ = 0;
+
+ cpp_push_buffer (
+ reader_,
+ reinterpret_cast<unsigned char const*> (buf_.c_str ()),
+ buf_.size (),
+ true);
+}
+
+cpp_ttype cxx_string_lexer::
+next (string& token, tree* node)
+{
+ token.clear ();
+ cpp_token const* t (cpp_get_token (reader_));
+
+ // If there was an error, the callback will be reset to 0. Diagnostics has
+ // already been issued.
+ //
+#if BUILDING_GCC_MAJOR >= 9
+ if (callbacks_->diagnostic == 0)
+#else
+ if (callbacks_->error == 0)
+#endif
+ throw invalid_input ();
+
+ cpp_ttype tt (t->type);
+
+ switch (tt)
+ {
+ case CPP_NAME:
+ {
+ char const* name (
+ reinterpret_cast<char const*> (NODE_NAME (t->val.node.node)));
+
+ // See if this is a keyword using the C++ parser machinery and
+ // the current C++ dialect.
+ //
+ tree id (get_identifier (name));
+
+ if (
+#if BUILDING_GCC_MAJOR >= 8
+ IDENTIFIER_KEYWORD_P (id)
+#else
+ C_IS_RESERVED_WORD (id)
+#endif
+ )
+ tt = CPP_KEYWORD;
+
+ if (node != 0)
+ *node = id;
+
+ token = name;
+ break;
+ }
+ case CPP_STRING:
+ case CPP_NUMBER:
+ {
+ if (node != 0)
+ *node = 0; // Doesn't seem to be available.
+
+ cpp_string const& s (t->val.str);
+ token.assign (reinterpret_cast<char const*> (s.text), s.len);
+ break;
+ }
+ default:
+ {
+ if (tt <= CPP_LAST_PUNCTUATOR)
+ {
+ if (node != 0)
+ *node = 0;
+ token = token_spelling[tt];
+ }
+ else
+ {
+ cerr << "unexpected token '" << token_spelling[tt] << "' in '" <<
+ data_ << "'" << endl;
+ throw invalid_input ();
+ }
+ break;
+ }
+ }
+
+ // Cache the location of this token.
+ //
+ loc_ = t->src_loc;
+
+ return tt;
+}
+
+location_t cxx_string_lexer::
+location () const
+{
+ return loc_;
+}
diff --git a/odb/odb/cxx-lexer.hxx b/odb/odb/cxx-lexer.hxx
new file mode 100644
index 0000000..ea16132
--- /dev/null
+++ b/odb/odb/cxx-lexer.hxx
@@ -0,0 +1,131 @@
+// file : odb/cxx-lexer.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CXX_LEXER_HXX
+#define ODB_CXX_LEXER_HXX
+
+#include <odb/gcc.hxx>
+
+#include <string>
+
+#include <odb/cxx-token.hxx>
+
+// A C++ keyword. This is an extension to libcpp token types. GCC 4.7.0
+// adds this define.
+//
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+# define CPP_KEYWORD ((cpp_ttype) (N_TTYPES + 1))
+#endif
+
+class cxx_lexer
+{
+public:
+ virtual
+ ~cxx_lexer ();
+
+public:
+ struct invalid_input {};
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0) = 0;
+
+ // Location of the last returned token.
+ //
+ virtual location_t
+ location () const = 0;
+
+public:
+ static char const* token_spelling[N_TTYPES + 1];
+};
+
+
+// Adapter to scan a saved token sequence. It returns numbers in the same
+// form as they were saved in the token sequence.
+//
+class cxx_tokens_lexer: public cxx_lexer
+{
+public:
+ void
+ start (cxx_tokens const&, location_t start_loc = 0);
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0);
+
+ virtual location_t
+ location () const;
+
+private:
+ cxx_tokens const* tokens_;
+ cxx_tokens::const_iterator cur_;
+ location_t loc_;
+};
+
+
+// A thin wrapper around the pragma_lex() function that recognizes
+// CPP_KEYWORD. It returns numbers as tree nodes.
+//
+class cxx_pragma_lexer: public cxx_lexer
+{
+public:
+ void
+ start ();
+
+ // Start with an already extracted (using the pragma_lex() function)
+ // token. This function translates the CPP_NAME to CPP_KEYWORD if
+ // necessary and returns the string token. It also uses the passed
+ // token and type for subsequent calls to next() so after the last
+ // next() call they will contain the information about the last
+ // token parsed.
+ //
+ std::string
+ start (tree& token, cpp_ttype& type);
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0);
+
+ virtual location_t
+ location () const;
+
+private:
+ std::string
+ translate ();
+
+private:
+ tree* token_;
+ cpp_ttype* type_;
+
+ tree token_data_;
+ cpp_ttype type_data_;
+};
+
+// A thin wrapper around cpp_reader for lexing C++ code fragments. It
+// returns numbers as string literals.
+//
+class cxx_string_lexer: public cxx_lexer
+{
+public:
+ cxx_string_lexer ();
+
+ virtual
+ ~cxx_string_lexer ();
+
+public:
+ void
+ start (std::string const&);
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0);
+
+ virtual location_t
+ location () const;
+
+private:
+ std::string data_;
+ std::string buf_;
+ line_maps line_map_;
+ cpp_reader* reader_;
+ cpp_callbacks* callbacks_;
+ location_t loc_;
+};
+
+#endif // ODB_CXX_LEXER_HXX
diff --git a/odb/odb/cxx-token.hxx b/odb/odb/cxx-token.hxx
new file mode 100644
index 0000000..34b28d4
--- /dev/null
+++ b/odb/odb/cxx-token.hxx
@@ -0,0 +1,30 @@
+// file : odb/cxx-token.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CXX_TOKEN_HXX
+#define ODB_CXX_TOKEN_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/gcc-fwd.hxx>
+
+struct cxx_token
+{
+ cxx_token (location_t l,
+ unsigned int t,
+ std::string const& lt = std::string (),
+ tree n = 0)
+ : loc (l), type (t), literal (lt), node (n) {}
+
+ location_t loc; // Location of this token.
+ unsigned int type; // Untyped cpp_ttype.
+ std::string literal; // Only used for name, keyword, string, amd number.
+ tree node; // Tree node for the number. The number can be
+ // represented as either literal, tree node, or
+ // both, depending on which lexer was used.
+};
+
+typedef std::vector<cxx_token> cxx_tokens;
+
+#endif // ODB_CXX_TOKEN_HXX
diff --git a/odb/odb/diagnostics.cxx b/odb/odb/diagnostics.cxx
new file mode 100644
index 0000000..57166bb
--- /dev/null
+++ b/odb/odb/diagnostics.cxx
@@ -0,0 +1,136 @@
+// file : odb/diagnostics.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <sstream>
+
+#include <odb/cxx-lexer.hxx>
+#include <odb/diagnostics.hxx>
+
+using namespace std;
+using cutl::fs::path;
+
+std::ostream&
+error (path const& p, size_t line, size_t clmn)
+{
+ //@@ We only need to do this if we are still parsing (i.e.,
+ // pragma parsing). Is there a way to detect this?
+ //
+ errorcount++;
+
+ cerr << p << ':' << line << ':' << clmn << ": error: ";
+ return cerr;
+}
+
+std::ostream&
+warn (path const& p, size_t line, size_t clmn)
+{
+ warningcount++;
+
+ cerr << p << ':' << line << ':' << clmn << ": warning: ";
+ return cerr;
+}
+
+std::ostream&
+info (path const& p, size_t line, size_t clmn)
+{
+ cerr << p << ':' << line << ':' << clmn << ": info: ";
+ return cerr;
+}
+
+std::ostream&
+error (location_t loc)
+{
+ errorcount++;
+ cerr << LOCATION_FILE (loc) << ':'
+ << LOCATION_LINE (loc) << ':'
+ << LOCATION_COLUMN (loc) << ':'
+ << " error: ";
+ return cerr;
+}
+
+std::ostream&
+warn (location_t loc)
+{
+ warningcount++;
+ cerr << LOCATION_FILE (loc) << ':'
+ << LOCATION_LINE (loc) << ':'
+ << LOCATION_COLUMN (loc) << ':'
+ << " warning: ";
+ return cerr;
+}
+
+std::ostream&
+info (location_t loc)
+{
+ cerr << LOCATION_FILE (loc) << ':'
+ << LOCATION_LINE (loc) << ':'
+ << LOCATION_COLUMN (loc) << ':'
+ << " info: ";
+ return cerr;
+}
+
+std::ostream&
+error (cxx_lexer& l)
+{
+ return error (l.location ());
+}
+
+std::ostream&
+warn (cxx_lexer& l)
+{
+ return warn (l.location ());
+}
+
+std::ostream&
+info (cxx_lexer& l)
+{
+ return info (l.location ());
+}
+
+std::string
+location_string (path const& p, size_t line, size_t clmn, bool leaf)
+{
+ ostringstream ostr;
+
+ if (leaf)
+ ostr << p.leaf ();
+ else
+ ostr << p;
+
+ ostr << ':' << line << ':' << clmn;
+ return ostr.str ();
+}
+
+std::string
+location_string (location_t loc, bool leaf)
+{
+ ostringstream ostr;
+
+ if (leaf)
+ ostr << path (LOCATION_FILE (loc)).leaf ();
+ else
+ ostr << LOCATION_FILE (loc);
+
+ ostr << ':' << LOCATION_LINE (loc) << ':' << LOCATION_COLUMN (loc);
+ return ostr.str ();
+}
+
+path
+location_file (location_t loc)
+{
+ return path (LOCATION_FILE (loc));
+}
+
+size_t
+location_line (location_t loc)
+{
+ return LOCATION_LINE (loc);
+}
+
+size_t
+location_column (location_t loc)
+{
+ return LOCATION_COLUMN (loc);
+}
diff --git a/odb/odb/diagnostics.hxx b/odb/odb/diagnostics.hxx
new file mode 100644
index 0000000..46f2272
--- /dev/null
+++ b/odb/odb/diagnostics.hxx
@@ -0,0 +1,96 @@
+// file : odb/diagnostics.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_DIAGNOSTICS_HXX
+#define ODB_DIAGNOSTICS_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <string>
+#include <cstddef>
+#include <iostream>
+
+#include <libcutl/fs/path.hxx>
+
+#include <odb/location.hxx>
+
+using std::endl;
+
+std::ostream&
+error (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+std::ostream&
+warn (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+std::ostream&
+info (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+inline std::ostream&
+error (location const& l)
+{
+ return error (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+warn (location const&l)
+{
+ return warn (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+info (location const&l)
+{
+ return info (l.file, l.line, l.column);
+}
+
+std::ostream&
+error (location_t);
+
+std::ostream&
+warn (location_t);
+
+std::ostream&
+info (location_t);
+
+//
+//
+class cxx_lexer;
+
+std::ostream&
+error (cxx_lexer&);
+
+std::ostream&
+warn (cxx_lexer&);
+
+std::ostream&
+info (cxx_lexer&);
+
+// Location as a string in the "<file>:<line>:<column>" format.
+//
+std::string
+location_string (cutl::fs::path const&,
+ std::size_t line,
+ std::size_t clmn,
+ bool leaf = false);
+
+inline std::string
+location_string (location const& l, bool leaf = false)
+{
+ return location_string (l.file, l.line, l.column, leaf);
+}
+
+std::string
+location_string (location_t, bool leaf = false);
+
+// location_t macro wrappers.
+//
+cutl::fs::path
+location_file (location_t);
+
+std::size_t
+location_line (location_t);
+
+std::size_t
+location_column (location_t);
+
+#endif // ODB_DIAGNOSTICS_HXX
diff --git a/odb/odb/emitter.cxx b/odb/odb/emitter.cxx
new file mode 100644
index 0000000..d6d8eac
--- /dev/null
+++ b/odb/odb/emitter.cxx
@@ -0,0 +1,50 @@
+// file : odb/emitter.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/context.hxx>
+#include <odb/emitter.hxx>
+
+using namespace std;
+
+void emitter::
+pre ()
+{
+}
+
+void emitter::
+post ()
+{
+}
+
+int emitter_ostream::streambuf::
+sync ()
+{
+ string s (str ());
+
+ // Get rid of the trailing newline if any.
+ //
+ if (string::size_type n = s.size ())
+ {
+ if (s[n - 1] == '\n')
+ s.resize (n - 1);
+ }
+
+ // Temporary restore output diversion.
+ //
+ bool r (false);
+ context& ctx (context::current ());
+
+ if (ctx.os.rdbuf () == this)
+ {
+ ctx.restore ();
+ r = true;
+ }
+
+ e_.line (s);
+
+ if (r)
+ ctx.diverge (this);
+
+ str (string ());
+ return 0;
+}
diff --git a/odb/odb/emitter.hxx b/odb/odb/emitter.hxx
new file mode 100644
index 0000000..1071dab
--- /dev/null
+++ b/odb/odb/emitter.hxx
@@ -0,0 +1,47 @@
+// file : odb/emitter.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_EMITTER_HXX
+#define ODB_EMITTER_HXX
+
+#include <string>
+#include <sstream>
+
+// Emit a code construct one line at a time.
+//
+struct emitter
+{
+ virtual void
+ pre ();
+
+ virtual void
+ line (const std::string&) = 0;
+
+ virtual void
+ post ();
+};
+
+// Send output line-by-line (std::endl marker) to the emitter.
+//
+class emitter_ostream: public std::ostream
+{
+public:
+ emitter_ostream (emitter& e): std::ostream (&buf_), buf_ (e) {}
+
+private:
+ class streambuf: public std::stringbuf
+ {
+ public:
+ streambuf (emitter& e): e_ (e) {}
+
+ virtual int
+ sync ();
+
+ private:
+ emitter& e_;
+ };
+
+ streambuf buf_;
+};
+
+#endif // ODB_EMITTER_HXX
diff --git a/odb/odb/features.hxx b/odb/odb/features.hxx
new file mode 100644
index 0000000..1093684
--- /dev/null
+++ b/odb/odb/features.hxx
@@ -0,0 +1,25 @@
+// file : odb/features.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_FEATURES_HXX
+#define ODB_FEATURES_HXX
+
+#include <cstring> // std::memset
+
+// Optional features used by client code that affect generated code.
+//
+struct features
+{
+ features () {std::memset (this, 0, sizeof (features));}
+
+ bool tr1_pointer;
+ bool boost_pointer;
+ bool simple_object;
+ bool polymorphic_object;
+ bool no_id_object;
+ bool session_object;
+ bool section;
+ bool view;
+};
+
+#endif // ODB_FEATURES_HXX
diff --git a/odb/odb/gcc-fwd.hxx b/odb/odb/gcc-fwd.hxx
new file mode 100644
index 0000000..83d3746
--- /dev/null
+++ b/odb/odb/gcc-fwd.hxx
@@ -0,0 +1,64 @@
+// file : odb/gcc-fwd.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GCC_FWD_HXX
+#define ODB_GCC_FWD_HXX
+
+#if __GNUC__ >= 5
+# if !__has_include(<bversion.h>)
+# error GCC plugin headers are not installed
+# endif
+#endif
+
+#include <bversion.h>
+
+#if BUILDING_GCC_MAJOR >= 6
+
+// If we include <system.h> here, it pulls in all kinds of GCC trouble that
+// "poisons" standard C/C++ declarations; see safe-ctype.h. So instead we
+// are going to "exclude" safe-ctype.h. To compensate, however, we will
+// include it first thing in gcc.hxx.
+//
+# include <config.h>
+# define SAFE_CTYPE_H
+# include <system.h>
+# undef SAFE_CTYPE_H
+# include <coretypes.h>
+
+typedef unsigned int source_location; // <line-map.h>
+typedef source_location location_t; // <input.h>
+
+#else // GCC < 6
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+# include <limits.h> // CHAR_BIT
+# include <config.h>
+#endif
+
+#if BUILDING_GCC_MAJOR >= 5
+# include <stdint.h> // Needed by coretypes.h
+#endif
+
+extern "C"
+{
+// The hwint.h header uses gcc_checking_assert macro from system.h. But
+// if we include system.h here, it pulls in all kinds of GCC trouble that
+// "poisons" standard C/C++ declarations (see safe-ctype.h for an example).
+// Instead we are just going to provide the no-op definition of this macro.
+//
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+# define gcc_checking_assert(expr) ((void)(0 & (expr)))
+# include <hwint.h>
+# undef gcc_checking_assert
+#endif
+
+#include <coretypes.h>
+
+typedef unsigned int source_location; // <line-map.h>
+typedef source_location location_t; // <input.h>
+
+} // extern "C"
+
+#endif
+
+#endif // ODB_GCC_FWD_HXX
diff --git a/odb/odb/gcc.hxx b/odb/odb/gcc.hxx
new file mode 100644
index 0000000..e5fecef
--- /dev/null
+++ b/odb/odb/gcc.hxx
@@ -0,0 +1,215 @@
+// file : odb/gcc.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GCC_HXX
+#define ODB_GCC_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+// Actually, let's keep it out. With it included we can compile in C++98
+// but not in C++14 (GCC 6 default).
+//
+// #if BUILDING_GCC_MAJOR >= 6
+// # include <safe-ctype.h> // See gcc-fwd.hxx.
+// #endif
+
+// GCC header includes to get the plugin and parse tree declarations.
+// The order is important and doesn't follow any kind of logic.
+//
+
+#include <stdlib.h>
+#include <gmp.h>
+
+#include <cstdlib> // Include before GCC poisons some declarations.
+
+// GCC 4.7 can be built using either C or C++ compiler. From 4.8 it
+// is always built as C++.
+//
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+# define ODB_GCC_PLUGIN_C
+#elif BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 7
+# include <config.h>
+# ifndef ENABLE_BUILD_WITH_CXX
+# define ODB_GCC_PLUGIN_C
+# endif
+#endif
+
+#ifdef ODB_GCC_PLUGIN_C
+extern "C"
+{
+#endif
+
+// GCC's system.h below includes safe-ctype.h which "disables" versions
+// from ctype.h. Well, now it's gonna learn how it feels to be disabled.
+//
+#define SAFE_CTYPE_H
+
+#include <gcc-plugin.h>
+
+#include <config.h>
+#include <system.h>
+#include <coretypes.h>
+#include <tree.h>
+#include <real.h>
+
+#include <cpplib.h>
+#include <cp/cp-tree.h>
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
+# include <c-family/c-common.h>
+# include <c-family/c-pragma.h>
+#else
+# include <c-common.h>
+# include <c-pragma.h>
+#endif
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+# include <stringpool.h> // get_identifier
+#endif
+
+#include <diagnostic.h>
+#include <output.h>
+
+#ifdef ODB_GCC_PLUGIN_C
+} // extern "C"
+#endif
+
+// Get the value of INTEGER_CST reinterpreted as unsigned.
+//
+inline unsigned long long
+integer_value (tree n)
+{
+ unsigned long long val;
+
+#if BUILDING_GCC_MAJOR >= 5
+ if (tree_fits_uhwi_p (n))
+ val = static_cast<unsigned long long> (tree_to_uhwi (n));
+ else
+ val = static_cast<unsigned long long> (tree_to_shwi (n));
+#else
+ HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n));
+ HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n));
+ unsigned short width (HOST_BITS_PER_WIDE_INT);
+
+ if (hwh == 0)
+ val = static_cast<unsigned long long> (hwl);
+ else if (hwh == -1 && hwl != 0)
+ val = static_cast<unsigned long long> (hwl);
+ else
+ val = static_cast<unsigned long long> ((hwh << width) + hwl);
+#endif
+
+ return val;
+}
+
+// Since 4.7.0 the location may point inside a macro rather than at
+// the expansion point. We are only really interested in the expansion
+// points so we use the real_source_location() wrapper rather than
+// DECL_SOURCE_LOCATION() to do this at the source.
+//
+inline location_t
+real_source_location (tree n)
+{
+ location_t l (DECL_SOURCE_LOCATION (n));
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ l = linemap_resolve_location (line_table, l, LRK_MACRO_EXPANSION_POINT, 0);
+#endif
+
+ return l;
+}
+
+// In 4.9.0 the tree code type was changed from int to enum tree_code.
+// the tree_code_name array is also gone with the get_tree_code_name()
+// function in its place.
+//
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+typedef enum tree_code gcc_tree_code_type;
+
+inline const char*
+gcc_tree_code_name (gcc_tree_code_type tc) {return get_tree_code_name (tc);}
+#else
+typedef int gcc_tree_code_type;
+
+inline const char*
+gcc_tree_code_name (gcc_tree_code_type tc) {return tree_code_name[tc];}
+#endif
+
+// Only since GCC 4.7.0.
+//
+#ifndef LOCATION_COLUMN
+#define LOCATION_COLUMN(LOC) (expand_location (LOC).column)
+#endif
+
+#ifndef DECL_SOURCE_COLUMN
+#define DECL_SOURCE_COLUMN(NODE) LOCATION_COLUMN (DECL_SOURCE_LOCATION (NODE))
+#endif
+
+// Only since GCC 4.6.0.
+//
+#ifndef DECL_CHAIN
+#define DECL_CHAIN(x) TREE_CHAIN(x)
+#endif
+
+// In GCC 6 ANON_AGGRNAME_P became anon_aggrname_p().
+// In GCC 10 anon_aggrname_p() became IDENTIFIER_ANON_P.
+//
+#if BUILDING_GCC_MAJOR < 6
+# define IDENTIFIER_ANON_P(X) ANON_AGGRNAME_P(X)
+#elif BUILDING_GCC_MAJOR < 10
+# define IDENTIFIER_ANON_P(X) anon_aggrname_p(X)
+#endif
+
+// In GCC 9:
+//
+// INCLUDED_FROM Became linemap_included_from_linemap().
+//
+// LAST_SOURCE_LINE Was removed apparently as no longer used. Studying
+// the line-map.h diff from 8.3 suggests that the old
+// implementation should still work.
+//
+#if BUILDING_GCC_MAJOR >= 9
+
+inline const line_map_ordinary*
+INCLUDED_FROM (line_maps* set, const line_map_ordinary* map)
+{
+ return linemap_included_from_linemap (set, map);
+}
+
+inline source_location
+LAST_SOURCE_LINE_LOCATION (const line_map_ordinary* map)
+{
+ return (((map[1].start_location - 1
+ - map->start_location)
+ & ~((1 << map->m_column_and_range_bits) - 1))
+ + map->start_location);
+}
+
+inline linenum_type
+LAST_SOURCE_LINE (const line_map_ordinary* map)
+{
+ return SOURCE_LINE (map, LAST_SOURCE_LINE_LOCATION (map));
+}
+
+#endif
+
+// In GCC 11:
+//
+// lookup_qualified_name() has a new interface.
+//
+// DECL_IS_BUILTIN became DECL_IS_UNDECLARED_BUILTIN.
+//
+#if BUILDING_GCC_MAJOR >= 11
+
+inline tree
+lookup_qualified_name (tree scope, tree name, bool type, bool complain)
+{
+ return lookup_qualified_name (
+ scope, name, (type ? LOOK_want::TYPE : LOOK_want::NORMAL), complain);
+}
+
+#define DECL_IS_BUILTIN(decl) DECL_IS_UNDECLARED_BUILTIN(decl)
+
+#endif
+
+#endif // ODB_GCC_HXX
diff --git a/odb/odb/generate.hxx b/odb/odb/generate.hxx
new file mode 100644
index 0000000..b3b9c43
--- /dev/null
+++ b/odb/odb/generate.hxx
@@ -0,0 +1,31 @@
+// file : odb/generate.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GENERATE_HXX
+#define ODB_GENERATE_HXX
+
+namespace include
+{
+ bool
+ generate (bool header);
+}
+
+namespace header
+{
+ void
+ generate ();
+}
+
+namespace inline_
+{
+ void
+ generate ();
+}
+
+namespace source
+{
+ void
+ generate ();
+}
+
+#endif // ODB_GENERATE_HXX
diff --git a/odb/odb/generator.cxx b/odb/odb/generator.cxx
new file mode 100644
index 0000000..ec0fefe
--- /dev/null
+++ b/odb/odb/generator.cxx
@@ -0,0 +1,1044 @@
+// file : odb/generator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cctype> // std::toupper, std::is{alpha,upper,lower}
+#include <string>
+#include <memory> // std::unique_ptr
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <libcutl/fs/auto-remove.hxx>
+
+#include <libcutl/compiler/code-stream.hxx>
+#include <libcutl/compiler/cxx-indenter.hxx>
+#include <libcutl/compiler/sloc-counter.hxx>
+
+#ifdef ODB_BUILD2
+#include <libstudxml/parser.hxx>
+#include <libstudxml/serializer.hxx>
+#else
+#include <libcutl/xml/parser.hxx>
+#include <libcutl/xml/serializer.hxx>
+#endif
+
+#include <odb/version.hxx>
+#include <odb/context.hxx>
+#include <odb/generator.hxx>
+
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
+#include <odb/generate.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+using namespace cutl;
+
+using semantics::path;
+typedef vector<string> strings;
+typedef vector<path> paths;
+typedef vector<cutl::shared_ptr<ofstream> > ofstreams;
+
+namespace
+{
+ static char const cxx_file_header[] =
+ "// -*- C++ -*-\n"
+ "//\n"
+ "// This file was generated by ODB, object-relational mapping (ORM)\n"
+ "// compiler for C++.\n"
+ "//\n\n";
+
+ static char const sql_file_header[] =
+ "/* This file was generated by ODB, object-relational mapping (ORM)\n"
+ " * compiler for C++.\n"
+ " */\n\n";
+
+ void
+ open (ifstream& ifs, path const& p)
+ {
+ ifs.open (p.string ().c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in read mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ open (ofstream& ofs, path const& p, ios_base::openmode m = ios_base::out)
+ {
+ ofs.open (p.string ().c_str (), ios_base::out | m);
+
+ if (!ofs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in write mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ append (ostream& os, strings const& text)
+ {
+ for (strings::const_iterator i (text.begin ());
+ i != text.end (); ++i)
+ {
+ os << *i << endl;
+ }
+ }
+
+ void
+ append (ostream& os, path const& file)
+ {
+ ifstream ifs;
+ open (ifs, file);
+
+ // getline() will set the failbit if it failed to extract anything,
+ // not even the delimiter and eofbit if it reached eof before seeing
+ // the delimiter.
+ //
+ // We used to just do:
+ //
+ // os << ifs.rdbuf ();
+ //
+ // But that has some drawbacks: it won't end with a newline if the file
+ // doesn't end with one. There were also some issues with Windows newlines
+ // (we ended up doubling them).
+ //
+ for (string s; getline (ifs, s); )
+ os << s << endl;
+ }
+
+ // Append prologue/interlude/epilogue.
+ //
+ void
+ append_logue (ostream& os,
+ database db,
+ database_map<vector<string> > const& text,
+ database_map<vector<string> > const& file,
+ char const* begin_comment,
+ char const* end_comment)
+ {
+ bool t (text.count (db) != 0);
+ bool f (file.count (db) != 0);
+
+ if (t || f)
+ {
+ os << begin_comment << endl;
+
+ if (t)
+ append (os, text[db]);
+
+ if (f)
+ {
+ strings const& fs (file[db]);
+
+ for (strings::const_iterator i (fs.begin ());
+ i != fs.end (); ++i)
+ append (os, path (*i));
+ }
+
+ os << end_comment << endl
+ << endl;
+ }
+ }
+}
+
+void
+generate (options const& ops,
+ features& fts,
+ semantics::unit& unit,
+ path const& p,
+ paths const& inputs)
+{
+ namespace sema_rel = semantics::relational;
+ using cutl::shared_ptr;
+
+ try
+ {
+ database db (ops.database ()[0]);
+ multi_database md (ops.multi_database ());
+
+ // First create the database model.
+ //
+ bool gen_schema (ops.generate_schema () && db != database::common);
+
+ shared_ptr<sema_rel::model> model;
+
+ if (gen_schema)
+ {
+ unique_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ model = relational::model::generate ();
+ break;
+ }
+ case database::common:
+ break;
+ }
+ }
+
+ // Input files.
+ //
+ path file (ops.input_name ().empty ()
+ ? p.leaf ()
+ : path (ops.input_name ()).leaf ());
+ string base (file.base ().string ());
+
+ path in_log_path;
+ path log_dir (ops.changelog_dir ().count (db) != 0
+ ? ops.changelog_dir ()[db]
+ : "");
+ if (ops.changelog_in ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog_in ()[db]);
+
+ if (!log_dir.empty () && !in_log_path.absolute ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else if (ops.changelog ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog ()[db]);
+
+ if (!in_log_path.absolute () && !log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else
+ {
+ string log_name (base + ops.changelog_file_suffix ()[db] +
+ ops.changelog_suffix ());
+ in_log_path = path (log_name);
+
+ if (!log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ else
+ in_log_path = p.directory () / in_log_path; // Use input directory.
+ }
+
+ // Load the old changelog and generate a new one.
+ //
+ bool gen_changelog (gen_schema && unit.count ("model-version") != 0);
+ cutl::shared_ptr<sema_rel::changelog> changelog;
+ cutl::shared_ptr<sema_rel::changelog> old_changelog;
+ string old_changelog_xml;
+
+ path out_log_path;
+ if (ops.changelog_out ().count (db))
+ {
+ out_log_path = path (ops.changelog_out ()[db]);
+
+ if (!log_dir.empty () && !out_log_path.absolute ())
+ out_log_path = log_dir / out_log_path;
+ }
+ else
+ out_log_path = in_log_path;
+
+ if (gen_changelog)
+ {
+ ifstream log;
+
+ // Unless we are forced to re-initialize the changelog, load the
+ // old one.
+ //
+ if (!ops.init_changelog ())
+ log.open (in_log_path.string ().c_str (),
+ ios_base::in | ios_base::binary);
+
+ if (log.is_open ()) // The changelog might not exist.
+ {
+ try
+ {
+ // Get the XML into a buffer. We use it to avoid modifying the
+ // file when the changelog hasn't changed.
+ //
+ for (bool first (true); !log.eof (); )
+ {
+ string line;
+ getline (log, line);
+
+ if (log.fail ())
+ ios_base::failure ("getline");
+
+ if (first)
+ first = false;
+ else
+ old_changelog_xml += '\n';
+
+ old_changelog_xml += line;
+ }
+
+ istringstream is (old_changelog_xml);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+
+ xml::parser p (is, in_log_path.string ());
+ old_changelog.reset (new (shared) sema_rel::changelog (p));
+
+ if (old_changelog->database () != db.string ())
+ {
+ cerr << in_log_path << ": error: wrong database '" <<
+ old_changelog->database () << "', expected '" << db <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+
+ string sn (ops.schema_name ()[db]);
+ if (old_changelog->schema_name () != sn)
+ {
+ cerr << in_log_path << ": error: wrong schema name '" <<
+ old_changelog->schema_name () << "', expected '" << sn <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << in_log_path << ": read failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::parsing& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ changelog = relational::changelog::generate (
+ *model,
+ unit.get<model_version> ("model-version"),
+ old_changelog.get (),
+ in_log_path.string (),
+ out_log_path.string (),
+ ops);
+ }
+
+ // Output files.
+ //
+ fs::auto_removes auto_rm;
+
+ string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ());
+ string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ());
+ string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ());
+ string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ());
+ string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ());
+
+ path hxx_path (hxx_name);
+ path ixx_path (ixx_name);
+ path cxx_path (cxx_name);
+ path sch_path (sch_name);
+ path sql_path (sql_name);
+ paths mig_pre_paths;
+ paths mig_post_paths;
+
+ bool gen_migration (gen_changelog && !ops.suppress_migration ());
+ bool gen_sql_migration (
+ gen_migration && ops.schema_format ()[db].count (schema_format::sql));
+
+ if (gen_sql_migration)
+ {
+ for (sema_rel::changelog::contains_changeset_iterator i (
+ changelog->contains_changeset_begin ());
+ i != changelog->contains_changeset_end (); ++i)
+ {
+ sema_rel::changeset& cs (i->changeset ());
+
+ // Default format: %N[-D%]-%3V-{pre|post}.sql
+ //
+ string n (base);
+
+ if (md != multi_database::disabled)
+ n += '-' + db.string ();
+
+ ostringstream os;
+ os << setfill ('0') << setw (3) << cs.version ();
+ n += '-' + os.str ();
+
+ mig_pre_paths.push_back (path (n + "-pre" + ops.sql_suffix ()));
+ mig_post_paths.push_back (path (n + "-post" + ops.sql_suffix ()));
+ }
+ }
+
+ if (!ops.output_dir ().empty ())
+ {
+ path dir (ops.output_dir ());
+ hxx_path = dir / hxx_path;
+ ixx_path = dir / ixx_path;
+ cxx_path = dir / cxx_path;
+ sch_path = dir / sch_path;
+ sql_path = dir / sql_path;
+
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ mig_pre_paths[i] = dir / mig_pre_paths[i];
+ mig_post_paths[i] = dir / mig_post_paths[i];
+ }
+ }
+ }
+
+ //
+ //
+ bool gen_cxx (!ops.generate_schema_only ());
+
+ ofstream hxx;
+ if (gen_cxx)
+ {
+ open (hxx, hxx_path);
+ auto_rm.add (hxx_path);
+ }
+
+ //
+ //
+ ofstream ixx;
+ if (gen_cxx)
+ {
+ open (ixx, ixx_path);
+ auto_rm.add (ixx_path);
+ }
+
+ //
+ //
+ ofstream cxx;
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ open (cxx, cxx_path);
+ auto_rm.add (cxx_path);
+ }
+
+ //
+ //
+ bool gen_sep_schema (
+ gen_schema &&
+ ops.schema_format ()[db].count (schema_format::separate));
+
+ ofstream sch;
+ if (gen_sep_schema)
+ {
+ open (sch, sch_path);
+ auto_rm.add (sch_path);
+ }
+
+ //
+ //
+ bool gen_sql_schema (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::sql));
+ ofstream sql;
+ if (gen_sql_schema)
+ {
+ open (sql, sql_path);
+ auto_rm.add (sql_path);
+ }
+
+ //
+ //
+ ofstreams mig_pre, mig_post;
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ shared_ptr<ofstream> pre (new (shared) ofstream);
+ shared_ptr<ofstream> post (new (shared) ofstream);
+
+ open (*pre, mig_pre_paths[i]);
+ auto_rm.add (mig_pre_paths[i]);
+ mig_pre.push_back (pre);
+
+ open (*post, mig_post_paths[i]);
+ auto_rm.add (mig_post_paths[i]);
+ mig_post.push_back (post);
+ }
+ }
+
+ // Print output file headers.
+ //
+ if (gen_cxx)
+ {
+ hxx << cxx_file_header;
+ ixx << cxx_file_header;
+
+ if (db != database::common)
+ cxx << cxx_file_header;
+ }
+
+ if (gen_sep_schema)
+ sch << cxx_file_header;
+
+ if (gen_sql_schema)
+ sql << sql_file_header;
+
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ *mig_pre[i] << sql_file_header;
+ *mig_post[i] << sql_file_header;
+ }
+ }
+
+ typedef compiler::ostream_filter<compiler::cxx_indenter, char> ind_filter;
+ typedef compiler::ostream_filter<compiler::sloc_counter, char> sloc_filter;
+
+ size_t sloc_total (0);
+
+ // Include settings.
+ //
+ string gp (ops.guard_prefix ());
+ if (!gp.empty () && gp[gp.size () - 1] != '_')
+ gp.append ("_");
+
+ // HXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (hxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ string guard (ctx->make_guard (gp + hxx_name));
+
+ hxx << "#ifndef " << guard << endl
+ << "#define " << guard << endl
+ << endl;
+
+ // Copy prologue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_prologue (),
+ ops.hxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ // Version check.
+ //
+ hxx << "#include <odb/version.hxx>" << endl
+ << endl
+ << "#if (ODB_VERSION != " << ODB_VERSION << "UL)" << endl
+ << "#error ODB runtime version mismatch" << endl
+ << "#endif" << endl
+ << endl;
+
+ hxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include main file(s).
+ //
+ for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i)
+ hxx << "#include " <<
+ ctx->process_include_path (i->leaf ().string ()) << endl;
+
+ hxx << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ if (include::generate (true))
+ hxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ header::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ header::generate ();
+ else
+ {
+ string n (base +
+ ops.odb_file_suffix ()[database::common] +
+ ops.hxx_suffix ());
+
+ ctx->os << "#include " << ctx->process_include_path (n) << endl
+ << endl;
+ }
+
+ relational::header::generate ();
+ break;
+ }
+ }
+ }
+
+ hxx << "#include " << ctx->process_include_path (ixx_name) << endl
+ << endl;
+
+ hxx << "#include <odb/post.hxx>" << endl
+ << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_epilogue (),
+ ops.hxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ hxx << "#endif // " << guard << endl;
+
+ if (ops.show_sloc ())
+ cerr << hxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // IXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (ixx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_prologue (),
+ ops.ixx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ inline_::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ inline_::generate ();
+
+ relational::inline_::generate ();
+ break;
+ }
+ }
+ }
+
+ // Copy epilogue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_epilogue (),
+ ops.ixx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << ixx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // CXX
+ //
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ unique_ptr<context> ctx (
+ create_context (cxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_prologue (),
+ ops.cxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ cxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include query columns implementations for explicit instantiations.
+ //
+ string impl_guard;
+ if (md == multi_database::dynamic && ctx->ext.empty ())
+ {
+ impl_guard = ctx->make_guard (
+ "ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
+
+ cxx << "#define " << impl_guard << endl;
+ }
+
+ cxx << "#include " << ctx->process_include_path (hxx_name) << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ include::generate (false);
+
+ if (!impl_guard.empty ())
+ cxx << "#undef " << impl_guard << endl;
+
+ cxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ // Dynamic multi-database support.
+ //
+ source::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::source::generate ();
+
+ if (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::embedded))
+ relational::schema::generate_source (changelog.get ());
+
+ break;
+ }
+ }
+ }
+
+ cxx << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_epilogue (),
+ ops.cxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << cxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SCH
+ //
+ if (gen_sep_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sch, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_prologue (),
+ ops.schema_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ sch << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ sch << "#include <odb/database.hxx>" << endl
+ << "#include <odb/schema-catalog-impl.hxx>" << endl
+ << endl
+ << "#include <odb/details/unused.hxx>" << endl
+ << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::schema::generate_source (changelog.get ());
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ sch << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_epilogue (),
+ ops.schema_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << sch_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SQL
+ //
+ if (gen_sql_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sql, unit, ops, fts, model.get ()));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (sql,
+ db,
+ ops.sql_prologue (),
+ ops.sql_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ if (!ops.omit_drop ())
+ relational::schema::generate_drop ();
+
+ // Interlude.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_interlude (),
+ ops.sql_interlude_file (),
+ "/* Begin interlude.\n */",
+ "/*\n * End interlude. */");
+
+ if (!ops.omit_create ())
+ relational::schema::generate_create ();
+
+ // Epilogue.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_epilogue (),
+ ops.sql_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // MIG
+ //
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ sema_rel::changeset& cs (
+ changelog->contains_changeset_at (i).changeset ());
+
+ // pre
+ //
+ {
+ ofstream& mig (*mig_pre[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_pre (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // post
+ //
+ {
+ ofstream& mig (*mig_post[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_post (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+ }
+ }
+
+ // Save the changelog if it has changed.
+ //
+ if (gen_changelog)
+ {
+ try
+ {
+ ostringstream os;
+ os.exceptions (ifstream::badbit | ifstream::failbit);
+ xml::serializer s (os, out_log_path.string ());
+ changelog->serialize (s);
+ string const& changelog_xml (os.str ());
+
+ if (changelog_xml != old_changelog_xml)
+ {
+ ofstream log;
+ open (log, out_log_path, ios_base::binary);
+
+ if (old_changelog == 0)
+ auto_rm.add (out_log_path);
+
+ log.exceptions (ifstream::badbit | ifstream::failbit);
+ log << changelog_xml;
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << out_log_path << ": write failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::serialization& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ // Communicate the sloc count to the driver. This is necessary to
+ // correctly handle the total if we are compiling multiple files in
+ // one invocation.
+ //
+ if (ops.show_sloc () || ops.sloc_limit_specified ())
+ cout << "odb:sloc:" << sloc_total << endl;
+
+ auto_rm.cancel ();
+ }
+ catch (operation_failed const&)
+ {
+ // Code generation failed. Diagnostics has already been issued.
+ //
+ throw generator_failed ();
+ }
+ catch (semantics::invalid_path const& e)
+ {
+ cerr << "error: '" << e.path () << "' is not a valid filesystem path"
+ << endl;
+ throw generator_failed ();
+ }
+ catch (fs::error const&)
+ {
+ // Auto-removal of generated files failed. Ignore it.
+ //
+ throw generator_failed ();
+ }
+}
diff --git a/odb/odb/generator.hxx b/odb/odb/generator.hxx
new file mode 100644
index 0000000..205043b
--- /dev/null
+++ b/odb/odb/generator.hxx
@@ -0,0 +1,22 @@
+// file : odb/generator.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GENERATOR_HXX
+#define ODB_GENERATOR_HXX
+
+#include <vector>
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+class generator_failed {};
+
+void
+generate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const& file,
+ std::vector<semantics::path> const& inputs);
+
+#endif // ODB_GENERATOR_HXX
diff --git a/odb/odb/header.cxx b/odb/odb/header.cxx
new file mode 100644
index 0000000..fad28b3
--- /dev/null
+++ b/odb/odb/header.cxx
@@ -0,0 +1,900 @@
+// file : odb/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+
+using namespace std;
+
+namespace header
+{
+ struct class1: traversal::class_, virtual context
+ {
+ class1 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> pointer_query_columns_type_;
+ };
+}
+
+void header::class1::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (context::optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // class_traits
+ //
+ os << "template <>" << endl
+ << "struct class_traits< " << type << " >"
+ << "{"
+ << "static const class_kind kind = class_object;"
+ << "};";
+
+ // object_traits
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // object_type & pointer_type
+ //
+ os << "typedef " << type << " object_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
+ << "typedef odb::pointer_traits<pointer_type> pointer_traits;"
+ << endl;
+
+ // polymorphic, root_type, base_type, etc.
+ //
+ os << "static const bool polymorphic = " << poly << ";"
+ << endl;
+
+ if (poly)
+ {
+ os << "typedef " << class_fq_name (*poly_root) << " root_type;";
+
+ if (poly_derived)
+ {
+ os << "typedef " << class_fq_name (*poly_base) << " base_type;"
+ << "typedef object_traits<root_type>::discriminator_type " <<
+ "discriminator_type;"
+ << "typedef polymorphic_concrete_info<root_type> info_type;";
+
+ if (abst)
+ os << "typedef polymorphic_abstract_info<root_type> " <<
+ "abstract_info_type;";
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ size_t depth (polymorphic_depth (c));
+
+ os << endl
+ << "static const std::size_t depth = " << depth << "UL;";
+ }
+ else
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*discriminator, hint));
+
+ os << "typedef " << t.fq_name (hint) << " discriminator_type;"
+ << "typedef polymorphic_map<object_type> map_type;"
+ << "typedef polymorphic_concrete_info<object_type> info_type;";
+
+ if (abst)
+ os << "typedef polymorphic_abstract_info<object_type> " <<
+ "abstract_info_type;";
+
+ os << endl
+ << "static const std::size_t depth = 1UL;";
+ }
+
+ os << endl;
+ }
+
+ // id_type, version_type, etc.
+ //
+ if (id != 0)
+ {
+ if (base_id)
+ {
+ semantics::class_& b (dynamic_cast<semantics::class_&> (idf->scope ()));
+ string const& type (class_fq_name (b));
+
+ os << "typedef object_traits< " << type << " >::id_type id_type;";
+
+ if (opt != 0)
+ os << "typedef object_traits< " << type << " >::version_type " <<
+ "version_type;";
+
+ os << endl;
+
+ if (poly_derived)
+ os << "static const bool auto_id = false;";
+ else
+ os << "static const bool auto_id = object_traits< " << type <<
+ " >::auto_id;";
+ }
+ else
+ {
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*id, hint));
+ os << "typedef " << t.fq_name (hint) << " id_type;";
+ }
+
+ if (opt != 0)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*opt, hint));
+ os << "typedef " << t.fq_name (hint) << " version_type;";
+ }
+
+ os << endl
+ << "static const bool auto_id = " << auto_id << ";";
+ }
+
+ os << endl;
+ }
+ else if (!reuse_abst)
+ {
+ // Object without id.
+ //
+ os << "typedef void id_type;"
+ << endl
+ << "static const bool auto_id = false;"
+ << endl;
+ }
+
+ // abstract
+ //
+ os << "static const bool abstract = " << abst << ";"
+ << endl;
+
+ // id()
+ //
+ if (id != 0 || !reuse_abst)
+ {
+ // We want to generate a dummy void id() accessor even if this
+ // object has no id to help us in the runtime. This way we can
+ // write generic code that will work for both void and non-void
+ // ids.
+ //
+ os << "static id_type" << endl
+ << "id (const object_type&);"
+ << endl;
+ }
+
+ // version()
+ //
+ if (opt != 0)
+ {
+ os << "static version_type" << endl
+ << "version (const object_type&);"
+ << endl;
+ }
+
+ // Query.
+ //
+ if (options.generate_query ())
+ {
+ // Generate object pointer tags here if we are generating dynamic
+ // multi-database support.
+ //
+ if (multi_dynamic && has_a (c, test_pointer | exclude_base))
+ {
+ query_tags t;
+ t.traverse (c);
+ }
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (!reuse_abst)
+ {
+ // Cache traits typedefs.
+ //
+ if (id == 0)
+ {
+ os << "typedef" << endl
+ << "no_id_pointer_cache_traits<pointer_type>" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "no_id_reference_cache_traits<object_type>" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else
+ {
+ char const* obj (poly_derived ? "root_type" : "object_type");
+ char const* ptr (poly_derived
+ ? "object_traits<root_type>::pointer_type"
+ : "pointer_type");
+ if (session (c))
+ {
+ string const& s (options.session_type ());
+
+ os << "typedef" << endl
+ << "odb::pointer_cache_traits<" << endl
+ << " " << ptr << "," << endl
+ << " " << s << " >" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "odb::reference_cache_traits<" << endl
+ << " " << obj << "," << endl
+ << " " << s << " >" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else
+ {
+ os << "typedef" << endl
+ << "no_op_pointer_cache_traits<" << ptr << ">" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "no_op_reference_cache_traits<" << obj << ">" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ }
+
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, object_type&, callback_event);"
+ << endl;
+
+ os << "static void" << endl
+ << "callback (database&, const object_type&, callback_event);"
+ << endl;
+ }
+
+ os << "};";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ // pointer_query_columns & query_columns
+ //
+ if (options.generate_query ())
+ {
+ // If we don't have object pointers, then also generate
+ // query_columns (in this case pointer_query_columns and
+ // query_columns are the same and the former inherits from
+ // the latter). Otherwise we have to postpone query_columns
+ // generation until the second pass to deal with forward-
+ // declared objects.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ pointer_query_columns_type_->traverse (c);
+ }
+
+ // object_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::object_traits< " << type << " >"
+ << "{";
+
+ // We don't need to generate anything else for reuse-abstract objects.
+ //
+ if (reuse_abst)
+ {
+ os << "};";
+ return;
+ }
+
+ os << "public:" << endl;
+
+ if (options.generate_query ())
+ {
+ // base_traits is needed for query support.
+ //
+ if (poly_derived)
+ os << "typedef object_traits_impl<base_type, id_common> base_traits;"
+ << endl;
+
+ // query_base_type
+ //
+ os << "typedef odb::query_base query_base_type;"
+ << endl;
+ }
+
+ // function_table_type
+ //
+ os << "struct function_table_type"
+ << "{";
+
+ // persist ()
+ //
+ os << "void (*persist) (database&, " << (auto_id ? "" : "const ") <<
+ "object_type&" << (poly ? ", bool, bool" : "") << ");";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "pointer_type (*find1) (database&, const id_type&);";
+
+ // find (id, obj)
+ //
+ os << "bool (*find2) (database&, const id_type&, object_type&" <<
+ (poly ? ", bool" : "") << ");";
+
+ // reload ()
+ //
+ os << "bool (*reload) (database&, object_type&" <<
+ (poly ? ", bool" : "") << ");";
+
+ // update ()
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "void (*update) (database&, const object_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+ }
+
+ // erase ()
+ //
+ os << "void (*erase1) (database&, const id_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+
+ os << "void (*erase2) (database&, const object_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "bool (*load_section) (connection&, object_type&, section&" <<
+ (poly ? ", const info_type*" : "") << ");";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "bool (*update_section) (connection&, const object_type&, " <<
+ "const section&" << (poly ? ", const info_type*" : "") << ");";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ os << "result<object_type> (*query) (database&, const query_base_type&);";
+
+ os << "unsigned long long (*erase_query) (database&, " <<
+ "const query_base_type&);";
+
+ if (options.generate_prepared ())
+ {
+ os << "odb::details::shared_ptr<prepared_query_impl> " <<
+ "(*prepare_query) (connection&, const char*, const query_base_type&);";
+
+ os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
+ "prepared_query_impl&);";
+ }
+ }
+
+ os << "};" // function_table_type
+ << "static const function_table_type* function_table[database_count];"
+ << endl;
+
+ //
+ // Forwarding functions.
+ //
+
+ // persist ()
+ //
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&);"
+ << endl;
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "static pointer_type" << endl
+ << "find (database&, const id_type&);"
+ << endl;
+
+ // find (id, obj)
+ //
+ os << "static bool" << endl
+ << "find (database&, const id_type&, object_type&);"
+ << endl;
+
+ // reload ()
+ //
+ os << "static bool" << endl
+ << "reload (database&, object_type&);"
+ << endl;
+
+ // update ()
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "static void" << endl
+ << "update (database&, const object_type&);"
+ << endl;
+ }
+
+ // erase ()
+ //
+ os << "static void" << endl
+ << "erase (database&, const id_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type&);"
+ << endl;
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "load (connection&, object_type&, section&);"
+ << endl;
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "update (connection&, const object_type&, const section&);"
+ << endl;
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ {
+ os << "static result<object_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+ }
+
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+ }
+
+ os << "};"; // object_traits_impl
+}
+
+void header::class1::
+traverse_view (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // class_traits
+ //
+ os << "template <>" << endl
+ << "struct class_traits< " << type << " >"
+ << "{"
+ << "static const class_kind kind = class_view;"
+ << "};";
+
+ // view_traits
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // view_type & pointer_type
+ //
+ os << "typedef " << type << " view_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
+ << endl;
+
+ // Generate associated object tags here if we are generating dynamic
+ // multi-database support.
+ //
+ if (multi_dynamic)
+ {
+ query_tags t;
+ t.traverse (c);
+ }
+
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, view_type&, callback_event);"
+ << endl;
+
+ os << "};";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ size_t obj_count (c.get<size_t> ("object-count"));
+
+ // view_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // query_base_type and query_columns (definition generated by class2).
+ //
+ os << "typedef odb::query_base query_base_type;"
+ << "struct query_columns";
+
+ if (obj_count == 0)
+ os << "{"
+ << "};";
+ else
+ os << ";"
+ << endl;
+
+ // function_table_type
+ //
+ os << "struct function_table_type"
+ << "{";
+
+ if (!options.omit_unprepared ())
+ os << "result<view_type> (*query) (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "odb::details::shared_ptr<prepared_query_impl> " <<
+ "(*prepare_query) (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
+ "prepared_query_impl&);"
+ << endl;
+ }
+
+ os << "};" // function_table_type
+ << "static const function_table_type* function_table[database_count];"
+ << endl;
+
+ //
+ // Forwarding functions.
+ //
+
+ if (!options.omit_unprepared ())
+ os << "static result<view_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+
+ os << "};";
+}
+
+namespace header
+{
+ struct class2: traversal::class_, virtual context
+ {
+ class2 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> query_columns_type_inst_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+}
+
+void header::class2::
+traverse_object (type& c)
+{
+ if (options.generate_query ())
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // query_columns
+ //
+ // If we don't have any pointers, then query_columns is generated
+ // in pass 1 (see the comment in class1 for details).
+ //
+ if (has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ // Generate extern template declarations.
+ //
+ query_columns_type_inst_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+}
+
+void header::class2::
+traverse_view (type& c)
+{
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ view_query_columns_type_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+}
+
+namespace header
+{
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ os << "#include <memory>" << endl
+ << "#include <cstddef>" << endl; // std::size_t
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <string>" << endl; // For discriminator.
+
+ if (ctx.options.std () >= cxx_version::cxx11)
+ os << "#include <utility>" << endl; // move()
+
+ os << endl;
+
+ os << "#include <odb/core.hxx>" << endl
+ << "#include <odb/traits.hxx>" << endl
+ << "#include <odb/callback.hxx>" << endl
+ << "#include <odb/wrapper-traits.hxx>" << endl
+ << "#include <odb/pointer-traits.hxx>" << endl;
+
+#ifndef ODB_BUILD2
+ if (ctx.options.std () == cxx_version::cxx98)
+ {
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost::shared_ptr and std::tr1::shared_ptr usage since
+ // the latter is just a using-declaration for the former. To resolve
+ // this we will include TR1 traits if the Boost TR1 header is included.
+ //
+ if (ctx.features.tr1_pointer)
+ {
+ os << "#include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "#include <odb/tr1/pointer-traits.hxx>" << endl;
+ }
+ else if (ctx.features.boost_pointer)
+ {
+ os << "#ifdef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
+ << "# include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "# include <odb/tr1/pointer-traits.hxx>" << endl
+ << "#endif" << endl;
+ }
+ }
+#endif
+
+ os << "#include <odb/container-traits.hxx>" << endl;
+
+ if (ctx.features.session_object)
+ {
+ if (ctx.options.session_type () == "odb::session")
+ os << "#include <odb/session.hxx>" << endl;
+
+ os << "#include <odb/cache-traits.hxx>" << endl;
+ }
+ else
+ os << "#include <odb/no-op-cache-traits.hxx>" << endl;
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-info.hxx>" << endl;
+
+ if (ctx.options.generate_query ())
+ {
+ if (ctx.multi_dynamic)
+ os << "#include <odb/query-dynamic.hxx>" << endl;
+
+ if (ctx.options.generate_prepared ())
+ os << "#include <odb/prepared-query.hxx>" << endl;
+
+ os << "#include <odb/result.hxx>" << endl;
+
+ if (ctx.features.simple_object)
+ os << "#include <odb/simple-object-result.hxx>" << endl;
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-object-result.hxx>" << endl;
+
+ if (ctx.features.no_id_object)
+ os << "#include <odb/no-id-object-result.hxx>" << endl;
+
+ if (ctx.features.view)
+ os << "#include <odb/view-image.hxx>" << endl
+ << "#include <odb/view-result.hxx>" << endl;
+ }
+
+ os << endl
+ << "#include <odb/details/unused.hxx>" << endl;
+
+ if (ctx.options.generate_query ())
+ os << "#include <odb/details/shared-ptr.hxx>" << endl;
+
+ os << endl;
+
+ os << "namespace odb"
+ << "{";
+
+ // Generate common code.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class1 c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ if (ctx.multi_dynamic)
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class2 c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ os << "}";
+ }
+}
diff --git a/odb/odb/include.cxx b/odb/odb/include.cxx
new file mode 100644
index 0000000..5fda7c0
--- /dev/null
+++ b/odb/odb/include.cxx
@@ -0,0 +1,738 @@
+// file : odb/include.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <set>
+#include <map>
+#include <locale>
+#include <cassert>
+#include <fstream>
+#include <sstream>
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+
+#include <iostream>
+
+using namespace std;
+using semantics::path;
+
+namespace
+{
+ struct include_directive
+ {
+ enum type { quote, bracket };
+
+ type type_;
+ path path_;
+ };
+
+#if BUILDING_GCC_MAJOR >= 6
+ typedef line_map_ordinary line_map_type;
+#else
+ typedef line_map line_map_type;
+# ifndef linemap_check_ordinary
+# define linemap_check_ordinary(X) (X)
+# endif
+#endif
+
+ struct includes
+ {
+ typedef std::map<line_map_type const*, include_directive> map_type;
+ bool trailing; // Included at the beginning or at the end of the main file.
+ map_type map;
+ };
+ typedef std::map<path, includes> include_map;
+
+ // Map of files to the lines which contain include directives
+ // that we are interested in.
+ //
+ typedef std::map<size_t, include_directive*> include_lines;
+ typedef std::map<string, include_lines> file_map;
+
+ // Set of include directives sorted in the preference order.
+ //
+ struct include_comparator
+ {
+ bool
+ operator() (include_directive const* x, include_directive const* y) const
+ {
+ // Prefer <> over "".
+ //
+ if (x->type_ != y->type_)
+ return x->type_ < y->type_;
+
+ // Otherwise, prefer longer (more qualified) paths over the
+ // shorter ones.
+ //
+ return x->path_.string ().size () < y->path_.string ().size ();
+ }
+ };
+
+ typedef
+ std::multiset<include_directive const*, include_comparator>
+ include_set;
+
+ struct class_: traversal::class_, context
+ {
+ class_ (include_map& map)
+ : typedefs_ (true), main_file_loc_ (0), map_ (map)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other)
+ return;
+
+ names (c); // Check nested classes.
+
+ // We only generate things for objects and composite value types. In
+ // particular, we don't care about views since they cannot be used in
+ // definitions of other views, objects, or composite values.
+ //
+ if (ck != class_object && ck != class_composite)
+ return;
+
+ // Not interested in classes that we are generating.
+ //
+ // If we have an explicit definition location, use that. Otherwise,
+ // if this is a class template instantiation, then get the file
+ // corresponding to the pragma, not the instantiation itself,
+ // since that's where we are generation the code for this class.
+ // While at it, also get the location.
+ //
+ using semantics::path;
+
+ path f;
+ location_t l;
+
+ // Pretty much the same code as in context::class_location().
+ //
+ if (c.count ("definition"))
+ {
+ l = c.get<location_t> ("definition");
+ f = path (LOCATION_FILE (l));
+ }
+ else if (c.is_a<semantics::class_instantiation> ())
+ {
+ l = c.get<location_t> ("location");
+ f = path (LOCATION_FILE (l));
+ }
+ else
+ {
+ f = c.file ();
+ tree decl (TYPE_NAME (c.tree_node ()));
+ l = real_source_location (decl);
+
+ // Any include directives that follow are trailing (specified at
+ // the end of the main file). Note that we ignore views in this
+ // test so if a file defines only views, then all includes will
+ // be treated as leading. This is ok since views cannot have
+ // circular dependencies. We also ignore overridden locations for
+ // the purpose of this test since they are not really in the file
+ // being compiled. We assume that any includes that come after
+ // such classes are still leading.
+ //
+ if (f == unit.file ())
+ {
+ if (main_file_loc_ == 0)
+ main_file_loc_ = l;
+ return;
+ }
+ }
+
+ // This is a persistent object or composite value type declared in
+ // another header file. Include its -odb header.
+ //
+ if (l > BUILTINS_LOCATION)
+ {
+ line_map_type const* lm (
+ linemap_check_ordinary (
+ linemap_lookup (line_table, l)));
+
+ if (lm != 0 && !MAIN_FILE_P (lm))
+ {
+ lm = INCLUDED_FROM (line_table, lm);
+
+ f.complete ();
+ f.normalize ();
+
+ if (map_.find (f) == map_.end ())
+ {
+ includes& i (map_[f]);
+ i.trailing = (main_file_loc_ != 0 && l > main_file_loc_);
+ i.map[lm] = include_directive ();
+ }
+ }
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ location_t main_file_loc_;
+ include_map& map_;
+ };
+
+ class include_parser
+ {
+ public:
+ include_parser (options const& options)
+ : loc_ ("C"), options_ (options)
+ {
+ }
+
+ void
+ parse_file (string const& file, include_lines& lines)
+ {
+ string f (file);
+ size_t n (f.size ());
+ database db (options_.database ()[0]);
+
+ // Check if we have a synthesized prologue/epilogue fragment.
+ //
+ if (n != 0 && f[0] == '<' && f[n - 1] == '>')
+ {
+ size_t p (f.rfind ('-'));
+
+ if (p != string::npos)
+ {
+ string name (f, 1, p - 1);
+
+ if (name == "odb-prologue" || name == "odb-epilogue")
+ {
+ // Extract the fragment number.
+ //
+ {
+ istringstream istr (string (f, p + 1));
+ istr >> n;
+ }
+
+ n--; // Prologues/epilogues are counted from 1.
+
+ stringstream ss;
+ f.clear ();
+
+ // We don't need the #line part.
+ //
+ if (name == "odb-prologue")
+ {
+ size_t size (options_.odb_prologue ().size ());
+
+ if (n < size)
+ ss << options_.odb_prologue ()[db][n];
+ else
+ f = options_.odb_prologue_file ()[db][n - size];
+ }
+ else
+ {
+ size_t size (options_.odb_epilogue ().size ());
+
+ if (n < size)
+ ss << options_.odb_epilogue ()[db][n];
+ else
+ f = options_.odb_epilogue_file ()[db][n - size];
+ }
+
+ if (f.empty ())
+ {
+ parse_stream (ss, file, lines);
+ return;
+ }
+ // Otherwise use the code below to parse the file.
+ }
+ }
+ }
+
+ ifstream is (f.c_str ());
+
+ if (!is.is_open ())
+ {
+ cerr << "error: unable to open '" << f << "' in read mode" << endl;
+ throw operation_failed ();
+ }
+
+ parse_stream (is, f, lines);
+ }
+
+ void
+ parse_stream (istream& is, string const& name, include_lines& lines)
+ {
+ typedef char_traits<char>::int_type int_type;
+
+ size_t lmax (lines.rbegin ()->first);
+
+ string line;
+ bool bslash (false);
+ size_t lb (1), le (1);
+ bool eof (false);
+
+ for (int_type c (is.get ()); !eof; c = is.get ())
+ {
+ if (is.fail ())
+ {
+ if (is.eof ())
+ {
+ // If we are still in the range, treat this as the last newline.
+ //
+ c = '\n';
+ eof = true;
+ }
+ else
+ break; // Some other failure -- bail out.
+ }
+
+ if (c == '\n')
+ {
+ le++;
+
+ if (!bslash)
+ {
+ //cerr << "line: " << lb << "-" << (le - 1) << " " << line << endl;
+
+ // See if we are interested in this range of physical lines.
+ //
+ include_lines::iterator li (lines.lower_bound (lb));
+ include_lines::iterator ui (lines.upper_bound (le - 1));
+
+ // We should have at most one entry per logical line.
+ //
+ for (; li != ui; ++li)
+ {
+ if (li->first >= lb && li->first <= (le - 1))
+ {
+ if (!parse_line (line, *li->second))
+ {
+ cerr << name << ":" << lb << ":1: error: "
+ << "unable to parse #include directive" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ if (le > lmax)
+ break;
+
+ lb = le;
+ line.clear ();
+ }
+
+ bslash = false;
+ continue;
+ }
+
+ if (bslash)
+ {
+ line += '\\';
+ bslash = false;
+ }
+
+ if (c == '\\')
+ bslash = true;
+ else
+ {
+ line += char (c);
+ }
+ }
+
+ if (is.bad () || (is.fail () && !is.eof ()))
+ {
+ cerr << "error: input error while reading '" << name << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ bool
+ parse_line (string const& l, include_directive& inc)
+ {
+ enum state
+ {
+ start_hash,
+ start_keyword,
+ parse_keyword,
+ start_path,
+ parse_path,
+ parse_done
+ };
+
+ bool com (false); // In C-style comment.
+ string lex;
+ char path_end ('\0');
+ state s (start_hash);
+
+ for (size_t i (0), n (l.size ()); i < n; ++i)
+ {
+ char c (l[i]);
+
+ if (com)
+ {
+ if (c == '*' && (i + 1) < n && l[i + 1] == '/')
+ {
+ ++i;
+ com = false;
+ c = ' '; // Replace a comment with a single space.
+ }
+ else
+ continue;
+ }
+
+ // We only ignore spaces in start states.
+ //
+ if (is_space (c))
+ {
+ switch (s)
+ {
+ case start_hash:
+ case start_keyword:
+ case start_path:
+ {
+ continue;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // C comment can be anywhere except in the path.
+ //
+ if (s != parse_path && c == '/' && (i + 1) < n && l[i + 1] == '*')
+ {
+ ++i;
+ com = true;
+ continue;
+ }
+
+ switch (s)
+ {
+ case start_hash:
+ {
+ if (c != '#')
+ return false;
+
+ s = start_keyword;
+ break;
+ }
+ case start_keyword:
+ {
+ lex.clear ();
+ s = parse_keyword;
+ }
+ // Fall through.
+ case parse_keyword:
+ {
+ if (is_alpha (c))
+ {
+ lex += c;
+ break;
+ }
+
+ if (lex != "include")
+ return false;
+
+ s = start_path;
+ --i; // Re-parse the same character again.
+ break;
+ }
+ case start_path:
+ {
+ if (c == '"')
+ {
+ path_end = '"';
+ inc.type_ = include_directive::quote;
+ }
+ else if (c == '<')
+ {
+ path_end = '>';
+ inc.type_ = include_directive::bracket;
+ }
+ else
+ return false;
+
+ lex.clear ();
+ s = parse_path;
+ break;
+ }
+ case parse_path:
+ {
+ if (c != path_end)
+ lex += c;
+ else
+ s = parse_done;
+
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ if (s == parse_done)
+ break;
+ }
+
+ if (s != parse_done)
+ return false;
+
+ inc.path_ = path (lex);
+ return true;
+ }
+
+ private:
+ bool
+ is_alpha (char c) const
+ {
+ return isalpha (c, loc_);
+ }
+
+ bool
+ is_space (char c) const
+ {
+ return isspace (c, loc_);
+ }
+
+ private:
+ std::locale loc_;
+ options const& options_;
+ };
+
+ bool
+ generate_impl (bool header)
+ {
+ bool r (false);
+
+ // We do the same include directive collection and processing
+ // twice, once for the header file and once for the source file.
+ // If that proves to be too slow, we will need to do it only once
+ // and cache the result.
+ //
+ context ctx;
+ include_map imap;
+
+ // Collect all the files that we need to include.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c (imap);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ // Add all the known include locations for each file in the map.
+ //
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ size_t used (line_table->used);
+ line_map_type const* maps (line_table->maps);
+#else
+ size_t used (line_table->info_ordinary.used);
+ line_map_type const* maps (line_table->info_ordinary.maps);
+#endif
+
+ for (size_t i (0); i < used; ++i)
+ {
+ line_map_type const* m (maps + i);
+
+ if (MAIN_FILE_P (m) || m->reason != LC_ENTER)
+ continue;
+
+ line_map_type const* ifm (INCLUDED_FROM (line_table, m));
+
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ path f (m->to_file);
+#else
+ path f (ORDINARY_MAP_FILE_NAME (m));
+#endif
+
+ f.complete ();
+ f.normalize ();
+
+ include_map::iterator it (imap.find (f));
+
+ if (it != imap.end ())
+ it->second.map[ifm] = include_directive ();
+ }
+
+ //
+ //
+ file_map fmap;
+
+ for (include_map::iterator i (imap.begin ()), e (imap.end ()); i != e; ++i)
+ {
+ // Note that the LAST_SOURCE_LINE value of a map that includes another
+ // map is the line of that include.
+
+ /*
+ cerr << endl
+ << i->first << " included from" << endl;
+
+ for (includes::iterator j (i->second.begin ());
+ j != i->second.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+ cerr << '\t' << lm->to_file << ":" << LAST_SOURCE_LINE (lm) << endl;
+ }
+ */
+
+ // First see if there is an include from the main file. If so, then
+ // it is preferred over all others. Use the first one if there are
+ // several.
+ //
+ line_map_type const* main_lm (0);
+ include_directive* main_inc (0);
+
+ for (includes::map_type::iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+
+ if (MAIN_FILE_P (lm))
+ {
+ if (main_lm == 0 ||
+ LAST_SOURCE_LINE (main_lm) > LAST_SOURCE_LINE (lm))
+ {
+ main_lm = lm;
+ main_inc = &j->second;
+ }
+ }
+ }
+
+ if (main_lm != 0)
+ {
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ string f (main_lm->to_file);
+#else
+ string f (ORDINARY_MAP_FILE_NAME (main_lm));
+#endif
+ size_t n (f.size ());
+
+ // Check if this is a synthesized fragment.
+ //
+ if (!(n != 0 && f[0] == '<' && f[n - 1] == '>'))
+ {
+ path p (f);
+ p.complete ();
+ p.normalize ();
+ f = p.string ();
+ }
+
+ fmap[f][LAST_SOURCE_LINE (main_lm)] = main_inc;
+ continue;
+ }
+
+ // Otherwise, add all the entries.
+ //
+ for (includes::map_type::iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ string f (lm->to_file);
+#else
+ string f (ORDINARY_MAP_FILE_NAME (lm));
+#endif
+ size_t n (f.size ());
+
+ // Check if this is a synthesized fragment.
+ //
+ if (!(n != 0 && f[0] == '<' && f[n - 1] == '>'))
+ {
+ path p (f);
+ p.complete ();
+ p.normalize ();
+ f = p.string ();
+ }
+
+ fmap[f][LAST_SOURCE_LINE (lm)] = &j->second;
+ }
+ }
+
+ // Parse the collected include directives.
+ //
+ include_parser ip (ctx.options);
+
+ for (file_map::iterator i (fmap.begin ()), e (fmap.end ()); i != e; ++i)
+ {
+ ip.parse_file (i->first, i->second);
+ }
+
+ // Finally, output the include directives.
+ //
+ for (include_map::const_iterator i (imap.begin ()), e (imap.end ());
+ i != e; ++i)
+ {
+ includes const& is (i->second);
+
+ // In header we generate only leading includes. In source -- only
+ // trailing.
+ //
+ if (header == is.trailing)
+ continue;
+
+ include_directive const* inc (0);
+
+ if (is.map.size () == 1)
+ {
+ inc = &is.map.begin ()->second;
+ }
+ else
+ {
+ include_set set;
+
+ for (includes::map_type::const_iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ if (!j->second.path_.empty ())
+ set.insert (&j->second);
+ }
+
+ assert (set.size () > 0);
+ inc = *set.rbegin ();
+ }
+
+ path f (inc->path_.base ());
+ f += ctx.options.odb_file_suffix ()[ctx.options.database ()[0]];
+ f += ctx.options.hxx_suffix ();
+
+ char o (inc->type_ == include_directive::quote ? '"' : '<');
+ ctx.os << "#include " << ctx.process_include_path (
+ f.string (), false, o) << endl;
+ r = true;
+ }
+
+ return r;
+ }
+}
+
+namespace include
+{
+ bool
+ generate (bool header) {return generate_impl (header);}
+}
diff --git a/odb/odb/inline.cxx b/odb/odb/inline.cxx
new file mode 100644
index 0000000..15482aa
--- /dev/null
+++ b/odb/odb/inline.cxx
@@ -0,0 +1,506 @@
+// file : odb/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+#include <odb/diagnostics.hxx>
+
+using namespace std;
+
+namespace inline_
+{
+ //
+ //
+ struct callback_calls: traversal::class_, virtual context
+ {
+ callback_calls ()
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ void
+ traverse (type& c, bool constant)
+ {
+ const_ = constant;
+ traverse (c);
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases.
+ //
+ if (!(obj || view (c)))
+ return;
+
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+
+ // In case of the const instance, we only generate the call if
+ // there is a const callback. Note also that we cannot use
+ // object_type/view_type alias because it can be a base type.
+ //
+ string const& type (class_fq_name (c));
+
+ if (const_)
+ {
+ if (c.count ("callback-const"))
+ os << "static_cast<const " << type << "&> (x)." << name <<
+ " (e, db);";
+ }
+ else
+ os << "static_cast< " << type << "&> (x)." << name << " (e, db);";
+ }
+ else if (obj)
+ inherits (c);
+ }
+
+ protected:
+ bool const_;
+ traversal::inherits inherits_;
+ };
+
+ struct class_: traversal::class_, virtual context
+ {
+ class_ ()
+ : typedefs_ (false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ callback_calls callback_calls_;
+ };
+}
+
+void inline_::class_::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (context::optimistic (c));
+
+ // Base class that contains the object id.
+ //
+ type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
+
+ bool poly (polymorphic (c));
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits< " + type + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // id (object_type)
+ //
+ if (id != 0 || !reuse_abst)
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const object_type&" << (id != 0 ? " o" : "") << ")"
+ << "{";
+
+ if (id != 0)
+ {
+ if (base_id)
+ os << "return object_traits< " << class_fq_name (*base) <<
+ " >::id (o);";
+ else
+ {
+ // Get the id using the accessor expressions.
+ //
+ string r ("o");
+
+ for (data_member_path::const_iterator b (id->begin ()), i (b);
+ i != id->end ();
+ ++i)
+ {
+ member_access& ma ((*i)->get<member_access> ("get"));
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ r = ma.translate (r);
+ }
+
+ os << "return " << r << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ if (opt != 0)
+ {
+ os << "inline" << endl
+ << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const object_type& o)"
+ << "{";
+
+ if (base_id)
+ os << "return object_traits< " << class_fq_name (*base) <<
+ " >::version (o);";
+ else
+ {
+ // Get the id using the accessor expression. If this is not
+ // a synthesized expression, then output its location for
+ // easier error tracking.
+ //
+ member_access& ma (opt->get<member_access> ("get"));
+
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "return " << ma.translate ("o") << ";";
+ }
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ // callback ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, object_type& x, callback_event e)"
+ << endl
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (x);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_.traverse (c, false);
+ os << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, const object_type& x, callback_event e)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (x);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_.traverse (c, true);
+ os << "}";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ traits = "access::object_traits_impl< " + type + ", id_common >";
+
+ //
+ // Forwarding functions.
+ //
+
+ // persist ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "persist (database& db, " << (auto_id ? "" : "const ") <<
+ "object_type& o)"
+ << "{"
+ << "function_table[db.id ()]->persist (db, o" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ {
+ os << "inline" << endl
+ << traits << "::pointer_type" << endl
+ << traits << "::" << endl
+ << "find (database& db, const id_type& id)"
+ << "{"
+ << "return function_table[db.id ()]->find1 (db, id);"
+ << "}";
+ }
+
+ // find (id, obj)
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "find (database& db, const id_type& id, object_type& o)"
+ << "{"
+ << "return function_table[db.id ()]->find2 (db, id, o" <<
+ (poly ? ", true" : "") << ");"
+ << "}";
+
+ // reload ()
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "reload (database& db, object_type& o)"
+ << "{"
+ << "return function_table[db.id ()]->reload (db, o" <<
+ (poly ? ", true" : "") << ");"
+ << "}";
+
+ // update ()
+ //
+ // In case of a polymorphic object, we generate update() even if it is
+ // readonly since the potentially-readwrite base will rely on it to
+ // initialize the id image.
+ //
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "update (database& db, const object_type& o)"
+ << "{"
+ << "function_table[db.id ()]->update (db, o" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+ }
+
+ // erase ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const id_type& id)"
+ << "{"
+ << "function_table[db.id ()]->erase1 (db, id" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& o)"
+ << "{"
+ << "function_table[db.id ()]->erase2 (db, o" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "load (connection& c, object_type& o, section& s)"
+ << "{"
+ << "return function_table[c.database ().id ()]->load_section (" <<
+ "c, o, s" << (poly ? ", 0" : "") << ");"
+ << "}";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "update (connection& c, const object_type& o, const section& s)"
+ << "{"
+ << "return function_table[c.database ().id ()]->update_section (" <<
+ "c, o, s" << (poly ? ", 0" : "") << ");"
+ << "}";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ {
+ os << "inline" << endl
+ << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "return function_table[db.id ()]->query (db, q);"
+ << "}";
+ }
+
+ os << "inline" << endl
+ << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const query_base_type& q)"
+ << "{"
+ << "return function_table[db.id ()]->erase_query (db, q);"
+ << "}";
+
+ if (options.generate_prepared ())
+ {
+ os << "inline" << endl
+ << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "return function_table[c.database ().id ()]->prepare_query (" <<
+ "c, n, q);"
+ << "}";
+
+ os << "inline" << endl
+ << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& pq)"
+ << "{"
+ << "return function_table[pq.conn.database ().id ()]->" <<
+ "execute_query (pq);"
+ << "}";
+ }
+ }
+}
+
+void inline_::class_::
+traverse_view (type& c)
+{
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits< " + type + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // callback ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, view_type& x, callback_event e)"
+ << endl
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (x);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_.traverse (c, false);
+ os << "}";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ traits = "access::view_traits_impl< " + type + ", id_common >";
+
+ //
+ // Forwarding functions.
+ //
+
+ if (!options.omit_unprepared ())
+ {
+ os << "inline" << endl
+ << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "return function_table[db.id ()]->query (db, q);"
+ << "}";
+ }
+
+ if (options.generate_prepared ())
+ {
+ os << "inline" << endl
+ << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "return function_table[c.database ().id ()]->prepare_query (" <<
+ "c, n, q);"
+ << "}";
+
+ os << "inline" << endl
+ << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& pq)"
+ << "{"
+ << "return function_table[pq.conn.database ().id ()]->" <<
+ "execute_query (pq);"
+ << "}";
+ }
+}
+
+namespace inline_
+{
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ if (ctx.multi_dynamic)
+ os << "#include <odb/database.hxx>" << endl
+ << endl;
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+}
diff --git a/odb/odb/instance.cxx b/odb/odb/instance.cxx
new file mode 100644
index 0000000..2d10239
--- /dev/null
+++ b/odb/odb/instance.cxx
@@ -0,0 +1,73 @@
+// file : odb/instance.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+#include <cstdlib> // abort
+#include <cxxabi.h> // abi::__cxa_demangle
+
+#include <odb/instance.hxx>
+
+using namespace std;
+
+struct demangled_name
+{
+ demangled_name (): s (0), n (0) {}
+ ~demangled_name () {free (s);}
+ char* s;
+ size_t n;
+};
+
+static demangled_name name_;
+
+string entry_base::
+name (type_info const& ti)
+{
+ char*& s (name_.s);
+
+ {
+ int r;
+ s = abi::__cxa_demangle (ti.name (), s, &name_.n, &r);
+
+ if (r != 0)
+ abort (); // We are in static initialization, so this is fatal.
+ }
+
+ string str (s), r;
+
+ // Get the first component. It can be a database kind or name.
+ //
+ string::size_type p (str.find ("::"));
+
+ if (p == string::npos)
+ abort (); // Derived type should be in a namespace.
+
+ string n (str, 0, p);
+
+ // See if it is one of the known kinds.
+ //
+ if (n == "relational")
+ {
+ r = n;
+ p = str.find ("::", 12); // 12 for "relational::"
+ n.assign (str, 12, p == string::npos ? p : p - 12);
+ }
+
+ // See if it is one of the known databases.
+ //
+ database db;
+ istringstream is (n);
+ if (!(is >> db))
+ {
+ if (r.empty ())
+ abort (); // Has to have either kind or database.
+ }
+ else
+ {
+ if (!r.empty ())
+ r += "::";
+
+ r += n;
+ }
+
+ return r;
+}
diff --git a/odb/odb/instance.hxx b/odb/odb/instance.hxx
new file mode 100644
index 0000000..2b47939
--- /dev/null
+++ b/odb/odb/instance.hxx
@@ -0,0 +1,295 @@
+// file : odb/instance.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_INSTANCE_HXX
+#define ODB_INSTANCE_HXX
+
+#include <map>
+#include <string>
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/option-types.hxx>
+#include <odb/context.hxx>
+
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+//
+// Dynamic traversal instantiation support.
+//
+
+template <typename B>
+struct factory
+{
+ static B*
+ create (B const& prototype)
+ {
+ std::string kind, name;
+ database db (context::current ().options.database ()[0]);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ name = "common";
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ kind = "relational";
+ name = kind + "::" + db.string ();
+ break;
+ }
+ }
+
+ if (map_ != 0)
+ {
+ typename map::const_iterator i;
+
+ if (!name.empty ())
+ i = map_->find (name);
+
+ if (i == map_->end ())
+ i = map_->find (kind);
+
+ if (i != map_->end ())
+ return i->second (prototype);
+ }
+
+ return new B (prototype);
+ }
+
+private:
+ template <typename>
+ friend struct entry;
+
+ static void
+ init ()
+ {
+ if (factory<B>::count_++ == 0)
+ factory<B>::map_ = new typename factory<B>::map;
+ }
+
+ static void
+ term ()
+ {
+ if (--factory<B>::count_ == 0)
+ delete factory<B>::map_;
+ }
+
+ typedef B* (*create_func) (B const&);
+ typedef std::map<std::string, create_func> map;
+ static map* map_;
+ static std::size_t count_;
+};
+
+template <typename B>
+typename factory<B>::map* factory<B>::map_;
+
+template <typename B>
+std::size_t factory<B>::count_;
+
+struct entry_base
+{
+ static std::string
+ name (std::type_info const&);
+};
+
+template <typename D>
+struct entry: entry_base
+{
+ typedef typename D::base base;
+
+ entry ()
+ {
+ factory<base>::init ();
+ (*factory<base>::map_)[name (typeid (D))] = &create;
+ }
+
+ ~entry ()
+ {
+ factory<base>::term ();
+ }
+
+ static base*
+ create (base const& prototype)
+ {
+ return new D (prototype);
+ }
+};
+
+template <typename B>
+struct instance
+{
+ typedef typename B::base base_type;
+ typedef ::factory<base_type> factory_type;
+
+ ~instance ()
+ {
+ delete x_;
+ }
+
+ instance ()
+ {
+ base_type prototype;
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1>
+ instance (A1& a1)
+ {
+ base_type prototype (a1);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1>
+ instance (A1 const& a1)
+ {
+ base_type prototype (a1);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2>
+ instance (A1& a1, A2& a2)
+ {
+ base_type prototype (a1, a2);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2>
+ instance (A1 const& a1, A2 const& a2)
+ {
+ base_type prototype (a1, a2);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3>
+ instance (A1& a1, A2& a2, A3& a3)
+ {
+ base_type prototype (a1, a2, a3);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ base_type prototype (a1, a2, a3);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4)
+ {
+ base_type prototype (a1, a2, a3, a4);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4)
+ {
+ base_type prototype (a1, a2, a3, a4);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5)
+ {
+ base_type prototype (a1, a2, a3, a4, a5);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4,
+ A5 const& a5)
+ {
+ base_type prototype (a1, a2, a3, a4, a5);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5, A6 a6)
+ {
+ base_type prototype (a1, a2, a3, a4, a5, a6);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4,
+ A5 const& a5, A6 const& a6)
+ {
+ base_type prototype (a1, a2, a3, a4, a5, a6);
+ x_ = factory_type::create (prototype);
+ }
+
+ instance (instance const& i)
+ {
+ // This is tricky: use the other instance as a prototype.
+ //
+ x_ = factory_type::create (*i.x_);
+ }
+
+ base_type*
+ operator-> () const
+ {
+ return x_;
+ }
+
+ base_type&
+ operator* () const
+ {
+ return *x_;
+ }
+
+ base_type*
+ get () const
+ {
+ return x_;
+ }
+
+private:
+ instance& operator= (instance const&);
+
+private:
+ base_type* x_;
+};
+
+template <typename T>
+inline traversal::edge_base&
+operator>> (instance<T>& n, traversal::edge_base& e)
+{
+ n->edge_traverser (e);
+ return e;
+}
+
+template <typename T>
+inline traversal::relational::edge_base&
+operator>> (instance<T>& n, traversal::relational::edge_base& e)
+{
+ n->edge_traverser (e);
+ return e;
+}
+
+template <typename T>
+inline traversal::node_base&
+operator>> (traversal::edge_base& e, instance<T>& n)
+{
+ e.node_traverser (*n);
+ return *n;
+}
+
+template <typename T>
+inline traversal::relational::node_base&
+operator>> (traversal::relational::edge_base& e, instance<T>& n)
+{
+ e.node_traverser (*n);
+ return *n;
+}
+
+#endif // ODB_INSTANCE_HXX
diff --git a/odb/odb/location.cxx b/odb/odb/location.cxx
new file mode 100644
index 0000000..23bd7ef
--- /dev/null
+++ b/odb/odb/location.cxx
@@ -0,0 +1,13 @@
+// file : odb/location.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/location.hxx>
+#include <odb/diagnostics.hxx>
+
+location::
+location (location_t l)
+ : file (location_file (l)),
+ line (location_line (l)),
+ column (location_column (l))
+{
+}
diff --git a/odb/odb/location.hxx b/odb/odb/location.hxx
new file mode 100644
index 0000000..cc59196
--- /dev/null
+++ b/odb/odb/location.hxx
@@ -0,0 +1,25 @@
+// file : odb/location.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_LOCATION_HXX
+#define ODB_LOCATION_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <cstddef>
+#include <libcutl/fs/path.hxx>
+
+struct location
+{
+ location (location_t);
+ location (cutl::fs::path const& f, std::size_t l, std::size_t c)
+ : file (f), line (l), column (c)
+ {
+ }
+
+ cutl::fs::path file;
+ std::size_t line;
+ std::size_t column;
+};
+
+#endif // ODB_LOCATION_HXX
diff --git a/odb/odb/lookup.cxx b/odb/odb/lookup.cxx
new file mode 100644
index 0000000..a54ef15
--- /dev/null
+++ b/odb/odb/lookup.cxx
@@ -0,0 +1,374 @@
+// file : odb/lookup.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/lookup.hxx>
+#include <odb/semantics.hxx>
+
+using namespace std;
+
+namespace lookup
+{
+ // Return canonical fundamental type name (<length> <sign> <type>, e.g.,
+ // short unsigned int) or empty string if it is not a fundamental type.
+ // Note that the type can still be invalid (e.g., unsigned float) so it
+ // needs to be resolved.
+ //
+ static string
+ parse_fundamental (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ string& name)
+ {
+ bool
+ si (false), // signed
+ un (false), // unsigned
+ sh (false), // short
+ lo (false), // long
+ ll (false); // long long
+
+ string type;
+
+ for (; tt == CPP_KEYWORD; ptt = tt, tt = l.next (tl, &tn))
+ {
+ if (!name.empty ())
+ name += ' ';
+ name += tl;
+
+ if (tl == "signed")
+ {
+ if (si || un)
+ throw invalid_name ();
+
+ si = true;
+ }
+ else if (tl == "unsigned")
+ {
+ if (si || un)
+ throw invalid_name ();
+
+ un = true;
+ }
+ else if (tl == "short")
+ {
+ if (sh || lo || ll)
+ throw invalid_name ();
+
+ sh = true;
+ }
+ else if (tl == "long")
+ {
+ if (sh || ll)
+ throw invalid_name ();
+
+ if (lo)
+ {
+ lo = false;
+ ll = true;
+ }
+ else
+ lo = true;
+ }
+ else if (tl == "bool" ||
+ tl == "char" ||
+ tl == "wchar_t" ||
+ tl == "char16_t" || // C++11
+ tl == "char32_t" || // C++11
+ tl == "int" ||
+ tl == "float" ||
+ tl == "double")
+ {
+ if (!type.empty ())
+ throw invalid_name ();
+
+ type = tl;
+ }
+ else
+ break;
+ }
+
+ if (type.empty () && (si || un || sh || lo || ll))
+ type = "int"; // If type not specified, it defaults to int.
+
+ if (type.empty ())
+ return type;
+
+ string r;
+
+ if (sh)
+ r += "short ";
+
+ if (lo)
+ r += "long ";
+
+ if (ll)
+ r += "long long ";
+
+ if (si && type == "char")
+ r += "signed ";
+
+ if (un)
+ r += "unsigned ";
+
+ r += type;
+ return r;
+ }
+
+ string
+ parse_scoped_name (cxx_lexer& l, cpp_ttype& tt, string& tl, tree& tn)
+ {
+ string name;
+
+ if (tt == CPP_SCOPE)
+ {
+ name += "::";
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_KEYWORD)
+ {
+ cpp_ttype ptt; // Not used.
+ string t (parse_fundamental (l, tt, tl, tn, ptt, name));
+
+ if (!t.empty ())
+ return name;
+ }
+
+ while (true)
+ {
+ if (tt != CPP_NAME)
+ throw invalid_name ();
+
+ name += tl;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_SCOPE)
+ break;
+
+ name += "::";
+ tt = l.next (tl, &tn);
+ }
+
+ return name;
+ }
+
+ tree
+ resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ tree scope,
+ string& name,
+ bool is_type,
+ bool trailing_scope,
+ tree* end_scope)
+ {
+ tree id;
+ bool first (true);
+
+ if (tt == CPP_SCOPE)
+ {
+ name += "::";
+ scope = global_namespace;
+ first = false;
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_KEYWORD)
+ {
+ string t (parse_fundamental (l, tt, tl, tn, ptt, name));
+
+ if (!t.empty ())
+ {
+ tree decl (
+ lookup_qualified_name (
+ global_namespace, get_identifier (t.c_str ()), true, false));
+
+ if (decl == error_mark_node)
+ throw unable_to_resolve (name, true);
+
+ if (end_scope != 0)
+ *end_scope = global_namespace;
+
+ return decl;
+ }
+ }
+
+ while (true)
+ {
+ if (end_scope != 0)
+ *end_scope = scope;
+
+ if (tt != CPP_NAME)
+ throw invalid_name ();
+
+ name += tl;
+ id = get_identifier (tl.c_str ());
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ bool last (true);
+ if (tt == CPP_SCOPE)
+ {
+ // If trailing scope names are allowed, then we also need to
+ // check what's after the scope.
+ //
+ if (trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME)
+ last = false;
+ }
+ else
+ last = false;
+ }
+
+ tree decl = lookup_qualified_name (scope, id, last && is_type, false);
+
+ // If this is the first component in the name, then also search the
+ // outer scopes.
+ //
+ if (decl == error_mark_node && first && scope != global_namespace)
+ {
+ do
+ {
+ scope = TYPE_P (scope)
+ ? CP_TYPE_CONTEXT (scope)
+ : CP_DECL_CONTEXT (scope);
+ decl = lookup_qualified_name (scope, id, last && is_type, false);
+ } while (decl == error_mark_node && scope != global_namespace);
+ }
+
+ if (decl == error_mark_node)
+ throw unable_to_resolve (name, last);
+
+ scope = decl;
+
+ if (last)
+ break;
+
+ first = false;
+
+ if (TREE_CODE (scope) == TYPE_DECL)
+ scope = TREE_TYPE (scope);
+
+ name += "::";
+
+ if (!trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ }
+
+ return scope;
+ }
+
+ semantics::node&
+ resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ semantics::scope& start_scope,
+ string& name,
+ semantics::type_id const& tid,
+ bool trailing_scope,
+ semantics::scope** end_scope)
+ {
+ bool first (true);
+ semantics::scope* scope (&start_scope);
+
+ if (tt == CPP_SCOPE)
+ {
+ name += "::";
+ for (; !scope->global_scope (); scope = &scope->scope_ ()) ;
+ first = false;
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_KEYWORD)
+ {
+ string t (parse_fundamental (l, tt, tl, tn, ptt, name));
+
+ if (!t.empty ())
+ {
+ for (; !scope->global_scope (); scope = &scope->scope_ ()) ;
+
+ if (end_scope != 0)
+ *end_scope = scope;
+
+ return scope->lookup<semantics::fund_type> (t);
+ }
+ }
+
+ semantics::names* r;
+
+ for (;;)
+ {
+ if (end_scope != 0)
+ *end_scope = scope;
+
+ if (tt != CPP_NAME)
+ throw invalid_name ();
+
+ name += tl;
+ string n (tl);
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ bool last (true);
+ if (tt == CPP_SCOPE)
+ {
+ // If trailing scope names are allowed, then we also need to
+ // check what's after the scope.
+ //
+ if (trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME)
+ last = false;
+ }
+ else
+ last = false;
+ }
+
+ // If this is the first component in the name, then also search the
+ // outer scopes.
+ //
+ bool hidden (false);
+ r = scope->lookup (
+ n,
+ (last ? tid : typeid (semantics::scope)),
+ (first ? 0 : semantics::scope::exclude_outer) |
+ (last ? semantics::scope::include_hidden : 0),
+ (last ? &hidden : 0));
+
+ if (r == 0)
+ throw semantics::unresolved (name, hidden);
+
+ if (last)
+ break;
+
+ scope = &dynamic_cast<semantics::scope&> (r->named ());
+ first = false;
+
+ name += "::";
+
+ if (!trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ }
+
+ return r->named ();
+ }
+}
diff --git a/odb/odb/lookup.hxx b/odb/odb/lookup.hxx
new file mode 100644
index 0000000..86c65b2
--- /dev/null
+++ b/odb/odb/lookup.hxx
@@ -0,0 +1,97 @@
+// file : odb/lookup.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_LOOKUP_HXX
+#define ODB_LOOKUP_HXX
+
+#include <odb/gcc.hxx>
+
+#include <string>
+
+#include <odb/cxx-lexer.hxx>
+#include <odb/semantics/elements.hxx>
+
+namespace lookup
+{
+ struct invalid_name
+ {
+ };
+
+ struct unable_to_resolve
+ {
+ unable_to_resolve (std::string const& n, bool l): name_ (n), last_ (l) {}
+
+ std::string const&
+ name () const {return name_;}
+
+ // Last component in the name.
+ //
+ bool
+ last () const {return last_;}
+
+ private:
+ std::string name_;
+ bool last_;
+ };
+
+ std::string
+ parse_scoped_name (cxx_lexer&,
+ cpp_ttype&,
+ std::string& tl, // Token literal.
+ tree& tn); // Token node.
+
+ // If trailing_scope is true, then this function also handles
+ // names in the 'foo::bar::<something-other-than-name>' form.
+ // In this case token will be <something-other-than-name> and
+ // ptt will be CPP_SCOPE.
+ //
+ // The names are appended to the 'name' variable as they are
+ // being resolved.
+ //
+ tree
+ resolve_scoped_name (cxx_lexer&,
+ cpp_ttype&,
+ std::string& tl, // Token literal.
+ tree& tn, // Token node.
+ cpp_ttype& ptt, // Previous token type.
+ tree start_scope,
+ std::string& name,
+ bool is_type,
+ bool trailing_scope = false,
+ tree* end_scope = 0);
+
+ // The same but using semantic graph instead of GCC tree. Also
+ // throws semantics::unresolved instead of unable_to_resolve.
+ //
+ semantics::node&
+ resolve_scoped_name (cxx_lexer&,
+ cpp_ttype&,
+ std::string& tl, // Token literal.
+ tree& tn, // Token node.
+ cpp_ttype& ptt, // Previous token type.
+ semantics::scope& start_scope,
+ std::string& name,
+ semantics::type_id const&,
+ bool trailing_scope = false,
+ semantics::scope** end_scope = 0);
+
+ template <typename T>
+ T&
+ resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ std::string& tl, // Token literal.
+ tree& tn, // Token node.
+ cpp_ttype& ptt, // Previous token type.
+ semantics::scope& start_scope,
+ std::string& name,
+ bool trailing_scope = false,
+ semantics::scope** end_scope = 0)
+ {
+ return dynamic_cast<T&> (
+ resolve_scoped_name (
+ l, tt, tl, tn, ptt,
+ start_scope, name, typeid (T), trailing_scope, end_scope));
+ }
+}
+
+#endif // ODB_LOOKUP_HXX
diff --git a/odb/odb/odb.cxx b/odb/odb/odb.cxx
new file mode 100644
index 0000000..701f6e1
--- /dev/null
+++ b/odb/odb/odb.cxx
@@ -0,0 +1,2178 @@
+// file : odb/odb.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <errno.h>
+#include <stdlib.h> // getenv, setenv
+#include <string.h> // strerror, memset
+#include <unistd.h> // stat, close
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+// Process.
+//
+#ifndef _WIN32
+# include <unistd.h> // execvp, fork, dup2, pipe, {STDIN,STDERR}_FILENO
+# include <sys/types.h> // waitpid
+# include <sys/wait.h> // waitpid
+#else
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h> // CreatePipe, CreateProcess, GetTemp*, MAX_PATH
+# include <io.h> // _open_osfhandle
+# include <fcntl.h> // _O_TEXT
+#endif
+
+#include <string>
+#include <vector>
+#include <cstddef> // size_t
+#include <sstream>
+#include <fstream>
+#include <iostream>
+#include <ext/stdio_filebuf.h>
+
+#include <libcutl/fs/path.hxx>
+#include <libcutl/fs/auto-remove.hxx>
+
+#include <odb/version.hxx>
+#include <odb/options.hxx>
+#include <odb/profile.hxx>
+
+#ifdef HAVE_CONFIG_H
+# include <odb/config.h>
+#endif
+
+using namespace std;
+using cutl::fs::path;
+using cutl::fs::invalid_path;
+using cutl::fs::auto_remove;
+
+typedef vector<string> strings;
+typedef vector<path> paths;
+
+//
+// Path manipulation.
+//
+
+// Escape backslashes in the path.
+//
+static string
+escape_path (string const&);
+
+// Search the PATH environment variable for the file.
+//
+static path
+path_search (path const&);
+
+// Driver path with the directory part (search PATH).
+//
+static path
+driver_path (path const& driver);
+
+#ifndef ODB_STATIC_PLUGIN
+static path
+plugin_path (path const& driver, string const& gxx);
+#endif
+
+//
+// Process manipulation.
+//
+struct process_info
+{
+#ifndef _WIN32
+ pid_t id;
+#else
+ HANDLE id;
+#endif
+
+ int out_fd; // Write to this fd to send to the new process' stdin.
+ int in_efd; // Read from this fd to receive from the new process' stderr.
+ int in_ofd; // Read from this fd to receive from the new process' stdout.
+};
+
+struct process_failure {};
+
+#ifdef _WIN32
+// Deal with Windows command line length limit.
+//
+static auto_remove
+fixup_cmd_line (vector<const char*>& args,
+ size_t start,
+ const char* name,
+ string& arg);
+#endif
+
+// Start another process using the specified command line. Connect the
+// newly created process' stdin to out_fd. Also if connect_* are true,
+// connect the created process' stdout and stderr to in_*fd. Issue
+// diagnostics and throw process_failure if anything goes wrong. The
+// name argument is the name of the current process for diagnostics.
+//
+static process_info
+start_process (char const* args[],
+ char const* name,
+ bool connect_stderr = false,
+ bool connect_stdout = false);
+
+// Wait for the process to terminate. Return true if the process terminated
+// normally and with the zero exit status. Issue diagnostics and throw
+// process_failure if anything goes wrong. The name argument is the name
+// of the current process for diagnostics.
+//
+static bool
+wait_process (process_info, char const* name);
+
+//
+//
+static string
+encode_plugin_flag (string const& k);
+
+static string
+encode_plugin_option (string const& k, string const& v);
+
+// Extract header search paths from GCC's -v output. May throw the
+// profile_failure, process_failure and invalid_path exceptions. Name
+// is the program name (argv[0]) for diagnostics.
+//
+static paths
+profile_paths (strings const& args, char const* name);
+
+static char const* const db_macro[] =
+{
+ "-DODB_DATABASE_COMMON",
+ "-DODB_DATABASE_MSSQL",
+ "-DODB_DATABASE_MYSQL",
+ "-DODB_DATABASE_ORACLE",
+ "-DODB_DATABASE_PGSQL",
+ "-DODB_DATABASE_SQLITE"
+};
+
+int
+main (int argc, char* argv[])
+{
+ ostream& e (cerr);
+
+ try
+ {
+ strings args, plugin_args;
+ bool v (false);
+
+ // The first argument points to the program name, which is
+ // g++ by default.
+ //
+#ifdef ODB_GXX_NAME
+ path gxx (ODB_GXX_NAME);
+
+ if (gxx.empty ())
+ {
+ e << argv[0] << ": error: embedded g++ compile name is empty" << endl;
+ return 1;
+ }
+
+ // If the g++ name is a relative path (starts with '.'), then use
+ // our own path as base.
+ //
+ if (gxx.string ()[0] == '.')
+ {
+ path dp (driver_path (path (argv[0])));
+ path d (dp.directory ());
+
+ if (!d.empty ())
+ gxx = d / gxx;
+ }
+
+ args.push_back (gxx.string ());
+
+ // Also modify LD_LIBRARY_PATH to include the lib path.
+ //
+#ifndef _WIN32
+ {
+#ifdef __APPLE__
+ char const name[] = "DYLD_LIBRARY_PATH";
+#else
+ char const name[] = "LD_LIBRARY_PATH";
+#endif
+
+ string ld_paths;
+
+ if (char const* s = getenv (name))
+ ld_paths = s;
+
+ path d (gxx.directory ());
+
+ if (!d.empty ())
+ {
+ d.complete ();
+ d /= path ("..") / path ("lib");
+
+ if (ld_paths.empty ())
+ ld_paths = d.string ();
+ else
+ ld_paths = d.string () + path::traits::path_separator + ld_paths;
+
+ if (setenv (name, ld_paths.c_str (), 1) != 0)
+ {
+ e << argv[0] << ": error: unable to update environment" << endl;
+ return 1;
+ }
+ }
+ }
+#endif // _WIN32
+
+#else
+ args.push_back ("g++");
+#endif // ODB_GXX_NAME
+
+ // Default options.
+ //
+ args.push_back ("-x");
+ args.push_back ("c++");
+ args.push_back (""); // Reserve space for -std=c++XX.
+ args.push_back ("-S");
+ args.push_back ("-Wunknown-pragmas");
+ args.push_back ("-Wno-deprecated");
+ args.push_back (""); // Reserve space for -fplugin=path.
+
+ // Parse the default options file if we have one.
+ //
+ strings def_inc_dirs;
+ strings def_defines;
+#ifdef ODB_DEFAULT_OPTIONS_FILE
+ {
+ path file (ODB_DEFAULT_OPTIONS_FILE);
+
+ // If the path is relative, then use the driver's path as a base. If
+ // the file is not found in that directory, then also try outer
+ // directory (so that we can find /etc if driver is in /usr/bin).
+ //
+ if (file.relative ())
+ {
+ bool found (false);
+ path dd (driver_path (path (argv[0])).directory ());
+
+ for (path d (dd);; d = d.directory ())
+ {
+ path f (d / file);
+ // Check that the file exist without checking for permissions, etc.
+ //
+ struct stat s;
+ if (stat (f.string ().c_str (), &s) == 0 && S_ISREG (s.st_mode))
+ {
+ file = f;
+ found = true;
+ break;
+ }
+
+ if (d.root ())
+ break;
+ }
+
+ if (!found)
+ file = dd / file; // For diagnostics.
+ }
+
+ cli::argv_file_scanner s (file.string ());
+
+ bool first_x (true);
+
+ while (s.more ())
+ {
+ string a (s.next ());
+ size_t n (a.size ());
+
+ // -x
+ //
+ if (a == "-x")
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ if (first_x)
+ {
+ first_x = false;
+
+ // If it doesn't start with '-', then it must be the g++
+ // executable name. Update the first argument with it.
+ //
+ if (a[0] != '-')
+ args[0] = a;
+ else
+ args.push_back (a);
+ }
+ else
+ args.push_back (a);
+ }
+ // -I
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ def_inc_dirs.push_back (a);
+
+ if (n == 2) // -I /path
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -I option"
+ << endl;
+ return 1;
+ }
+
+ def_inc_dirs.push_back (a);
+ }
+ }
+ // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-framework")
+ {
+ def_inc_dirs.push_back (a);
+
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ def_inc_dirs.push_back (a);
+ }
+ // -D
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'D')
+ {
+ def_defines.push_back (a);
+
+ if (n == 2) // -D macro
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -D option"
+ << endl;
+ return 1;
+ }
+
+ def_defines.push_back (a);
+ }
+ }
+ // -U
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'U')
+ {
+ def_defines.push_back (a);
+
+ if (n == 2) // -U macro
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -U option"
+ << endl;
+ return 1;
+ }
+
+ def_defines.push_back (a);
+ }
+ }
+ else
+ plugin_args.push_back (a);
+ }
+ }
+#endif
+
+ // Add the default preprocessor defines (-D/-U) before the user-supplied
+ // ones.
+ //
+ args.insert (args.end (), def_defines.begin (), def_defines.end ());
+
+ // Parse driver options.
+ //
+ // We scan expanding --options-file in order to allow specifying ad hoc
+ // options (-I, etc) in options files.
+ //
+ bool first_x (true);
+
+ for (cli::argv_file_scanner scan (argc, argv, "--options-file");
+ scan.more (); )
+ {
+ string a (scan.next ());
+ size_t n (a.size ());
+
+ // -v
+ //
+ if (a == "-v")
+ {
+ v = true;
+ args.push_back (a);
+ }
+ // -x
+ //
+ else if (a == "-x")
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -x option"
+ << endl;
+ return 1;
+ }
+
+ if (first_x)
+ {
+ first_x = false;
+
+ // If it doesn't start with '-', then it must be the g++
+ // executable name. Update the first argument with it.
+ //
+ if (v[0] != '-')
+ args[0] = v;
+ else
+ args.push_back (v);
+ }
+ else
+ args.push_back (v);
+ }
+ // -I
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -I /path
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -I option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-framework")
+ {
+ args.push_back (a);
+
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ // -D
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'D')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -D macro
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -D option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // -U
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'U')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -U macro
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -U option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // Store everything else in a list so that we can parse it with the
+ // cli parser. This is the only reliable way to find out where the
+ // options end.
+ //
+ else
+ plugin_args.push_back (a);
+ }
+
+ // Add the default include directories (-I) after the user-supplied
+ // ones.
+ //
+ args.insert (args.end (), def_inc_dirs.begin (), def_inc_dirs.end ());
+
+ // Find the plugin.
+ //
+ {
+#ifndef ODB_STATIC_PLUGIN
+ path plugin (plugin_path (path (argv[0]), args[0]));
+#else
+ // Use a dummy name if the plugin is linked into the compiler.
+ //
+ path plugin ("odb");
+#endif
+
+ if (plugin.empty ())
+ return 1; // Diagnostics has already been issued.
+
+#ifdef ODB_BUILD2
+#ifdef _WIN32
+ // Here is the problem: since the plugin is loaded by GCC (cc1plus.exe
+ // to be precise), the DLL assembly magic we have for executables won't
+ // help here.
+ //
+ // To allow executing the ODB compiler in-place we add the odb.exe.dlls/
+ // directory to PATH. It is a bit of hack but then DLL assemblies for
+ // DLLs is whole new level of insanity that we are unlikely to ever
+ // touch.
+ //
+ // And it turns out we have the same problem in the installed case: if
+ // the installation directory is not in PATH, then GCC won't find the
+ // DLLs the plugin needs. So we handle both here.
+ //
+ {
+ path d (plugin.directory ());
+ d.complete ();
+ d.normalize ();
+ d /= path ("odb.exe.dlls");
+
+ struct stat st;
+ if (stat (d.string ().c_str (), &st) != 0 || !S_ISDIR (st.st_mode))
+ d = d.directory ();
+
+ string v ("PATH=" + d.string ());
+
+ if (char const* p = getenv ("PATH"))
+ {
+ v += ';';
+ v += p;
+ }
+
+ _putenv (v.c_str ());
+ }
+#endif
+#endif
+
+ args[7] = "-fplugin=" + plugin.string ();
+ }
+
+ // Parse plugin options. We have to do it twice to get the target
+ // database which is needed while loading profiles.
+ //
+ vector<char*> av;
+ av.push_back (argv[0]);
+
+ for (strings::iterator i (plugin_args.begin ()), end (plugin_args.end ());
+ i != end; ++i)
+ {
+ av.push_back (const_cast<char*> (i->c_str ()));
+ }
+
+ int ac (static_cast<int> (av.size ()));
+
+ cli::argv_file_scanner::option_info oi[3];
+ oi[0].option = "--options-file"; // Keep in case profile uses it.
+ oi[0].search_func = 0;
+ oi[1].option = "-p";
+ oi[2].option = "--profile";
+
+ vector<database> dbs;
+ bool show_sloc;
+ size_t sloc_limit;
+ {
+ oi[1].search_func = &profile_search_ignore;
+ oi[2].search_func = &profile_search_ignore;
+
+ cli::argv_file_scanner scan (ac, &av[0], oi, 3);
+ options ops (scan);
+
+ // Handle --build2-metadata (see also buildfile).
+ //
+ if (ops.build2_metadata_specified ())
+ {
+ ostream& o (cout);
+
+ // Note that the export.metadata variable should be the first non-
+ // blank/comment line.
+ //
+ o << "# build2 buildfile odb" << endl
+ << "export.metadata = 1 odb" << endl
+ << "odb.name = [string] odb" << endl
+ << "odb.version = [string] '" << ODB_COMPILER_VERSION_STR << '\'' << endl
+ << "odb.checksum = [string] '" << ODB_COMPILER_VERSION_STR << '\'' << endl
+ << "odb.environment = [strings] CPATH CPLUS_INCLUDE_PATH GCC_EXEC_PREFIX COMPILER_PATH" << endl;
+
+ return 0;
+ }
+
+ // Handle --version.
+ //
+ if (ops.version ())
+ {
+ ostream& o (cout);
+
+ o << "ODB object-relational mapping (ORM) compiler for C++ "
+ ODB_COMPILER_VERSION_STR << endl;
+
+#ifdef ODB_BUILD2
+ o << "Copyright (c) " << ODB_COPYRIGHT << "." << endl;
+#endif
+
+ o << "This is free software; see the source for copying conditions. "
+ << "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS "
+ << "FOR A PARTICULAR PURPOSE." << endl;
+
+ return 0;
+ }
+
+ // Handle --help.
+ //
+ if (ops.help ())
+ {
+ ostream& o (cout);
+
+ o << "Usage: " << argv[0] << " [options] file [file ...]" << endl
+ << "Options:" << endl;
+
+ options::print_usage (cout);
+ return 0;
+ }
+
+ // Check that required options were specifed.
+ //
+ dbs = ops.database ();
+
+ if (dbs.empty ())
+ {
+ e << argv[0] << ": error: no database specified with the --database "
+ << "option" << endl;
+ return 1;
+ }
+
+ if (dbs.size () > 1 && !ops.multi_database_specified ())
+ {
+ e << argv[0] << ": error: --multi-database option required when " <<
+ "multiple databases are specified"<< endl;
+ return 1;
+ }
+
+ show_sloc = ops.show_sloc ();
+ sloc_limit = ops.sloc_limit_specified () ? ops.sloc_limit () : 0;
+
+ // Translate some ODB options to GCC options.
+ //
+ switch (ops.std ())
+ {
+ case cxx_version::cxx98:
+ {
+ args[3] = "-std=c++98";
+ break;
+ }
+ case cxx_version::cxx11:
+ {
+ args[3] = "-std=c++0x"; // c++11 was only added in GCC 4.7.0.
+ break;
+ }
+ case cxx_version::cxx14:
+ {
+ args[3] = "-std=c++1y";
+ break;
+ }
+ case cxx_version::cxx17:
+ {
+ args[3] = "-std=c++1z";
+ break;
+ }
+ case cxx_version::cxx20:
+ {
+ args[3] = "-std=c++2a";
+ break;
+ }
+ }
+ }
+
+ // Obtain profile (-I) search paths.
+ //
+ paths prof_paths (profile_paths (args, argv[0]));
+
+ if (v)
+ {
+ e << "Profile search paths:" << endl;
+
+ for (paths::const_iterator i (prof_paths.begin ());
+ i != prof_paths.end (); ++i)
+ e << " " << *i << endl;
+ }
+
+ // Pass profile search paths (svc-path option).
+ //
+ for (paths::const_iterator i (prof_paths.begin ());
+ i != prof_paths.end (); ++i)
+ {
+ args.push_back (encode_plugin_option ("svc-path", i->string ()));
+ }
+
+ // Add common ODB macros.
+ //
+ args.push_back ("-DODB_COMPILER");
+
+ {
+ ostringstream ostr;
+ ostr << ODB_COMPILER_VERSION;
+ args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ());
+ }
+
+ // Compile for each database.
+ //
+ size_t sloc_total (0);
+
+ for (vector<database>::iterator i (dbs.begin ()); i != dbs.end (); ++i)
+ {
+ database db (*i);
+ strings db_args (args);
+
+ // Add database-specific ODB macro.
+ //
+ db_args.push_back (db_macro[db]);
+
+ // Second parse.
+ //
+ profile_data pd (prof_paths, db, argv[0]);
+ oi[1].search_func = &profile_search;
+ oi[2].search_func = &profile_search;
+ oi[1].arg = &pd;
+ oi[2].arg = &pd;
+
+ cli::argv_file_scanner scan (ac, &av[0], oi, 3);
+ options ops (scan);
+
+ size_t end (scan.end () - 1); // We have one less in plugin_args.
+
+ if (end == plugin_args.size ())
+ {
+ e << argv[0] << ": error: input file expected" << endl;
+ return 1;
+ }
+
+ // Encode plugin options.
+ //
+ // Add the database we are compiling for first. More databases
+ // could be specified in options files but they will be ignored
+ // by the plugin (it only cares about the first).
+ //
+ db_args.push_back (encode_plugin_option ("database", db.string ()));
+
+ cli::options const& desc (options::description ());
+ for (size_t i (0); i < end; ++i)
+ {
+ string k, a (plugin_args[i]);
+
+ // Ignore certain options.
+ //
+ if (a == "--")
+ {
+ // Ignore the option seperator since GCC doesn't understand it.
+ //
+ continue;
+ }
+ else if (a == "-d" || a == "--database")
+ {
+ // Ignore all other databases.
+ //
+ i++; // Skip the value.
+ continue;
+ }
+
+ cli::options::const_iterator it (desc.find (a));
+
+ if (it == desc.end ())
+ {
+ e << argv[0] << ": ice: unexpected option '" << a << "'" << endl;
+ return 1;
+ }
+
+ if (a.size () > 2 && a[0] == '-' && a[1] == '-')
+ k = string (a, 2); // long format
+ else
+ k = string (a, 1); // short format
+
+ if (it->flag ())
+ db_args.push_back (encode_plugin_flag (k));
+ else
+ {
+ // If there are more arguments then we may have a value.
+ //
+ if (i + 1 == end)
+ {
+ e << argv[0] << ": ice: expected argument for '" << a << "'"
+ << endl;
+ return 1;
+ }
+
+ db_args.push_back (encode_plugin_option (k, plugin_args[++i]));
+ }
+ }
+
+ // Reserve space for and remember the position of the svc-file
+ // option.
+ //
+ size_t svc_file_pos (db_args.size ());
+ db_args.push_back ("");
+
+ // If compiling multiple input files at once, pass them also with
+ // the --svc-file option.
+ //
+ bool at_once (ops.at_once () && plugin_args.size () - end > 1);
+ if (at_once)
+ {
+ if (ops.input_name ().empty ())
+ {
+ e << "error: --input-name required when compiling multiple " <<
+ "input files at once (--at-once)" << endl;
+ return 1;
+ }
+
+ for (size_t i (end); i < plugin_args.size (); ++i)
+ db_args.push_back (
+ encode_plugin_option ("svc-file", plugin_args[i]));
+ }
+
+ // Create an execvp-compatible argument array.
+ //
+ typedef vector<char const*> cstrings;
+ cstrings exec_args;
+
+ for (strings::const_iterator i (db_args.begin ()), end (db_args.end ());
+ i != end; ++i)
+ {
+ exec_args.push_back (i->c_str ());
+ }
+
+ exec_args.push_back ("-"); // Compile stdin.
+ exec_args.push_back (0);
+
+ // Iterate over the input files and compile each of them.
+ //
+ for (; end < plugin_args.size (); ++end)
+ {
+ string name (at_once ? ops.input_name () : plugin_args[end]);
+
+ // Set the --svc-file option.
+ //
+ db_args[svc_file_pos] = encode_plugin_option ("svc-file", name);
+ exec_args[svc_file_pos] = db_args[svc_file_pos].c_str ();
+
+ //
+ //
+ ifstream ifs;
+
+ if (!at_once)
+ {
+ ifs.open (name.c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << name << ": error: unable to open in read mode" << endl;
+ return 1;
+ }
+ }
+
+ if (v)
+ {
+ e << "Compiling " << name << endl;
+ for (cstrings::const_iterator i (exec_args.begin ());
+ i != exec_args.end (); ++i)
+ {
+ if (*i != 0)
+ e << *i << (*(i + 1) != 0 ? ' ' : '\n');
+ }
+ }
+
+ // Deal with Windows command line length limit.
+ //
+#ifdef _WIN32
+ string ops_file_arg;
+ auto_remove opt_file_rm (
+ fixup_cmd_line (exec_args, 1, argv[0], ops_file_arg));
+#endif
+
+ process_info pi (start_process (&exec_args[0], argv[0], false, true));
+
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (
+ pi.out_fd, ios_base::out | ios_base::binary);
+ ostream os (&fb);
+
+ if (!at_once)
+ {
+ // See if we there is a UTF-8 BOM in the input file. If so,
+ // then we need to write it before prologues.
+ //
+ if (ifs.peek () == 0xEF)
+ {
+ ifs.get ();
+ if (ifs.get () != 0xBB || ifs.get () != 0xBF)
+ {
+ e << name << ": error: invalid UTF-8 BOM sequence" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os.put (0xEF);
+ os.put (0xBB);
+ os.put (0xBF);
+ }
+ }
+
+ if (!ops.trace ())
+ {
+ // Add the standard prologue.
+ //
+ os << "#line 1 \"<standard-odb-prologue>\"" << endl;
+
+ // Make sure ODB compiler and libodb versions are compatible.
+ //
+ os << "#include <odb/version.hxx>" << endl
+ << endl
+ << "#if ODB_VERSION != " << ODB_VERSION << endl
+ << "# error incompatible ODB compiler and runtime " <<
+ "versions" << endl
+ << "#endif" << endl
+ << endl;
+
+ // Include std::string. It is used as a default type for
+ // the implicit discriminator member in polymorphism
+ // support.
+ //
+ os << "#include <string>" << endl
+ << endl;
+
+ // Add ODB compiler metaprogramming tests.
+ //
+ os << "namespace odb" << endl
+ << "{" << endl
+ << "namespace compiler" << endl
+ << "{" << endl;
+
+ // operator< test, used in validator.
+ //
+ os << "template <typename T>" << endl
+ << "bool" << endl
+ << "has_lt_operator (const T& x, const T& y)" << endl
+ << "{" << endl
+ << "bool r (x < y);" << endl
+ << "return r;" << endl
+ << "}" << endl;
+
+ os << "}" << endl
+ << "}" << endl;
+ }
+
+ // Add custom prologue if any.
+ //
+ // NOTE: if you change the format, you also need to update code
+ // in include.cxx
+ //
+ size_t pro_count (1);
+ if (ops.odb_prologue ().count (db) != 0)
+ {
+ strings const& pro (ops.odb_prologue ()[db]);
+ for (size_t i (0); i < pro.size (); ++i, ++pro_count)
+ {
+ os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" << endl
+ << pro[i] << endl;
+ }
+ }
+
+ if (ops.odb_prologue_file ().count (db) != 0)
+ {
+ strings const& prof (ops.odb_prologue_file ()[db]);
+ for (size_t i (0); i < prof.size (); ++i, ++pro_count)
+ {
+ os << "#line 1 \"<odb-prologue-" << pro_count << ">\""
+ << endl;
+
+ ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << prof[i] << ": error: unable to open in read mode" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << prof[i] << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os << endl;
+ }
+ }
+
+ if (at_once)
+ {
+ // Include all the input files (no need to escape).
+ //
+ os << "#line 1 \"<command-line>\"" << endl;
+
+ bool b (ops.include_with_brackets ());
+ char op (b ? '<' : '"'), cl (b ? '>' : '"');
+
+ for (; end < plugin_args.size (); ++end)
+ os << "#include " << op << plugin_args[end] << cl << endl;
+ }
+ else
+ {
+ // Write the synthesized translation unit to stdout.
+ //
+ os << "#line 1 \"" << escape_path (name) << "\"" << endl;
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << name << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ // Add a new line in case the input file doesn't end with one.
+ //
+ os << endl;
+ }
+
+ // Add custom epilogue if any.
+ //
+ // NOTE: if you change the format, you also need to update code
+ // in include.cxx
+ //
+ size_t epi_count (1);
+ if (ops.odb_epilogue ().count (db) != 0)
+ {
+ strings const& epi (ops.odb_epilogue ()[db]);
+ for (size_t i (0); i < epi.size (); ++i, ++epi_count)
+ {
+ os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" << endl
+ << epi[i] << endl;
+ }
+ }
+
+ if (ops.odb_epilogue_file ().count (db) != 0)
+ {
+ strings const& epif (ops.odb_epilogue_file ()[db]);
+ for (size_t i (0); i < epif.size (); ++i, ++epi_count)
+ {
+ os << "#line 1 \"<odb-epilogue-" << epi_count << ">\""
+ << endl;
+
+ ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << epif[i] << ": error: unable to open in read mode" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << epif[i] << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os << endl;
+ }
+ }
+
+ if (!ops.trace ())
+ {
+ // Add the standard epilogue at the end so that we see all
+ // the declarations.
+ //
+ os << "#line 1 \"<standard-odb-epilogue>\"" << endl;
+
+ // Includes for standard smart pointers. The Boost TR1 header
+ // may or may not delegate to the GCC implementation. In either
+ // case, the necessary declarations will be provided so we don't
+ // need to do anything.
+ //
+ os << "#include <memory>" << endl;
+
+ // Standard wrapper traits.
+ //
+ os << "#include <odb/wrapper-traits.hxx>" << endl;
+
+ // Standard pointer traits.
+ //
+ os << "#include <odb/pointer-traits.hxx>" << endl;
+
+ // Standard container traits.
+ //
+ os << "#include <odb/container-traits.hxx>" << endl;
+
+ // TR1 wrapper/pointer traits.
+ //
+#ifndef ODB_BUILD2
+ if (ops.std () == cxx_version::cxx98)
+ os << endl
+ << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
+ << "# include <tr1/memory>" << endl
+ << "#endif" << endl
+ << "#include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "#include <odb/tr1/pointer-traits.hxx>" << endl;
+#endif
+ }
+ }
+
+ // Filter the output stream looking for communication from the
+ // plugin.
+ //
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
+ istream is (&fb);
+
+ for (bool first (true); !is.eof (); )
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ {
+ e << argv[0] << ": error: io failure while parsing output"
+ << endl;
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (line.compare (0, 9, "odb:sloc:") == 0)
+ {
+ if (show_sloc || sloc_limit != 0)
+ {
+ size_t n;
+ istringstream is (string (line, 9, string::npos));
+
+ if (!(is >> n && is.eof ()))
+ {
+ e << argv[0] << ": error: invalid odb:sloc value" << endl;
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ sloc_total += n;
+ }
+
+ continue;
+ }
+
+ if (first)
+ first = false;
+ else
+ cout << endl;
+
+ cout << line;
+ }
+ }
+
+ if (!wait_process (pi, argv[0]))
+ return 1;
+ } // End input file loop.
+ } // End database loop.
+
+ // Handle SLOC.
+ //
+ if (show_sloc)
+ e << "total: " << sloc_total << endl;
+
+ if (sloc_limit != 0 && sloc_limit < sloc_total)
+ {
+ e << argv[0] << ": error: SLOC limit of " << sloc_limit << " lines " <<
+ "has been exceeded" << endl;
+
+ if (!show_sloc)
+ e << argv[0] << ": info: use the --show-sloc option to see the "
+ << "current total" << endl;
+
+ return 1;
+ }
+ }
+ catch (profile_failure const&)
+ {
+ // Diagnostics has already been issued.
+ //
+ return 1;
+ }
+ catch (process_failure const&)
+ {
+ // Diagnostics has already been issued.
+ //
+ return 1;
+ }
+ catch (invalid_path const& ex)
+ {
+ e << argv[0] << ": error: invalid path '" << ex.path () << "'" << endl;
+ return 1;
+ }
+ catch (cli::exception const& ex)
+ {
+ e << ex << endl;
+ return 1;
+ }
+}
+
+static inline string
+encode_plugin_flag (string const& k)
+{
+ return "-fplugin-arg-odb-" + k;
+}
+
+static string
+encode_plugin_option (string const& k, string const& cv)
+{
+ string o ("-fplugin-arg-odb-");
+ o += k;
+ o += '=';
+
+ if (!cv.empty ())
+ {
+ // A value cannot contain '='. Encode it as the backspace
+ // character.
+ //
+ string v (cv);
+ for (size_t i (0); i < v.size (); ++i)
+ if (v[i] == '=')
+ v[i] = '\b';
+
+ o += v;
+ }
+
+ return o;
+}
+
+static paths
+profile_paths (strings const& sargs, char const* name)
+{
+ // Copy some of the arguments from the passed list. We also need
+ // the g++ executable.
+ //
+ strings args;
+
+ args.push_back (sargs[0]);
+ args.push_back ("-v");
+ args.push_back ("-x");
+ args.push_back ("c++");
+ args.push_back ("-E");
+ args.push_back ("-P");
+
+ for (strings::const_iterator i (++sargs.begin ()), end (sargs.end ());
+ i != end; ++i)
+ {
+ string const& a (*i);
+
+ // -I
+ //
+ if (a.size () > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ args.push_back (a);
+
+ if (a.size () == 2) // -I /path
+ {
+ args.push_back (*(++i));
+ }
+ }
+ // -framework
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-isysroot" ||
+ a == "-framework")
+ {
+ args.push_back (a);
+
+ if (++i == end)
+ {
+ cerr << name << ": error: expected argument for the " << a
+ << " option" << endl;
+ throw profile_failure ();
+ }
+
+ args.push_back (*i);
+ }
+ // --sysroot
+ //
+ else if (a.compare (0, 10, "--sysroot=") == 0)
+ args.push_back (a);
+ // -std
+ //
+ else if (a.compare (0, 5, "-std=") == 0)
+ args.push_back (a);
+ }
+
+ // Create an execvp-compatible argument array.
+ //
+ vector<char const*> exec_args;
+
+ for (strings::const_iterator i (args.begin ()), end (args.end ());
+ i != end; ++i)
+ {
+ exec_args.push_back (i->c_str ());
+ }
+
+ exec_args.push_back ("-"); // Compile stdin.
+ exec_args.push_back (0);
+
+#ifdef _WIN32
+ string ops_file_arg;
+ auto_remove opt_file_rm (fixup_cmd_line (exec_args, 1, name, ops_file_arg));
+#endif
+
+ process_info pi (start_process (&exec_args[0], name, true));
+ close (pi.out_fd); // Preprocess empty file.
+
+ // Read the output into a temporary string stream. We don't parse
+ // it on the fly because we don't know whether it is the data or
+ // diagnostics until after the process is terminated and we get
+ // the exit code. We also cannot first wait for the exist code
+ // and then read the output because the process might get blocked.
+ //
+ stringstream ss;
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_efd, ios_base::in);
+ istream is (&fb);
+
+ for (bool first (true); !is.eof (); )
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ {
+ cerr << name << ": error: "
+ << "io failure while parsing profile paths" << endl;
+
+ wait_process (pi, name);
+ throw profile_failure ();
+ }
+
+ if (first)
+ first = false;
+ else
+ ss << endl;
+
+ ss << line;
+ }
+ }
+
+ if (!wait_process (pi, name))
+ {
+ // Things didn't go well and ss should contain the diagnostics.
+ // In case it is empty, issue our own.
+ //
+ if (!ss.str ().empty ())
+ cerr << ss.rdbuf ();
+ else
+ cerr << name << ": error: unable to extract profile paths" << endl;
+
+ throw profile_failure ();
+ }
+
+ // Parse the cached output.
+ //
+ paths r;
+ {
+ enum
+ {
+ read_prefix,
+ read_path,
+ read_suffix
+ } state = read_prefix;
+
+ while (!ss.eof () && state != read_suffix)
+ {
+ string line;
+ getline (ss, line);
+
+ if (ss.fail () && !ss.eof ())
+ {
+ cerr << name << ": error: "
+ << "io failure while parsing profile paths" << endl;
+ throw profile_failure ();
+ }
+
+ switch (state)
+ {
+ case read_prefix:
+ {
+ // The English string that we are looking for is "#include <...>
+ // search starts here:" but it can be translated. However, all
+ // the translations seems to have the "#include" and "<...>"
+ // parts, so we can search for those.
+ //
+ if (line.find ("#include") != string::npos &&
+ line.find ("<...>") != string::npos)
+ state = read_path;
+ break;
+ }
+ case read_path:
+ {
+ // The end of the list is terminated with the "End of search
+ // list." line, which, again, can be translated. Here we don't
+ // have any invariable parts that we can use. Instead, we will
+ // rely on the fact that all the paths are space-indented.
+ //
+ if (!line.empty () && line[0] != ' ')
+ state = read_suffix;
+ else
+ // Paths are indented with a space.
+ //
+ r.push_back (path (string (line, 1)));
+
+ break;
+ }
+ case read_suffix:
+ {
+ // We shouldn't get here.
+ break;
+ }
+ }
+ }
+
+ if (state != read_suffix)
+ {
+ cerr << name << ": error: unable to parse profile paths" << endl;
+ throw profile_failure ();
+ }
+ }
+
+ return r;
+}
+
+//
+// Path manipulation.
+//
+
+static string
+escape_path (string const& p)
+{
+ string r;
+
+ for (size_t i (0); i < p.size (); ++i)
+ {
+ if (p[i] == '\\')
+ r += "\\\\";
+ else
+ r += p[i];
+ }
+
+ return r;
+}
+
+static path
+path_search (path const& f)
+{
+ typedef path::traits traits;
+
+ // If there is a directory component in the file, then the PATH
+ // search does not apply.
+ //
+ if (!f.directory ().empty ())
+ return f;
+
+ string paths;
+
+ // If there is no PATH in environment then the default search
+ // path is the current directory.
+ //
+ if (char const* s = getenv ("PATH"))
+ paths = s;
+ else
+ paths = traits::path_separator;
+
+ // On Windows also check the current directory.
+ //
+#ifdef _WIN32
+ paths += traits::path_separator;
+#endif
+
+ struct stat info;
+
+ for (size_t b (0), e (paths.find (traits::path_separator));
+ b != string::npos;)
+ {
+ path p (string (paths, b, e != string::npos ? e - b : e));
+
+ // Empty path (i.e., a double colon or a colon at the beginning
+ // or end of PATH) means search in the current dirrectory.
+ //
+ if (p.empty ())
+ p = path (".");
+
+ path dp (p / f);
+
+ // Just check that the file exist without checking for permissions, etc.
+ //
+ if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
+ return dp;
+
+ // On Windows also try the path with the .exe extension.
+ //
+#ifdef _WIN32
+ dp += ".exe";
+
+ if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
+ return dp;
+#endif
+
+ if (e == string::npos)
+ b = e;
+ else
+ {
+ b = e + 1;
+ e = paths.find (traits::path_separator, b);
+ }
+ }
+
+ return path ();
+}
+
+static path
+driver_path (path const& drv)
+{
+ return drv.directory ().empty () ? path_search (drv) : drv;
+}
+
+#ifndef ODB_STATIC_PLUGIN
+static path
+plugin_path (path const& drv,
+#ifdef ODB_GCC_PLUGIN_DIR
+ string const& gxx)
+#else
+ string const&)
+#endif
+{
+#ifdef _WIN32
+ char const plugin_ext[] = ".dll";
+
+// While GCC 8 switched to using .dylib as the plugin extension, there is a
+// bug in the extension stripping code. So for now we use the .so extension
+// everywhere (see also buildfile if changing this).
+//
+//#elif defined(__APPLE__) && defined(ODB_BUILD2)
+// char const plugin_ext[] = ".dylib";
+#else
+ char const plugin_ext[] = ".so";
+#endif
+
+ // Figure out the plugin base name which is just the driver name (but
+ // without the .exe extension on Windows). If the driver name starts with
+ // 'lt-', then we are running through the libtool script. Strip this prefix
+ // -- the shared object should be in the same directory.
+ //
+#ifdef _WIN32
+ string b (drv.leaf ().base ().string ());
+#else
+ string b (drv.leaf ().string ());
+#endif
+
+ bool lt (b.size () > 3 && b[0] == 'l' && b[1] == 't' && b[2] == '-');
+ if (lt)
+ b = string (b, 3, string::npos);
+
+ path dp (driver_path (drv));
+
+ if (dp.empty ())
+ {
+ cerr << drv << ": error: unable to resolve ODB driver path" << endl;
+ return path ();
+ }
+
+ dp = dp.directory ();
+ struct stat info;
+
+ // Regardless of whether we were given a plugin path, first try
+ // the current directory for the .la file. This will make sure
+ // running ODB from the build directory works as expected.
+ //
+ // @@ BUILD2: not going to work for build2 build.
+ //
+ path pp (dp / path (b + ".la"));
+ if (stat (pp.string ().c_str (), &info) == 0)
+ {
+ pp = dp / path (".libs") / path (b + ".so");
+ if (stat (pp.string ().c_str (), &info) == 0)
+ return pp;
+ }
+
+#ifdef ODB_GCC_PLUGIN_DIR
+ // Plugin should be installed into the GCC default plugin directory.
+ // Ideally, in this situation, we would simply pass the plugin name and
+ // let GCC append the correct directory. Unfortunately, this mechanism
+ // was only added in GCC 4.6 so in order to support 4.5 we will have to
+ // emulate it ourselves.
+ //
+ if (!lt)
+ {
+ //@@ BUILD2: if/when dropping old GCC should just get rid of this.
+#if 1
+ // First get the default GCC plugin directory.
+ //
+ path d;
+ vector<char const*> exec_args;
+ exec_args.push_back (gxx.c_str ());
+ exec_args.push_back ("-print-file-name=plugin");
+ exec_args.push_back (0);
+
+ process_info pi (
+ start_process (
+ &exec_args[0], drv.string ().c_str (), false, true));
+ close (pi.out_fd);
+
+ // Read the path from stdout.
+ //
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
+ istream is (&fb);
+ string line;
+ getline (is, line);
+ d = path (line);
+ }
+
+ if (!wait_process (pi, drv.string ().c_str ()))
+ return path (); // Assume GCC issued some diagnostics.
+
+ if (d.string () == "plugin")
+ {
+ cerr << drv << ": error: unable to obtain GCC plugin directory" << endl;
+ return path ();
+ }
+
+ // See if the plugin is there.
+ //
+ pp = d / path (b + plugin_ext);
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: no ODB plugin in GCC plugin directory '" <<
+ d << "'" << endl;
+ return path ();
+ }
+
+ return pp;
+#else
+ return path (b);
+#endif
+ }
+#elif defined (ODB_PLUGIN_PATH)
+ // If we were given a plugin path, use that unless we are running
+ // via libtool.
+ //
+ if (!lt)
+ {
+ string rp (ODB_PLUGIN_PATH);
+ if (!rp.empty ())
+ dp /= path (rp);
+
+ pp = dp / path (b + plugin_ext);
+
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: no ODB plugin in '" << dp << "'" << endl;
+ return path ();
+ }
+
+ return pp;
+ }
+#endif
+
+ // Try in the current directory.
+ //
+ pp = dp / path (b + plugin_ext);
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: unable to locate ODB plugin" << endl;
+ return path ();
+ }
+
+ return pp;
+}
+#endif
+
+//
+// Process manipulation.
+//
+
+#ifndef _WIN32
+
+static process_info
+start_process (char const* args[], char const* name, bool err, bool out)
+{
+ int out_fd[2];
+ int in_efd[2];
+ int in_ofd[2];
+
+ if (pipe (out_fd) == -1 ||
+ (err && pipe (in_efd) == -1) ||
+ (out && pipe (in_ofd) == -1))
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ pid_t pid (fork ());
+
+ if (pid == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ if (pid == 0)
+ {
+ // Child. Close the write end of the pipe and duplicate the read end
+ // to stdin. Then close the original read end descriptors.
+ //
+ if (close (out_fd[1]) == -1 ||
+ dup2 (out_fd[0], STDIN_FILENO) == -1 ||
+ close (out_fd[0]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ // Do the same for the stderr if requested.
+ //
+ if (err)
+ {
+ if (close (in_efd[0]) == -1 ||
+ dup2 (in_efd[1], STDERR_FILENO) == -1 ||
+ close (in_efd[1]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ // Do the same for the stdout if requested.
+ //
+ if (out)
+ {
+ if (close (in_ofd[0]) == -1 ||
+ dup2 (in_ofd[1], STDOUT_FILENO) == -1 ||
+ close (in_ofd[1]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ if (execvp (args[0], const_cast<char**> (&args[0])) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << args[0] << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ {
+ // Parent. Close the other ends of the pipes.
+ //
+ if (close (out_fd[0]) == -1 ||
+ (err && close (in_efd[1]) == -1) ||
+ (out && close (in_ofd[1]) == -1))
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ process_info r;
+ r.id = pid;
+ r.out_fd = out_fd[1];
+ r.in_efd = err ? in_efd[0] : 0;
+ r.in_ofd = out ? in_ofd[0] : 0;
+ return r;
+}
+
+static bool
+wait_process (process_info pi, char const* name)
+{
+ int status;
+
+ if (waitpid (pi.id, &status, 0) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
+}
+
+#else // _WIN32
+
+static void
+print_error (char const* name)
+{
+ LPTSTR msg;
+ DWORD e (GetLastError());
+
+ if (!FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ e,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &msg,
+ 0,
+ 0))
+ {
+ cerr << name << ": error: unknown error code " << e << endl;
+ return;
+ }
+
+ cerr << name << ": error: " << msg << endl;
+ LocalFree (msg);
+}
+
+// On Windows we need to protect command line arguments with spaces using
+// quotes. Since there could be actual quotes in the value, we need to escape
+// them.
+//
+static void
+append_quoted (string& cmd_line, const char* ca)
+{
+ string a (ca);
+ bool quote (a.find (' ') != string::npos);
+
+ if (quote)
+ cmd_line += '"';
+
+ for (size_t i (0); i < a.size (); ++i)
+ {
+ if (a[i] == '"')
+ cmd_line += "\\\"";
+ else
+ cmd_line += a[i];
+ }
+
+ if (quote)
+ cmd_line += '"';
+}
+
+// Deal with Windows command line length limit.
+//
+// The best approach seems to be passing the command line in an "options file"
+// ("response file" in Microsoft's terminology).
+//
+static auto_remove
+fixup_cmd_line (vector<const char*>& args,
+ size_t start,
+ const char* name,
+ string& arg)
+{
+ // Calculate the would-be command line length similar to how start_process()
+ // implementation does it.
+ //
+ size_t n (0);
+ string s;
+ for (const char* a: args)
+ {
+ if (a != nullptr)
+ {
+ if (n != 0)
+ n++; // For the space separator.
+
+ s.clear ();
+ append_quoted (s, a);
+ n += s.size ();
+ }
+ }
+
+ if (n <= 32766) // 32768 - "Unicode terminating null character".
+ return auto_remove ();
+
+ // Create the temporary file.
+ //
+ char d[MAX_PATH + 1], p[MAX_PATH + 1];
+ if (GetTempPathA (sizeof (d), d) == 0 ||
+ GetTempFileNameA (d, "odb-options-", 0, p) == 0)
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ auto_remove rm = auto_remove (path (p));
+ try
+ {
+ ofstream ofs (p);
+ if (!ofs.is_open ())
+ {
+ cerr << name << ": error: unable to open '" << p << "' in write mode"
+ << endl;
+ throw process_failure ();
+ }
+
+ ofs.exceptions (ios_base::badbit | ios_base::failbit);
+
+ // Write the arguments to file.
+ //
+ // The format is a space-separated list of potentially-quoted arguments
+ // with support for backslash-escaping.
+ //
+ string b;
+ for (size_t i (start), n (args.size () - 1); i != n; ++i)
+ {
+ const char* a (args[i]);
+
+ // We will most likely have backslashes so just do it.
+ //
+ {
+ for (b.clear (); *a != '\0'; ++a)
+ {
+ if (*a != '\\')
+ b += *a;
+ else
+ b += "\\\\";
+ }
+
+ a = b.c_str ();
+ }
+
+ s.clear ();
+ append_quoted (s, a);
+ ofs << (i != start ? " " : "") << s;
+ }
+
+ ofs << '\n';
+ ofs.close ();
+ }
+ catch (const ios_base::failure&)
+ {
+ cerr << name << ": error: unable to write to '" << p << "'" << endl;
+ throw process_failure ();
+ }
+
+ // Rewrite the command line.
+ //
+ arg = string ("@") + p;
+ args.resize (start);
+ args.push_back (arg.c_str());
+ args.push_back (nullptr);
+
+ return rm;
+}
+
+static process_info
+start_process (char const* args[], char const* name, bool err, bool out)
+{
+ HANDLE out_h[2];
+ HANDLE in_eh[2];
+ HANDLE in_oh[2];
+ SECURITY_ATTRIBUTES sa;
+
+ sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = true;
+ sa.lpSecurityDescriptor = 0;
+
+ if (!CreatePipe (&out_h[0], &out_h[1], &sa, 0) ||
+ !SetHandleInformation (out_h[1], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ if (err)
+ {
+ if (!CreatePipe (&in_eh[0], &in_eh[1], &sa, 0) ||
+ !SetHandleInformation (in_eh[0], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+ }
+
+ if (out)
+ {
+ if (!CreatePipe (&in_oh[0], &in_oh[1], &sa, 0) ||
+ !SetHandleInformation (in_oh[0], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+ }
+
+ // Create the process.
+ //
+ path file (args[0]);
+
+ // Do PATH search.
+ //
+ if (file.directory ().empty ())
+ file = path_search (file);
+ else if (file.base () == file) // No extension
+ file += ".exe"; // Assume .exe.
+
+ if (file.empty ())
+ {
+ cerr << args[0] << ": error: file not found" << endl;
+ throw process_failure ();
+ }
+
+ // Serialize the arguments to string.
+ //
+ string cmd_line;
+
+ for (char const** p (args); *p != 0; ++p)
+ {
+ if (p != args)
+ cmd_line += ' ';
+
+ append_quoted (cmd_line, *p);
+ }
+
+ // Prepare other info.
+ //
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ memset (&si, 0, sizeof (STARTUPINFO));
+ memset (&pi, 0, sizeof (PROCESS_INFORMATION));
+
+ si.cb = sizeof(STARTUPINFO);
+
+ if (err)
+ si.hStdError = in_eh[1];
+ else
+ si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (out)
+ si.hStdOutput = in_oh[1];
+ else
+ si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+
+ si.hStdInput = out_h[0];
+ si.dwFlags |= STARTF_USESTDHANDLES;
+
+ if (!CreateProcess (
+ file.string ().c_str (),
+ const_cast<char*> (cmd_line.c_str ()),
+ 0, // Process security attributes.
+ 0, // Primary thread security attributes.
+ true, // Inherit handles.
+ 0, // Creation flags.
+ 0, // Use our environment.
+ 0, // Use our current directory.
+ &si,
+ &pi))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ CloseHandle (pi.hThread);
+ CloseHandle (out_h[0]);
+
+ if (err)
+ CloseHandle (in_eh[1]);
+
+ if (out)
+ CloseHandle (in_oh[1]);
+
+ process_info r;
+ r.id = pi.hProcess;
+ r.out_fd = _open_osfhandle ((intptr_t) (out_h[1]), 0);
+
+ if (r.out_fd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+
+ if (err)
+ {
+ // Pass _O_TEXT to get newline translation.
+ //
+ r.in_efd = _open_osfhandle ((intptr_t) (in_eh[0]), _O_TEXT);
+
+ if (r.in_efd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ r.in_efd = 0;
+
+ if (out)
+ {
+ // Pass _O_TEXT to get newline translation.
+ //
+ r.in_ofd = _open_osfhandle ((intptr_t) (in_oh[0]), _O_TEXT);
+
+ if (r.in_ofd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ r.in_ofd = 0;
+
+ return r;
+}
+
+static bool
+wait_process (process_info pi, char const* name)
+{
+ DWORD status;
+
+ if (WaitForSingleObject (pi.id, INFINITE) != WAIT_OBJECT_0 ||
+ !GetExitCodeProcess (pi.id, &status))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ CloseHandle (pi.id);
+ return status == 0;
+}
+
+#endif // _WIN32
diff --git a/odb/odb/option-functions.cxx b/odb/odb/option-functions.cxx
new file mode 100644
index 0000000..7eda934
--- /dev/null
+++ b/odb/odb/option-functions.cxx
@@ -0,0 +1,132 @@
+// file : odb/option-functions.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <set>
+#include <utility> // std::make_pair()
+
+#include <odb/option-functions.hxx>
+
+using namespace std;
+
+void
+process_options (options& o)
+{
+ database db (o.database ()[0]);
+
+ // If --generate-schema-only was specified, then set --generate-schema
+ // as well.
+ //
+ if (o.generate_schema_only ())
+ o.generate_schema (true);
+
+ // If --warn-hard was specified, then set both --warn-hard-{add,delete}.
+ //
+ if (o.warn_hard ())
+ {
+ o.warn_hard_add (true);
+ o.warn_hard_delete (true);
+ }
+
+ // Set the default schema format depending on the database.
+ //
+ if (o.generate_schema () && o.schema_format ()[db].empty ())
+ {
+ set<schema_format>& f (o.schema_format ()[db]);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ break; // No schema for common.
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ {
+ f.insert (schema_format::sql);
+ break;
+ }
+ case database::sqlite:
+ {
+ f.insert (schema_format::embedded);
+ break;
+ }
+ }
+ }
+
+ // Set default --schema-version-table value.
+ //
+ if (o.schema_version_table ().count (db) == 0)
+ o.schema_version_table ()[db] = "schema_version";
+
+ // Set default --schema-name value.
+ //
+ if (o.schema_name ().count (db) == 0)
+ o.schema_name ()[db] = "";
+
+ // Set default --fkeys-deferrable-mode value.
+ //
+ if (o.fkeys_deferrable_mode ().count (db) == 0)
+ o.fkeys_deferrable_mode ()[db] = deferrable::deferred;
+
+ // Set default --{export,extern}-symbol values.
+ //
+ if (o.export_symbol ().count (db) == 0)
+ o.export_symbol ()[db] = "";
+
+ if (o.extern_symbol ().count (db) == 0)
+ o.extern_symbol ()[db] = "";
+
+ // Set default --*-file-suffix values.
+ //
+ {
+ database cm (database::common);
+
+ o.odb_file_suffix ().insert (make_pair (cm, string ("-odb")));
+ o.sql_file_suffix ().insert (make_pair (cm, string ("")));
+ o.schema_file_suffix ().insert (make_pair (cm, string ("-schema")));
+ o.changelog_file_suffix ().insert (make_pair (cm, string ("")));
+ }
+
+ if (o.multi_database () == multi_database::disabled)
+ {
+ o.odb_file_suffix ().insert (make_pair (db, string ("-odb")));
+ o.sql_file_suffix ().insert (make_pair (db, string ("")));
+ o.schema_file_suffix ().insert (make_pair (db, string ("-schema")));
+ o.changelog_file_suffix ().insert (make_pair (db, string ("")));
+ }
+ else
+ {
+ o.odb_file_suffix ().insert (make_pair (db, "-odb-" + db.string ()));
+ o.sql_file_suffix ().insert (make_pair (db, "-" + db.string ()));
+ o.schema_file_suffix ().insert (make_pair (db, "-schema-" + db.string ()));
+ o.changelog_file_suffix ().insert (make_pair (db, '-' + db.string ()));
+ }
+
+ // Set default --default-database value.
+ //
+ if (!o.default_database_specified ())
+ {
+ switch (o.multi_database ())
+ {
+ case multi_database::disabled:
+ {
+ o.default_database (db);
+ o.default_database_specified (true);
+ break;
+ }
+ case multi_database::dynamic:
+ {
+ o.default_database (database::common);
+ o.default_database_specified (true);
+ break;
+ }
+ case multi_database::static_:
+ {
+ // No default database unless explicitly specified.
+ break;
+ }
+ }
+ }
+}
diff --git a/odb/odb/option-functions.hxx b/odb/odb/option-functions.hxx
new file mode 100644
index 0000000..a4b072c
--- /dev/null
+++ b/odb/odb/option-functions.hxx
@@ -0,0 +1,12 @@
+// file : odb/option-functions.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_OPTION_FUNCTIONS_HXX
+#define ODB_OPTION_FUNCTIONS_HXX
+
+#include <odb/options.hxx>
+
+void
+process_options (options&);
+
+#endif // ODB_OPTION_FUNCTIONS_HXX
diff --git a/odb/odb/option-parsers.hxx b/odb/odb/option-parsers.hxx
new file mode 100644
index 0000000..2daa6eb
--- /dev/null
+++ b/odb/odb/option-parsers.hxx
@@ -0,0 +1,199 @@
+// file : odb/option-parsers.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_OPTION_PARSERS_HXX
+#define ODB_OPTION_PARSERS_HXX
+
+#include <vector>
+#include <sstream>
+
+#include <odb/option-types.hxx>
+#include <odb/options.hxx>
+
+namespace cli
+{
+ // Return true if there is a database prefix.
+ //
+ template <typename V>
+ bool
+ parse_option_value (std::string const& o, std::string const& ov,
+ database& k, V& v)
+ {
+ bool r (false);
+ std::string::size_type p = ov.find (':');
+
+ std::string vstr;
+ if (p != std::string::npos)
+ {
+ std::string kstr (ov, 0, p);
+
+ // See if this prefix resolves to the database name. If not,
+ // assume there is no prefix.
+ //
+ std::istringstream ks (kstr);
+
+ if (ks >> k && ks.eof ())
+ {
+ r = true;
+ vstr.assign (ov, p + 1, std::string::npos);
+ }
+ }
+
+ if (!r)
+ vstr = ov; // Use the whole value.
+
+ if (!vstr.empty ())
+ {
+ std::istringstream vs (vstr);
+
+ if (!(vs >> v && vs.eof ()))
+ throw invalid_value (o, ov);
+ }
+ else
+ v = V ();
+
+ return r;
+ }
+
+ // Specialization for std::string.
+ //
+ bool
+ parse_option_value (std::string const&, std::string const& ov,
+ database& k, std::string& v)
+ {
+ bool r (false);
+ std::string::size_type p = ov.find (':');
+
+ if (p != std::string::npos)
+ {
+ std::string kstr (ov, 0, p);
+
+ // See if this prefix resolves to the database name. If not,
+ // assume there is no prefix.
+ //
+ std::istringstream ks (kstr);
+
+ if (ks >> k && ks.eof ())
+ {
+ r = true;
+ v.assign (ov, p + 1, std::string::npos);
+ }
+ }
+
+ if (!r)
+ v = ov; // Use the whole value.
+
+ return r;
+ }
+
+ template <typename V>
+ struct parser<database_map<V> >
+ {
+ typedef database_map<V> map;
+
+ static void
+ parse (map& m, bool& xs, scanner& s)
+ {
+ xs = true;
+ std::string o (s.next ());
+
+ if (s.more ())
+ {
+ database k;
+ V v;
+
+ if (parse_option_value (o, s.next (), k, v))
+ m[k] = v; // Override any old value.
+ else
+ {
+ // No database prefix is specified which means it applies to
+ // all the databases. We also don't want to override database-
+ // specific values, so use insert().
+ //
+ m.insert (typename map::value_type (database::common, v));
+ m.insert (typename map::value_type (database::mssql, v));
+ m.insert (typename map::value_type (database::mysql, v));
+ m.insert (typename map::value_type (database::oracle, v));
+ m.insert (typename map::value_type (database::pgsql, v));
+ m.insert (typename map::value_type (database::sqlite, v));
+ }
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename V>
+ struct parser<database_map<std::vector<V> > >
+ {
+ typedef database_map<std::vector<V> > map;
+
+ static void
+ parse (map& m, bool& xs, scanner& s)
+ {
+ xs = true;
+ std::string o (s.next ());
+
+ if (s.more ())
+ {
+ database k;
+ V v;
+
+ if (parse_option_value (o, s.next (), k, v))
+ m[k].push_back (v);
+ else
+ {
+ // No database prefix is specified which means it applies to
+ // all the databases.
+ //
+ m[database::common].push_back (v);
+ m[database::mssql].push_back (v);
+ m[database::mysql].push_back (v);
+ m[database::oracle].push_back (v);
+ m[database::pgsql].push_back (v);
+ m[database::sqlite].push_back (v);
+ }
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename V>
+ struct parser<database_map<std::set<V> > >
+ {
+ typedef database_map<std::set<V> > map;
+
+ static void
+ parse (map& m, bool& xs, scanner& s)
+ {
+ xs = true;
+ std::string o (s.next ());
+
+ if (s.more ())
+ {
+ database k;
+ V v;
+
+ if (parse_option_value (o, s.next (), k, v))
+ m[k].insert (v);
+ else
+ {
+ // No database prefix is specified which means it applies to
+ // all the databases.
+ //
+ m[database::common].insert (v);
+ m[database::mssql].insert (v);
+ m[database::mysql].insert (v);
+ m[database::oracle].insert (v);
+ m[database::pgsql].insert (v);
+ m[database::sqlite].insert (v);
+ }
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+}
+
+#endif // ODB_OPTION_PARSERS_HXX
diff --git a/odb/odb/option-types.cxx b/odb/odb/option-types.cxx
new file mode 100644
index 0000000..c4a030b
--- /dev/null
+++ b/odb/odb/option-types.cxx
@@ -0,0 +1,352 @@
+// file : odb/option-types.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <istream>
+#include <ostream>
+#include <algorithm> // std::lower_bound
+
+#include <odb/option-types.hxx>
+
+using namespace std;
+
+//
+// cxx_version
+//
+
+static const char* cxx_version_[] =
+{
+ "c++98",
+ "c++11",
+ "c++14",
+ "c++17",
+ "c++20"
+};
+
+string cxx_version::
+string () const
+{
+ return cxx_version_[v_];
+}
+
+istream&
+operator>> (istream& is, cxx_version& v)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "c++98")
+ v = cxx_version::cxx98;
+ else if (s == "c++11")
+ v = cxx_version::cxx11;
+ else if (s == "c++14")
+ v = cxx_version::cxx14;
+ else if (s == "c++17")
+ v = cxx_version::cxx17;
+ else if (s == "c++20")
+ v = cxx_version::cxx20;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+//
+// database
+//
+
+static const char* database_[] =
+{
+ "common",
+ "mssql",
+ "mysql",
+ "oracle",
+ "pgsql",
+ "sqlite"
+};
+
+static const char* database_name_[] =
+{
+ "Common Interface",
+ "SQL Server",
+ "MySQL",
+ "Oracle",
+ "PostgreSQL",
+ "SQLite"
+};
+
+string database::
+string () const
+{
+ return database_[v_];
+}
+
+string database::
+name () const
+{
+ return database_name_[v_];
+}
+
+istream&
+operator>> (istream& is, database& db)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (database_ + sizeof (database_) / sizeof (char*));
+ const char** i (lower_bound (database_, e, s));
+
+ if (i != e && *i == s)
+ db = database::value (i - database_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, database db)
+{
+ return os << db.string ();
+}
+
+//
+// multi_database
+//
+
+static const char* multi_database_[] =
+{
+ "dynamic",
+ "static",
+ "disabled"
+};
+
+string multi_database::
+string () const
+{
+ return multi_database_[v_];
+}
+
+istream&
+operator>> (istream& is, multi_database& db)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (
+ multi_database_ + sizeof (multi_database_) / sizeof (char*) - 1);
+ const char** i (lower_bound (multi_database_, e, s));
+
+ if (i != e && *i == s)
+ db = multi_database::value (i - multi_database_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, multi_database db)
+{
+ return os << db.string ();
+}
+
+//
+// schema_format
+//
+
+static const char* schema_format_[] =
+{
+ "embedded",
+ "separate",
+ "sql"
+};
+
+string schema_format::
+string () const
+{
+ return schema_format_[v_];
+}
+
+istream&
+operator>> (istream& is, schema_format& sf)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (schema_format_ + sizeof (schema_format_) / sizeof (char*));
+ const char** i (lower_bound (schema_format_, e, s));
+
+ if (i != e && *i == s)
+ sf = schema_format::value (i - schema_format_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, schema_format sf)
+{
+ return os << sf.string ();
+}
+
+//
+// name_case
+//
+
+istream&
+operator>> (istream& is, name_case& v)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "upper")
+ v = name_case::upper;
+ else if (s == "lower")
+ v = name_case::lower;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+//
+// pgsql_version
+//
+
+istream&
+operator>> (istream& is, pgsql_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = pgsql_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, pgsql_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
+
+//
+// oracle_version
+//
+
+istream&
+operator>> (istream& is, oracle_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = oracle_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, oracle_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
+
+//
+// mssql_version
+//
+
+istream&
+operator>> (istream& is, mssql_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = mssql_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, mssql_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
diff --git a/odb/odb/option-types.hxx b/odb/odb/option-types.hxx
new file mode 100644
index 0000000..869fc83
--- /dev/null
+++ b/odb/odb/option-types.hxx
@@ -0,0 +1,391 @@
+// file : odb/option-types.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_OPTION_TYPES_HXX
+#define ODB_OPTION_TYPES_HXX
+
+#include <map>
+#include <iosfwd>
+#include <string>
+#include <cassert>
+
+#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/deferrable.hxx>
+
+using semantics::relational::qname;
+using semantics::relational::deferrable;
+
+struct cxx_version
+{
+ enum value
+ {
+ cxx98,
+ cxx11,
+ cxx14,
+ cxx17,
+ cxx20
+ };
+
+ cxx_version (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, cxx_version&);
+
+//
+//
+struct database
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ common,
+ mssql,
+ mysql,
+ oracle,
+ pgsql,
+ sqlite
+ };
+
+ database (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+ // Full name (e.g., PostgreSQL).
+ //
+ std::string
+ name () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, database&);
+
+std::ostream&
+operator<< (std::ostream&, database);
+
+//
+//
+template <typename V>
+struct database_map: std::map<database, V>
+{
+ typedef std::map<database, V> base_type;
+
+ using base_type::operator[];
+
+ V const&
+ operator[] (database const& k) const
+ {
+ typename base_type::const_iterator i (this->find (k));
+ assert (i != this->end ());
+ return i->second;
+ }
+};
+
+//
+//
+struct multi_database
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ dynamic,
+ static_,
+ disabled // Special value.
+ };
+
+ multi_database (value v = disabled) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, multi_database&);
+
+std::ostream&
+operator<< (std::ostream&, multi_database);
+
+//
+//
+struct schema_format
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ embedded,
+ separate,
+ sql
+ };
+
+ schema_format (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, schema_format&);
+
+std::ostream&
+operator<< (std::ostream&, schema_format);
+
+//
+//
+struct name_case
+{
+ enum value
+ {
+ upper,
+ lower
+ };
+
+ name_case (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, name_case&);
+
+//
+//
+struct pgsql_version
+{
+ pgsql_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, pgsql_version&);
+
+std::ostream&
+operator<< (std::ostream&, pgsql_version);
+
+//
+//
+struct oracle_version
+{
+ oracle_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, oracle_version&);
+
+std::ostream&
+operator<< (std::ostream&, oracle_version);
+
+//
+//
+struct mssql_version
+{
+ mssql_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, mssql_version&);
+
+std::ostream&
+operator<< (std::ostream&, mssql_version);
+
+#endif // ODB_OPTION_TYPES_HXX
diff --git a/odb/odb/options.cli b/odb/odb/options.cli
new file mode 100644
index 0000000..17ee438
--- /dev/null
+++ b/odb/odb/options.cli
@@ -0,0 +1,1086 @@
+// file : odb/options.cli
+// license : GNU GPL v3; see accompanying LICENSE file
+
+include <set>;
+include <vector>;
+include <string>;
+include <cstddef>;
+include <cstdint>;
+
+include <odb/option-types.hxx>;
+
+class options
+{
+ //
+ // Wrapper options. These are not passed to the plugin.
+ //
+
+ std::uint64_t --build2-metadata; // Leave undocumented/hidden.
+
+ bool --help {"Print usage information and exit."};
+ bool --version {"Print version and exit."};
+
+ //
+ // C++ preprocessor options. Also not passed to the plugin.
+ //
+ std::vector<std::string> -I
+ {
+ "<dir>",
+ "Add <dir> to the beginning of the list of directories to be searched
+ for included header files."
+ };
+
+ std::vector<std::string> -D
+ {
+ "<name>[=<def>]",
+ "Define macro <name> with definition <def>. If definition is omitted,
+ define <name> to be 1."
+ };
+
+ std::vector<std::string> -U
+ {
+ "<name>",
+ "Cancel any previous definitions of macro <name>, either built-in or
+ provided with the \cb{-D} option."
+ };
+
+ //
+ // Plugin options.
+ //
+ std::vector< ::database> --database | -d
+ {
+ "<db>",
+ "Generate code for the <db> database. Valid values are \cb{mssql},
+ \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common}
+ (multi-database mode only)."
+ };
+
+ ::multi_database --multi-database | -m = ::multi_database::disabled
+ {
+ "<type>",
+ "Enable multi-database support and specify its type. Valid values
+ 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
+ {
+ "<db>",
+ "When static multi-database support is used, specify the database that
+ should be made the default. When dynamic multi-database support is used,
+ \cb{common} is always made the default database."
+ };
+
+ bool --generate-query | -q
+ {
+ "Generate query support code. Without this support you cannot use views
+ and can only load objects via their ids."
+ };
+
+ bool --generate-prepared
+ {
+ "Generate prepared query execution support code."
+ };
+
+ bool --omit-unprepared
+ {
+ "Omit un-prepared (once-off) query execution support code."
+ };
+
+ bool --generate-session | -e
+ {
+ "Generate session support code. With this option session support will
+ be enabled by default for all the persistent classes except those for
+ which it was explicitly disabled using the \cb{db session} pragma."
+ };
+
+ bool --generate-schema | -s
+ {
+ "Generate the database schema. The database schema contains SQL
+ statements that create database tables necessary to store persistent
+ classes defined in the file being compiled. Note that by applying
+ this schema, all the existing information stored in such tables will
+ be lost.
+
+ Depending on the database being used (\cb{--database} option), the
+ schema is generated either as a standalone SQL file or embedded into
+ the generated C++ code. By default the SQL file is generated for
+ the MySQL, PostgreSQL, Oracle, and Microsoft SQL Server databases
+ and the schema is embedded into the C++ code for the SQLite database.
+ Use the \cb{--schema-format} option to alter the default schema format.
+
+ If database schema evolution support is enabled (that is, the object
+ model version is specified), then this option also triggers the
+ generation of database schema migration statements, again either as
+ standalong SQL files or embedded into the generated C++ code. You can
+ suppress the generation of schema migration statements by specifying
+ the \cb{--suppress-migration} option."
+ };
+
+ bool --generate-schema-only
+ {
+ "Generate only the database schema. Note that this option is only valid
+ when generating schema as a standalone SQL file (see \cb{--schema-format}
+ for details)."
+ };
+
+ bool --suppress-migration
+ {
+ "Suppress the generation of database schema migration statements."
+ };
+
+ bool --suppress-schema-version
+ {
+ "Suppress the generation of schema version table. If you specify this
+ option then you are also expected to manually specify the database
+ schema version and migration state at runtime using the
+ \cb{odb::database::schema_version()} function."
+ };
+
+ database_map<qname> --schema-version-table
+ {
+ "<name>",
+ "Specify the alternative schema version table name instead of the default
+ \cb{schema_version}. If you specify this option then you are also
+ expected to manually specify the schema version table name at runtime
+ using the \cb{odb::database::schema_version_table()} function. The table
+ name can be qualified."
+ };
+
+ database_map<std::set< ::schema_format> > --schema-format
+ {
+ "<format>",
+ "Generate the database schema in the specified format. Pass \cb{sql} as
+ <format> to generate the database schema as a standalone SQL file or
+ pass \cb{embedded} to embed the schema into the generated C++ code.
+ The \cb{separate} value is similar to \cb{embedded} except the schema
+ creation code is generated into a separate C++ file (\cb{name-schema.cxx}
+ by default). This value is primarily useful if you want to place the
+ schema creation functionality into a separate program or library.
+ Repeat this option to generate the same database schema in multiple
+ formats."
+ };
+
+ bool --omit-drop
+ {
+ "Omit \cb{DROP} statements from the generated database schema."
+ };
+
+ bool --omit-create
+ {
+ "Omit \cb{CREATE} statements from the generated database schema."
+ };
+
+ database_map<std::string> --schema-name
+ {
+ "<name>",
+ "Use <name> as the database schema name. Schema names are primarily
+ used to distinguish between multiple embedded schemas in the schema
+ catalog. They are not to be confused with database schemas (database
+ namespaces) which are specified with the \cb{--schema} option. If
+ this option is not specified, the empty name, which is the default
+ schema name, is used."
+ };
+
+ database_map<deferrable> --fkeys-deferrable-mode
+ {
+ "<m>",
+ "Use constraint checking mode <m> in foreign keys generated for object
+ relationships. Valid values for this option are \cb{not_deferrable},
+ \cb{immediate}, and \cb{deferred} (default). MySQL and SQL Server do
+ not support deferrable foreign keys and for these databases such keys
+ are generated commented out. Other foreign keys generated by the ODB
+ compiler (such as the ones used to support containers and polymorphic
+ hierarchies) are always generated as not deferrable.
+
+ Note also that if you use either \cb{not_deferrable} or \cb{immediate}
+ mode, then the order in which you persist, update, and erase objects
+ within a transaction becomes important."
+ };
+
+ std::string --default-pointer = "*"
+ {
+ "<ptr>",
+ "Use <ptr> as the default pointer for persistent objects and views.
+ Objects and views that do not have a pointer assigned with the
+ \cb{db pointer} pragma will use this pointer by default. The value
+ of this option can be '\cb{*}' which denotes the raw pointer and is
+ the default, or qualified name of a smart pointer class template,
+ for example, \cb{std::shared_ptr}. In the latter case, the ODB compiler
+ constructs the object or view pointer by adding a single template
+ argument of the object or view type to the qualified name, for example
+ \cb{std::shared_ptr<object>}. The ODB runtime uses object and view
+ pointers to return, and, in case of objects, pass and cache
+ dynamically allocated instances of object and view types.
+
+ Except for the raw pointer and the standard smart pointers defined
+ in the \cb{<memory>} header file, you are expected to include the
+ definition of the default pointer at the beginning of the generated
+ header file. There are two common ways to achieve this: you can either
+ include the necessary header in the file being compiled or you can use
+ the \cb{--hxx-prologue} option to add the necessary \cb{#include}
+ directive to the generated code."
+ };
+
+ std::string --session-type = "odb::session"
+ {
+ "<type>",
+ "Use <type> as the alternative session type instead of the default
+ \cb{odb::session}. This option can be used to specify a custom
+ session implementation to be use by the persistent classes. Note
+ that you will also need to include the definition of the custom
+ session type into the generated header file. This is normally
+ achieved with the \cb{--hxx-prologue*} options."
+ };
+
+ // The following option is "fake" in that it is actually handled by
+ // argv_file_scanner. We have it here to get the documentation.
+ //
+ std::string --profile | -p
+ {
+ "<name>",
+ "Specify a profile that should be used during compilation. A
+ profile is an options file. The ODB compiler first looks for
+ a database-specific version with the name constructed by appending
+ the \cb{-}\ci{database}\cb{.options} suffix to <name>, where
+ \ci{database} is the database name as specified with the
+ \cb{--database} option. If this file is not found, then the
+ ODB compiler looks for a database-independant version with the
+ name constructed by appending just the \cb{.options} suffix.
+
+ The profile options files are searched for in the same set of
+ directories as C++ headers included with the \cb{#include <...>}
+ directive (built-in paths plus those specified with the \cb{-I}
+ options). The options file is first searched for in the directory
+ itself and then in its \cb{odb/} subdirectory.
+
+ For the format of the options file refer to the \cb{--options-file}
+ option below. You can repeat this option to specify more than one
+ profile."
+ };
+
+ bool --at-once
+ {
+ "Generate code for all the input files as well as for all the files that
+ they include at once. The result is a single set of source/schema files
+ that contain all the generated code. If more than one input file is
+ specified together with this option, then the \cb{--input-name} option
+ must also be specified in order to provide the base name for the output
+ files. In this case, the directory part of such a base name is used as
+ the location of the combined file. This can be important for the
+ \cb{#include} directive resolution."
+ };
+
+ database_map<qname> --schema
+ {
+ "<schema>",
+ "Specify a database schema (database namespace) that should be
+ assigned to the persistent classes in the file being compiled.
+ Database schemas are not to be confused with database schema
+ names (schema catalog names) which are specified with the
+ \cb{--schema-name} option."
+ };
+
+ // Export control.
+ //
+ database_map<std::string> --export-symbol
+ {
+ "<symbol>",
+ "Insert <symbol> in places where DLL export/import control statements
+ (\cb{__declspec(dllexport/dllimport)}) are necessary. See also the
+ \cb{--extern-symbol} option below."
+ };
+
+ database_map<std::string> --extern-symbol
+ {
+ "<symbol>",
+ "If <symbol> is defined, insert it in places where a template
+ instantiation must be declared \cb{extern}. This option is normally
+ used together with \cb{--export-symbol} when both multi-database
+ support and queries are enabled."
+ };
+
+ // Language.
+ //
+ // @@ TODO: perhaps we should switch to latest to match how we build
+ // runtime by default?
+ //
+ cxx_version --std = cxx_version::cxx98
+ {
+ "<version>",
+ "Specify the C++ standard that should be used during compilation.
+ Valid values are \cb{c++98} (default), \cb{c++11}, \cb{c++14},
+ \cb{c++17}, and \cb{c++20}."
+ };
+
+ // Diagnostics.
+ //
+ bool --warn-hard-add
+ {
+ "Warn about hard-added data members."
+ };
+
+ bool --warn-hard-delete
+ {
+ "Warn about hard-deleted data members and persistent classes."
+ };
+
+ bool --warn-hard
+ {
+ "Warn about both hard-added and hard-deleted data members and
+ persistent classes."
+ };
+
+ // Output.
+ //
+ std::string --output-dir | -o
+ {
+ "<dir>",
+ "Write the generated files to <dir> instead of the current directory."
+ };
+
+ std::string --input-name
+ {
+ "<name>",
+ "Use <name> instead of the input file to derive the names of the
+ generated files. If the \cb{--at-once} option is specified, then
+ the directory part of <name> is used as the location of the
+ combined file. Refer to the \cb{--at-once} option for details."
+ };
+
+ database_map<std::string> --changelog
+ {
+ "<file>",
+ "Read/write changelog from/to <file> instead of the default changelog
+ file. The default changelog file name is derived from the input file
+ name and it is placed into the same directory as the input file. Note
+ that the \cb{--output-dir} option does not affect the changelog file
+ location. In other words, by default, the changelog file is treated
+ as another input rather than output even though the ODB compiler may
+ modify it. Use the \cb{--changelog-in} and \cb{--changelog-out}
+ options to specify different input and output chaneglog files."
+ };
+
+ database_map<std::string> --changelog-in
+ {
+ "<file>",
+ "Read changelog from <file> instead of the default changelog file. If
+ this option is specified, then you must also specify the output
+ chanegelog file with \cb{--changelog-out}."
+ };
+
+ database_map<std::string> --changelog-out
+ {
+ "<file>",
+ "Write changelog to <file> instead of the default changelog file. If
+ this option is specified, then you must also specify the input
+ chanegelog file with \cb{--changelog-in}."
+ };
+
+ database_map<std::string> --changelog-dir
+ {
+ "<dir>",
+ "Use <dir> instead of the input file directory as the changelog file
+ directory. This directory is also added to changelog files specified
+ with the \cb{--changelog}, \cb{--changelog-in}, and \cb{--changelog-in}
+ options unless they are absolute paths."
+ };
+
+ bool --init-changelog
+ {
+ "Force re-initialization of the changelog even if one exists (all the
+ existing change history will be lost). This option is primarily useful
+ for automated testing."
+ };
+
+ database_map<std::string> --odb-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> 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<std::string> --sql-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> to construct the name of the generated schema SQL file.
+ 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<std::string> --schema-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> 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."
+ };
+
+ database_map<std::string> --changelog-file-suffix
+ {
+ "<sfx>",
+ "Use <sfx> to construct the name of the changelog file. 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)."
+ };
+
+ std::string --hxx-suffix = ".hxx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.hxx} to construct the name of
+ the generated C++ header file."
+ };
+
+ std::string --ixx-suffix = ".ixx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.ixx} to construct the name of
+ the generated C++ inline file."
+ };
+
+ std::string --cxx-suffix = ".cxx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.cxx} to construct the name of
+ the generated C++ source file."
+ };
+
+ std::string --sql-suffix = ".sql"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.sql} to construct the name of
+ the generated database schema file."
+ };
+
+ std::string --changelog-suffix = ".xml"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.xml} to construct the name of
+ the changelog file."
+ };
+
+ // Prologues.
+ //
+ database_map<std::vector<std::string> > --hxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated schema C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated database migration file."
+ };
+
+ // Interludes.
+ //
+ database_map<std::vector<std::string> > --sql-interlude
+ {
+ "<text>",
+ "Insert <text> after all the \cb{DROP} and before any \cb{CREATE}
+ statements in the generated database schema file."
+ };
+
+ // Epilogues.
+ //
+ database_map<std::vector<std::string> > --hxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated schema C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated database migration file."
+ };
+
+ // Prologue files.
+ //
+ database_map<std::vector<std::string> > --hxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated schema
+ C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated
+ database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-prologue-file
+ {
+ "<f>",
+ "Insert the content of file <f> at the beginning of the generated database
+ migration file."
+ };
+
+ // Interlude files.
+ //
+ database_map<std::vector<std::string> > --sql-interlude-file
+ {
+ "<file>",
+ "Insert the content of <file> after all the \cb{DROP} and before any
+ \cb{CREATE} statements in the generated database schema file."
+ };
+
+ // Epilogue files.
+ //
+ database_map<std::vector<std::string> > --hxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ header
+ file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ inline
+ file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ source
+ file."
+ };
+
+ database_map<std::vector<std::string> > --schema-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated schema C++
+ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated database
+ schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-epilogue-file
+ {
+ "<f>",
+ "Insert the content of file <f> at the end of the generated database
+ migration file."
+ };
+
+ // ODB compilation prologue/epilogue.
+ //
+ database_map<std::vector<std::string> > --odb-prologue
+ {
+ "<text>",
+ "Compile <text> before the input header file. This option allows you
+ to add additional declarations, such as custom traits specializations,
+ to the ODB compilation process."
+ };
+
+ database_map<std::vector<std::string> > --odb-prologue-file
+ {
+ "<file>",
+ "Compile <file> contents before the input header file. Prologue files
+ are compiled after all the prologue text fragments (\cb{--odb-prologue}
+ option)."
+ };
+
+ database_map<std::vector<std::string> > --odb-epilogue
+ {
+ "<text>",
+ "Compile <text> after the input header file. This option allows you
+ to add additional declarations, such as custom traits specializations,
+ to the ODB compilation process."
+ };
+
+ database_map<std::vector<std::string> > --odb-epilogue-file
+ {
+ "<file>",
+ "Compile <file> contents after the input header file. Epilogue files
+ are compiled after all the epilogue text fragments (\cb{--odb-epilogue}
+ option)."
+ };
+
+ // SQL names.
+ //
+ database_map<std::string> --table-prefix
+ {
+ "<prefix>",
+ "Add <prefix> to table names and, for databases that have global index
+ and/or foreign key names, to those names as well. The prefix is added to
+ both names that were specified with the \cb{db table} and \cb{db index}
+ pragmas and those that were automatically derived from class and data
+ member names. If you require a separator, such as an underscore,
+ between the prefix and the name, then you should include it into the
+ prefix value."
+ };
+
+ database_map<std::string> --index-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_i} to construct index names.
+ The suffix is only added to names that were automatically derived from
+ data member names. If you require a separator, such as an underscore,
+ between the name and the suffix, then you should include it into the
+ suffix value."
+ };
+
+ database_map<std::string> --fkey-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_fk} to construct foreign key
+ names. If you require a separator, such as an underscore, between the
+ name and the suffix, then you should include it into the suffix value."
+ };
+
+ database_map<std::string> --sequence-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_seq} to construct sequence
+ names. If you require a separator, such as an underscore, between the
+ name and the suffix, then you should include it into the suffix value."
+ };
+
+ database_map<name_case> --sql-name-case
+ {
+ "<case>",
+ "Convert all automatically-derived SQL names to upper or lower case.
+ Valid values for this option are \cb{upper} and \cb{lower}."
+ };
+
+ database_map<std::vector<std::string> > --table-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived table names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --column-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived column names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --index-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived index names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --fkey-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived foreign key names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --sequence-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived sequence names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --statement-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived prepared statement names. See
+ the SQL NAME TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --sql-name-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform all automatically-derived SQL names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ bool --sql-name-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ SQL name \cb{--*-regex} options. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ // Accessor/modifier options.
+ //
+ std::vector<std::string> --accessor-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions used to transform
+ data member names to function names when searching for a suitable
+ accessor function. The argument to this option is a Perl-like regular
+ expression in the form \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
+ Any character can be used as a delimiter instead of '\cb{/}' and the
+ delimiter can be escaped inside \ci{pattern} and \ci{replacement}
+ with a backslash (\cb{\\}). You can specify multiple regular
+ expressions by repeating this option.
+
+ All the regular expressions are tried in the order specified and
+ the first expression that produces a suitable accessor function is
+ used. Each expression is tried twice: first with the actual member
+ name and then with the member's \i{public name} which is obtained by
+ removing the common member name decorations, such as leading and
+ trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
+ includes a number of built-in expressions for commonly used accessor
+ names, such as \cb{get_foo}, \cb{getFoo}, \cb{getfoo}, and just
+ \cb{foo}. The built-in expressions are tried last.
+
+ As an example, the following expression transforms data members with
+ public names in the form \cb{foo} to accessor names in the form
+ \cb{GetFoo}:
+
+ \cb{/(.+)/Get\\u$1/}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --accessor-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--accessor-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ std::vector<std::string> --modifier-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions used to transform
+ data member names to function names when searching for a suitable
+ modifier function. The argument to this option is a Perl-like regular
+ expression in the form \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
+ Any character can be used as a delimiter instead of '\cb{/}' and the
+ delimiter can be escaped inside \ci{pattern} and \ci{replacement}
+ with a backslash (\cb{\\}). You can specify multiple regular
+ expressions by repeating this option.
+
+ All the regular expressions are tried in the order specified and
+ the first expression that produces a suitable modifier function is
+ used. Each expression is tried twice: first with the actual member
+ name and then with the member's \i{public name} which is obtained by
+ removing the common member name decorations, such as leading and
+ trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
+ includes a number of built-in expressions for commonly used modifier
+ names, such as \cb{set_foo}, \cb{setFoo}, \cb{setfoo}, and just
+ \cb{foo}. The built-in expressions are tried last.
+
+ As an example, the following expression transforms data members with
+ public names in the form \cb{foo} to modifier names in the form
+ \cb{SetFoo}:
+
+ \cb{/(.+)/Set\\u$1/}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --modifier-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--modifier-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ // Include options.
+ //
+ bool --include-with-brackets
+ {
+ "Use angle brackets (<>) instead of quotes (\"\") in the generated
+ \cb{#include} directives."
+ };
+
+ std::string --include-prefix
+ {
+ "<prefix>",
+ "Add <prefix> to the generated \cb{#include} directive paths."
+ };
+
+ std::vector<std::string> --include-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions used to transform
+ generated \cb{#include} directive paths. The argument to this option
+ is a Perl-like regular expression in the form
+ \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}. Any character can be
+ used as a delimiter instead of '\cb{/}' and the delimiter can be escaped
+ inside \ci{pattern} and \ci{replacement} with a backslash (\cb{\\}).
+ You can specify multiple regular expressions by repeating this option.
+ All the regular expressions are tried in the order specified and the
+ first expression that matches is used.
+
+ As an example, the following expression transforms include paths in
+ the form \cb{foo/bar-odb.h} to paths in the form
+ \cb{foo/generated/bar-odb.h}:
+
+ \cb{%foo/(.+)-odb.h%foo/generated/$1-odb.h%}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --include-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--include-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ std::string --guard-prefix
+ {
+ "<prefix>",
+ "Add <prefix> to the generated header inclusion guards. The prefix is
+ transformed to upper case and characters that are illegal in a
+ preprocessor macro name are replaced with underscores."
+ };
+
+ bool --show-sloc
+ {
+ "Print the number of generated physical source lines of code (SLOC)."
+ };
+
+ std::size_t --sloc-limit
+ {
+ "<num>",
+ "Check that the number of generated physical source lines of code (SLOC)
+ does not exceed <num>."
+ };
+
+ // The following option is "fake" in that it is actually handled by
+ // argv_file_scanner. We have it here to get the documentation.
+ //
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=}) and an
+ option value. Empty lines and lines starting with \cb{#} are ignored.
+ Option values can be enclosed in double (\cb{\"}) or single (\cb{'})
+ quotes to preserve leading and trailing whitespaces as well as to specify
+ empty values. If the value itself contains trailing or leading quotes,
+ enclose it with an extra pair of quotes, for example \cb{'\"x\"'}.
+ Non-leading and non-trailing quotes are interpreted as being part of the
+ option value.
+
+ The semantics of providing options in a file is equivalent to providing
+ the same set of options in the same order on the command line at the
+ point where the \cb{--options-file} option is specified except that
+ the shell escaping and quoting is not required. Repeat this option
+ to specify more than one options file."
+ };
+
+ std::vector<std::string> -x
+ {
+ "<option>",
+ "Pass <option> to the underlying C++ compiler (\cb{g++}). The <option>
+ value that doesn't start with '\cb{-}' is considered the \cb{g++}
+ executable name."
+ };
+
+ bool -v {"Print the commands executed to run the stages of compilation."};
+
+ bool --trace {"Trace the compilation process."};
+
+ //
+ // MySQL-specific options.
+ //
+
+ std::string --mysql-engine = "InnoDB"
+ {
+ "<engine>",
+ "Use <engine> instead of the default \cb{InnoDB} in the generated
+ database schema file. For more information on the storage engine
+ options see the MySQL documentation. If you would like to use the
+ database-default engine, pass \cb{default} as the value for this
+ option."
+ };
+
+ //
+ // SQLite-specific options.
+ //
+
+ bool --sqlite-override-null
+ {
+ "Make all columns in the generated database schema allow \cb{NULL}
+ values. This is primarily useful in schema migration since SQLite
+ does not support dropping of columns. By making all columns \cb{NULL}
+ we can later \"delete\" them by setting their values to \cb{NULL}.
+ Note that this option overrides even the \cb{not_null} pragma."
+ };
+
+ bool --sqlite-lax-auto-id
+ {
+ "Do not force monotonically increasing automatically-assigned
+ object ids. In this mode the generated database schema omits the
+ \cb{AUTOINCREMENT} keyword which results in faster object persistence
+ but may lead to automatically-assigned ids not being in a strictly
+ ascending order. Refer to the SQLite documentation for details."
+ };
+
+ //
+ // PostgreSQL-specific options.
+ //
+
+ ::pgsql_version --pgsql-server-version (7, 4)
+ {
+ "<ver>",
+ "Specify the minimum PostgreSQL server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++
+ code and schema. The version must be in the \c{\i{major}\b{.}\i{minor}}
+ form, for example, \cb{9.1}. If this option is not specified, then
+ \cb{7.4} or later is assumed."
+ };
+
+ //
+ // Oracle-specific options.
+ //
+
+ ::oracle_version --oracle-client-version (10, 1)
+ {
+ "<ver>",
+ "Specify the minimum Oracle client library (OCI) version with which the
+ generated C++ code will be linked. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++
+ code. The version must be in the \c{\i{major}\b{.}\i{minor}} form,
+ for example, \cb{11.2}. If this option is not specified, then
+ \cb{10.1} or later is assumed."
+ };
+
+ bool --oracle-warn-truncation
+ {
+ "Warn about SQL names that are longer than 30 characters and are
+ therefore truncated. Note that during database schema generation
+ (\cb{--generate-schema}) ODB detects when such truncations lead
+ to name conflicts and issues diagnostics even without this option
+ specified."
+ };
+
+ //
+ // SQL Server-specific options.
+ //
+
+ ::mssql_version --mssql-server-version (10, 0)
+ {
+ "<ver>",
+ "Specify the minimum SQL Server server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++
+ code and schema. The version must be in the \c{\i{major}\b{.}\i{minor}}
+ form, for example, \cb{9.0} (SQL Server 2005), \cb{10.5} (2008R2), or
+ \cb{11.0} (2012). If this option is not specified, then \cb{10.0} (SQL
+ Server 2008) or later is assumed."
+ };
+
+ unsigned int --mssql-short-limit = 1024
+ {
+ "<size>",
+ "Specify the short data size limit. If a character, national character, or
+ binary data type has a maximum length (in bytes) less than or equal to
+ this limit, then it is treated as \i{short data}, otherwise it is \i{long
+ data}. For short data ODB pre-allocates an intermediate buffer of
+ the maximum size and binds it directly to a parameter or result
+ column. This way the underlying API (ODBC) can read/write directly
+ from/to this buffer. In the case of long data, the data is read/written
+ in chunks using the \cb{SQLGetData()}/\cb{SQLPutData()} ODBC functions.
+ While the long data approach reduces the amount of memory used by the
+ application, it may require greater CPU resources. The default short
+ data limit is 1024 bytes. When setting a custom short data limit, make
+ sure that it is sufficiently large so that no object id in the
+ application is treated as long data."
+ };
+};
diff --git a/odb/odb/parser.cxx b/odb/odb/parser.cxx
new file mode 100644
index 0000000..c026808
--- /dev/null
+++ b/odb/odb/parser.cxx
@@ -0,0 +1,2403 @@
+// file : odb/parser.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // Keep it first.
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#include <cassert>
+#include <sstream>
+#include <iostream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/parser.hxx>
+#include <odb/semantics.hxx>
+
+using namespace std;
+using namespace semantics;
+
+class parser::impl
+{
+public:
+ typedef parser::failed failed;
+
+ impl (options const&, loc_pragmas&, ns_loc_pragmas&, decl_pragmas&);
+
+ unique_ptr<unit>
+ parse (tree global_scope, path const& main_file);
+
+private:
+ typedef semantics::access access;
+
+ // Extended GCC tree declaration that is either a GCC tree
+ // declaration, a virtual declaration, or a pragma. If it is
+ // a pragma, then the assoc flag indicated whether this pragma
+ // has been associated with a declaration. Otherwise, the assoc
+ // flag indicates whether pragmas have been associated with this
+ // declaration (we use this to ignore certain declarations for
+ // pragma association purposes, e.g., the anonymous type in
+ // struct {...} m_).
+ //
+ struct tree_decl
+ {
+ tree decl;
+ virt_declaration const* vdecl;
+ pragma const* prag;
+ mutable bool assoc; // Allow modification via std::set iterator.
+
+ tree_decl (tree d): decl (d), vdecl (0), prag (0), assoc (false) {}
+ tree_decl (virt_declaration const& d)
+ : decl (0), vdecl (&d), prag (0), assoc (false) {}
+ tree_decl (pragma const& p)
+ : decl (0), vdecl (0), prag (&p), assoc (false) {}
+
+ bool
+ operator< (tree_decl const& y) const;
+ };
+
+ typedef multiset<tree_decl> decl_set;
+
+private:
+ void
+ collect (tree ns);
+
+ void
+ emit ();
+
+ // Emit a type declaration. This is either a named class-type definition/
+ // declaration or a typedef. In the former case the function returns the
+ // newly created type node. In the latter case it returns 0.
+ //
+ type*
+ emit_type_decl (tree);
+
+ // Emit a template declaration.
+ //
+ void
+ emit_template_decl (tree);
+
+ class_template&
+ emit_class_template (tree, bool stub = false);
+
+ union_template&
+ emit_union_template (tree, bool stub = false);
+
+ template <typename T>
+ T&
+ emit_class (tree, path const& f, size_t l, size_t c, bool stub = false);
+
+ template <typename T>
+ T&
+ emit_union (tree, path const& f, size_t l, size_t c, bool stub = false);
+
+ // Access is not used when creating a stub.
+ //
+ enum_&
+ emit_enum (tree,
+ access,
+ path const& f,
+ size_t l,
+ size_t c,
+ bool stub = false);
+
+ // Create new or find existing semantic graph type.
+ //
+ type&
+ emit_type (tree, access, path const& f, size_t l, size_t c);
+
+ type&
+ create_type (tree, access, path const& f, size_t l, size_t c);
+
+ string
+ emit_type_name (tree, bool direct = true);
+
+
+ //
+ // Pragma handling.
+ //
+ void
+ add_pragma (node&, pragma const&);
+
+ // Process positioned and named pragmas.
+ //
+ void
+ process_pragmas (declaration const&,
+ node&,
+ string const& name,
+ decl_set::const_iterator begin,
+ decl_set::const_iterator cur,
+ decl_set::const_iterator end);
+
+ // Process named pragmas only.
+ //
+ void
+ process_named_pragmas (declaration const&, node&);
+
+ void
+ diagnose_unassoc_pragmas (decl_set const&);
+
+ // Return declaration's fully-qualified scope name (e.g., ::foo::bar).
+ //
+ string
+ fq_scope (tree);
+
+ // Return declaration's access.
+ //
+ access
+ decl_access (tree decl)
+ {
+ // Note that TREE_PUBLIC() returns something other than what we need.
+ //
+ if (TREE_PRIVATE (decl))
+ return access::private_;
+
+ if (TREE_PROTECTED (decl))
+ return access::protected_;
+
+ return access::public_;
+ }
+
+ //
+ //
+ template <typename T>
+ void
+ define_fund (tree);
+
+private:
+ options const& ops_;
+ loc_pragmas& loc_pragmas_;
+ ns_loc_pragmas& ns_loc_pragmas_;
+ decl_pragmas& decl_pragmas_;
+
+ bool trace;
+ ostream& ts;
+
+ unit* unit_;
+ scope* scope_;
+ vector<scope*> class_scopes_; // Current hierarchy of class-like scopes.
+ size_t error_;
+
+ decl_set decls_;
+
+ typedef map<location_t, tree> decl_map;
+ decl_map all_decls_;
+};
+
+bool parser::impl::tree_decl::
+operator< (tree_decl const& y) const
+{
+ location_t xl, yl;
+ int xb (0), yb (0);
+
+ if (decl != 0)
+ xl = real_source_location (decl);
+ else if (vdecl != 0)
+ {
+ xl = vdecl->ord;
+ xb = vdecl->ord_bias;
+ }
+ else
+ xl = prag->loc;
+
+ if (y.decl != 0)
+ yl = real_source_location (y.decl);
+ else if (y.vdecl != 0)
+ {
+ yl = y.vdecl->ord;
+ yb = y.vdecl->ord_bias;
+ }
+ else
+ yl = y.prag->loc;
+
+ // If both are virtual and at the same location, use the definition
+ // location to order them.
+ //
+ if (vdecl != 0 && y.vdecl != 0 && xl == yl && xb == yb)
+ {
+ xl = vdecl->loc;
+ yl = y.vdecl->loc;
+ }
+
+ return xl < yl || (xl == yl && xb < yb);
+}
+
+//
+// Function templates.
+//
+
+template <typename T>
+void parser::impl::
+define_fund (tree t)
+{
+ t = TYPE_MAIN_VARIANT (t);
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t))));
+
+ T& node (unit_->new_fund_node<T> (t));
+ unit_->new_edge<defines> (*scope_, node, name);
+ unit_->insert (t, node);
+
+ process_named_pragmas (t, node);
+}
+
+template <typename T>
+T& parser::impl::
+emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub)
+{
+ c = TYPE_MAIN_VARIANT (c);
+
+ // See if there is a stub already for this type.
+ //
+ T* c_node (0);
+
+ if (node* n = unit_->find (c))
+ {
+ c_node = &dynamic_cast<T&> (*n);
+ }
+ else
+ {
+ c_node = &unit_->new_node<T> (file, line, clmn, c);
+ unit_->insert (c, *c_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (c))
+ return *c_node;
+
+ // Note: "include" the base classes into the class scope (see comment for
+ // self-typedefs in emit_type_decl()).
+ //
+ class_scopes_.push_back (c_node);
+
+ // Traverse base information.
+ //
+ tree bis (TYPE_BINFO (c));
+ size_t n (bis ? BINFO_N_BASE_BINFOS (bis) : 0);
+
+ for (size_t i (0); i < n; i++)
+ {
+ tree bi (BINFO_BASE_BINFO (bis, i));
+ access a (access::public_);
+
+ if (BINFO_BASE_ACCESSES (bis))
+ {
+ tree ac (BINFO_BASE_ACCESS (bis, i));
+
+ if (ac == NULL_TREE || ac == access_public_node)
+ {
+ a = access::public_;
+ }
+ else if (ac == access_protected_node)
+ {
+ a = access::protected_;
+ }
+ else
+ {
+ assert (ac == access_private_node);
+ a = access::private_;
+ }
+ }
+
+ bool virt (BINFO_VIRTUAL_P (bi));
+ tree base (TYPE_MAIN_VARIANT (BINFO_TYPE (bi)));
+
+ // Find the corresponding graph node. If we cannot find one then
+ // the base is a template instantiation since an ordinary class
+ // has to be defined (complete) in order to be a base.
+ //
+ class_* b_node (0);
+ string name;
+
+ if (node* n = unit_->find (base))
+ {
+ b_node = &dynamic_cast<class_&> (*n);
+
+ if (trace)
+ name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (base)));
+ }
+ else
+ {
+ // Use public access for a template instantiation in the inheritance
+ // declaration.
+ //
+ b_node = &dynamic_cast<class_&> (
+ emit_type (base, access::public_, file, line, clmn));
+
+ if (trace)
+ name = emit_type_name (base);
+ }
+
+ unit_->new_edge<inherits> (*c_node, *b_node, a, virt);
+
+ if (trace)
+ ts << "\t" << a.string () << (virt ? " virtual" : "") << " base "
+ << name << " (" << static_cast<type*> (b_node) << ")" << endl;
+ }
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (c)); d != NULL_TREE; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ if (!DECL_SELF_REFERENCE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ if (!DECL_ARTIFICIAL (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Add virtual declarations if any.
+ //
+ {
+ virt_declarations::const_iterator i (virt_declarations_.find (c));
+
+ if (i != virt_declarations_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Add location pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (c));
+
+ if (i != loc_pragmas_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = c_node;
+
+ for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag != 0)
+ continue;
+
+ // Handle virtual declarations.
+ //
+ if (i->vdecl != 0)
+ {
+ virt_declaration const& vd (*i->vdecl);
+
+ switch (vd.tree_code)
+ {
+ case FIELD_DECL:
+ {
+ // First check that it doesn't conflict with any of the real
+ // data members defined in this class.
+ //
+ tree d (
+ lookup_qualified_name (
+ c, get_identifier (vd.name.c_str ()), false, false));
+
+ if (d != error_mark_node && TREE_CODE (d) == FIELD_DECL)
+ {
+ error (vd.loc) << "virtual data member declaration '" << vd.name
+ << "' conflicts with a previous declaration"
+ << endl;
+
+ location_t l (real_source_location (d));
+ info (l) << "'" << vd.name << "' was previously declared here"
+ << endl;
+
+ throw failed ();
+ }
+
+ path file (LOCATION_FILE (vd.loc));
+ size_t line (LOCATION_LINE (vd.loc));
+ size_t clmn (LOCATION_COLUMN (vd.loc));
+
+ access a (access::public_);
+
+ type& type_node (emit_type (vd.type, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, tree (0)));
+
+ unit_->new_edge<names> (*c_node, member_node, vd.name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (vd.type))
+ edge.hint (*hint);
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (vd, member_node, vd.name, b, i, e);
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+ continue;
+ }
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (d));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ // If this is a bit-field then TREE_TYPE may be a modified type
+ // with lesser precision. In this case, DECL_BIT_FIELD_TYPE
+ // will be the type that was original specified. Use that type
+ // for now. Furthermore, bitfields can be anonymous, which we
+ // ignore.
+ //
+ //
+ bool bf (DECL_C_BIT_FIELD (d));
+
+ if (bf && DECL_NAME (d) == 0)
+ break;
+
+ // Another case where we can have NULL name is anonymous struct
+ // or union extension, for example:
+ //
+ // struct s
+ // {
+ // union
+ // {
+ // int a;
+ // int b;
+ // };
+ // };
+ //
+ // GCC appears to create a fake member for such a struct/union
+ // without any name. Ignore such members for now.
+ //
+ if (DECL_NAME (d) == 0)
+ break;
+
+ tree t (bf ? DECL_BIT_FIELD_TYPE (d) : TREE_TYPE (d));
+
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
+
+ path file (DECL_SOURCE_FILE (d));
+ size_t line (DECL_SOURCE_LINE (d));
+ size_t clmn (DECL_SOURCE_COLUMN (d));
+
+ access a (decl_access (d));
+
+ type& type_node (emit_type (t, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
+
+ unit_->new_edge<names> (*c_node, member_node, name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (t))
+ edge.hint (*hint);
+
+ if (trace)
+ {
+ string type_name (emit_type_name (t));
+
+ ts << "\t" << a.string () << " data member " << type_name
+ << " (" << &type_node << ") " << name << " at "
+ << file << ":" << line << endl;
+ }
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (d, member_node, name, b, i, e);
+
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+
+ return *c_node;
+}
+
+template <typename T>
+T& parser::impl::
+emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub)
+{
+ u = TYPE_MAIN_VARIANT (u);
+
+ // See if there is a stub already for this type.
+ //
+ T* u_node (0);
+
+ if (node* n = unit_->find (u))
+ {
+ u_node = &dynamic_cast<T&> (*n);
+ }
+ else
+ {
+ u_node = &unit_->new_node<T> (file, line, clmn, u);
+ unit_->insert (u, *u_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (u))
+ return *u_node;
+
+ class_scopes_.push_back (u_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ if (!DECL_SELF_REFERENCE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ if (!DECL_ARTIFICIAL (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Add location pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (u));
+
+ if (i != loc_pragmas_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = u_node;
+
+ for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (d));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ // We can have NULL name in case of anonymous struct or union
+ // extension, for example:
+ //
+ // union s
+ // {
+ // struct
+ // {
+ // int a;
+ // int b;
+ // };
+ // int c;
+ // };
+ //
+ // GCC appears to create a fake member for such a struct/union
+ // without any name. Ignore such members for now.
+ //
+ if (DECL_NAME (d) == 0)
+ break;
+
+ tree t (TREE_TYPE (d));
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
+
+ path file (DECL_SOURCE_FILE (d));
+ size_t line (DECL_SOURCE_LINE (d));
+ size_t clmn (DECL_SOURCE_COLUMN (d));
+
+ access a (decl_access (d));
+
+ type& type_node (emit_type (t, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
+
+ unit_->new_edge<names> (*u_node, member_node, name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (t))
+ edge.hint (*hint);
+
+ if (trace)
+ {
+ string type_name (emit_type_name (t));
+
+ ts << "\t" << a.string () << " union member " << type_name
+ << " (" << &type_node << ") " << name << " at "
+ << file << ":" << line << endl;
+ }
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (d, member_node, name, b, i, e);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *u_node;
+}
+
+//
+// Functions.
+//
+
+parser::impl::
+impl (options const& ops,
+ loc_pragmas & lp,
+ ns_loc_pragmas& nslp,
+ decl_pragmas& dp)
+ : ops_ (ops),
+ loc_pragmas_ (lp),
+ ns_loc_pragmas_ (nslp),
+ decl_pragmas_ (dp),
+ trace (ops.trace ()),
+ ts (cerr)
+{
+}
+
+unique_ptr<unit> parser::impl::
+parse (tree global_scope, path const& main_file)
+{
+ unique_ptr<unit> u (new unit (main_file));
+ u->insert (global_namespace, *u);
+ process_named_pragmas (global_namespace, *u);
+
+ unit_ = u.get ();
+ scope_ = unit_;
+ error_ = 0;
+
+ // Define fundamental types.
+ //
+ define_fund<fund_void> (void_type_node);
+ define_fund<fund_bool> (boolean_type_node);
+ define_fund<fund_char> (char_type_node);
+ define_fund<fund_wchar> (wchar_type_node);
+
+ if (ops_.std () >= cxx_version::cxx11)
+ {
+ define_fund<fund_char16> (char16_type_node);
+ define_fund<fund_char32> (char32_type_node);
+ }
+
+ define_fund<fund_signed_char> (signed_char_type_node);
+ define_fund<fund_unsigned_char> (unsigned_char_type_node);
+ define_fund<fund_short> (short_integer_type_node);
+ define_fund<fund_unsigned_short> (short_unsigned_type_node);
+ define_fund<fund_int> (integer_type_node);
+ define_fund<fund_unsigned_int> (unsigned_type_node);
+ define_fund<fund_long> (long_integer_type_node);
+ define_fund<fund_unsigned_long> (long_unsigned_type_node);
+ define_fund<fund_long_long> (long_long_integer_type_node);
+ define_fund<fund_unsigned_long_long> (long_long_unsigned_type_node);
+ define_fund<fund_float> (float_type_node);
+ define_fund<fund_double> (double_type_node);
+ define_fund<fund_long_double> (long_double_type_node);
+
+ // First collect all the namespace-level declarations we are
+ // interested in in the line-decl map so that they appear in
+ // the source code order.
+ //
+ collect (global_scope);
+
+ // Add namespace-level position pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (global_namespace));
+
+ if (i != loc_pragmas_.end ())
+ decls_.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Convert position namespace pragmas to name pragmas.
+ //
+ for (ns_loc_pragmas::const_iterator i (ns_loc_pragmas_.begin ());
+ i != ns_loc_pragmas_.end (); ++i)
+ {
+ pragma const& p (i->pragma);
+
+ decl_map::const_iterator j (all_decls_.lower_bound (p.loc));
+
+ if (j == all_decls_.end ())
+ {
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "namespace declaration" << endl;
+ error_++;
+ continue;
+ }
+
+ // Find the "namespace difference" between this declaration and
+ // the pragma's namespace. The outermost namespace in the result
+ // is what we are looking for.
+ //
+ tree ns (0);
+
+ for (tree prev (j->second), scope (CP_DECL_CONTEXT (prev));;
+ scope = CP_DECL_CONTEXT (scope))
+ {
+ if (scope == i->ns)
+ {
+ ns = prev;
+ break;
+ }
+
+ if (scope == global_namespace)
+ break;
+
+ prev = scope;
+ }
+
+ if (ns == 0 || TREE_CODE (ns) != NAMESPACE_DECL)
+ {
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "namespace declaration" << endl;
+ error_++;
+ continue;
+ }
+
+ pragma_set& s (decl_pragmas_[ns]);
+ pragma_set::iterator it (s.find (p.context_name));
+
+ // Make sure we override only if this pragma came after the one
+ // already in the set.
+ //
+ if (it == s.end () || it->second.loc <= p.loc)
+ s.insert (p);
+ }
+
+ // Construct the semantic graph.
+ //
+ if (error_ == 0)
+ emit ();
+
+ if (error_ > 0)
+ throw failed ();
+
+ return u;
+}
+
+void parser::impl::
+collect (tree ns)
+{
+ cp_binding_level* level = NAMESPACE_LEVEL (ns);
+ tree decl = level->names;
+
+ // Collect declarations.
+ //
+ for (; decl != NULL_TREE; decl = TREE_CHAIN (decl))
+ {
+ all_decls_[real_source_location (decl)] = decl;
+
+ if (DECL_IS_BUILTIN (decl))
+ continue;
+
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ {
+ // Skip special type declarations.
+ //
+ if (DECL_NAME (decl) == NULL_TREE)
+ continue;
+
+ tree type (TREE_TYPE (decl));
+ if (LAMBDA_TYPE_P (type))
+ continue;
+
+ decls_.insert (decl);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (decl))
+ decls_.insert (decl);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Traverse namespaces.
+ //
+ for (
+#if BUILDING_GCC_MAJOR >= 8
+ decl = level->names;
+#else
+ decl = level->namespaces;
+#endif
+ decl != NULL_TREE;
+ decl = TREE_CHAIN (decl))
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ // Now namespaces are interleaved with other declarations. In fact, we
+ // could probably collect everything in a single pass.
+ //
+ if (TREE_CODE (decl) != NAMESPACE_DECL)
+ continue;
+#endif
+
+ // Ignore namespace aliases.
+ //
+ if (DECL_NAMESPACE_ALIAS (decl))
+ continue;
+
+ if (!DECL_IS_BUILTIN (decl) || DECL_NAMESPACE_STD_P (decl))
+ {
+ tree dn (DECL_NAME (decl));
+
+ if (trace)
+ {
+ char const* name (dn ? IDENTIFIER_POINTER (dn) : "<anonymous>");
+
+ ts << "namespace " << name << " at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+ }
+
+ // Skip anonymous namespaces (there could be nothing of interest to us
+ // inside but they wreck havoc with our attempts to sort declarations
+ // into namespaces).
+ //
+ if (dn != 0)
+ collect (decl);
+ }
+ }
+}
+
+void parser::impl::
+emit ()
+{
+ for (decl_set::const_iterator b (decls_.begin ()), i (b),
+ e (decls_.end ()); i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree decl (i->decl);
+
+ // Get this declaration's namespace and unwind our scope until
+ // we find a common prefix of namespaces.
+ //
+ string pfx;
+ string ns (fq_scope (decl));
+
+ for (pfx = scope_->fq_name (); !pfx.empty (); pfx = scope_->fq_name ())
+ {
+ size_t n (pfx.size ());
+
+ // Make sure we handle cases like ns="::foobar", pfx="::foo".
+ //
+ if (ns.compare (0, n, pfx) == 0 && (ns.size () == n || ns[n - 1] == ':'))
+ break;
+
+ if (trace)
+ ts << "closing namespace " << scope_->name () << endl;
+
+ scope_ = &scope_->scope_ ();
+ }
+
+ // Build the rest of the namespace hierarchy for this declaration.
+ //
+ if (ns != pfx)
+ {
+ path f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ for (size_t b (pfx.size () + 2), e (ns.find ("::", b));
+ b != string::npos;)
+ {
+ string n (ns, b, e == string::npos ? e : e - b);
+
+ if (trace)
+ ts << "opening namespace " << n << " for "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ // Use the declarations's file, line, and column as an
+ // approximation for this namespace origin. Also resolve
+ // the tree node for this namespace.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ tree tree_node (
+ get_namespace_binding (
+ scope_->tree_node (), get_identifier (n.c_str ())));
+#else
+ tree tree_node (
+ namespace_binding (
+ get_identifier (n.c_str ()), scope_->tree_node ()));
+#endif
+
+ namespace_& node (unit_->new_node<namespace_> (f, l, c, tree_node));
+ unit_->new_edge<defines> (*scope_, node, n);
+
+ if (namespace_* orig =
+ dynamic_cast<namespace_*> (unit_->find (tree_node)))
+ {
+ // This is an extension.
+ //
+ node.original (*orig);
+ }
+ else
+ {
+ // This is the original. Add it to the map and process any
+ // pragmas it might have (at this stage namespaces can only
+ // have name pragmas).
+ //
+ unit_->insert (tree_node, node);
+ process_named_pragmas (tree_node, node);
+ }
+
+ scope_ = &node;
+
+ if (e == string::npos)
+ b = e;
+ else
+ {
+ b = e + 2;
+ e = ns.find ("::", b);
+ }
+ }
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (decl));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (decl);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ assert (class_scopes_.empty ());
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls_);
+}
+
+type* parser::impl::
+emit_type_decl (tree decl)
+{
+ tree t (TREE_TYPE (decl));
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ tree decl_name (DECL_NAME (decl));
+ char const* name (IDENTIFIER_POINTER (decl_name));
+
+ if (DECL_ARTIFICIAL (decl) &&
+ (tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE))
+ {
+ // If we have an anonymous class typedef, use the user-
+ // supplied name instead of the synthesized one. ARM
+ // says that in typedef struct {} S; S becomes struct's
+ // name.
+ //
+ if (IDENTIFIER_ANON_P (decl_name))
+ {
+ tree d (TYPE_NAME (t));
+
+ if (d != NULL_TREE &&
+ !DECL_ARTIFICIAL (d) &&
+ DECL_NAME (d) != NULL_TREE &&
+ !IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ decl = d;
+ decl_name = DECL_NAME (decl);
+ name = IDENTIFIER_POINTER (decl_name);
+ }
+ else
+ {
+ // This type has only the synthesized name which means that
+ // it is either typedef'ed as a derived type or it is used
+ // to declare a varibale or similar. The first case will be
+ // covered by the typedef handling code below. The second
+ // case will be covere by emit_type().
+ //
+ return 0;
+ }
+ }
+
+ path file (DECL_SOURCE_FILE (decl));
+ size_t line (DECL_SOURCE_LINE (decl));
+ size_t clmn (DECL_SOURCE_COLUMN (decl));
+
+ type* node (0);
+
+ // Pointers to member functions are represented as record
+ // types. Detect and handle this case.
+ //
+ if (TYPE_PTRMEMFUNC_P (t))
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ node = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_member_function_type");
+ unit_->insert (t, *node);
+ }
+ else
+ {
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " " << name
+ << " at " << file << ":" << line << endl;
+
+ switch (tc)
+ {
+ case RECORD_TYPE:
+ {
+ node = &emit_class<class_> (t, file, line, clmn);
+ break;
+ }
+ case UNION_TYPE:
+ {
+ node = &emit_union<union_> (t, file, line, clmn);
+ break;
+ }
+ case ENUMERAL_TYPE:
+ {
+ node = &emit_enum (t, decl_access (decl), file, line, clmn);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " " << name
+ << " (" << node << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+ }
+
+ if (COMPLETE_TYPE_P (t))
+ unit_->new_edge<defines> (*scope_, *node, name);
+ else
+ unit_->new_edge<declares> (*scope_, *node, name);
+
+ return node;
+ }
+ else
+ {
+ // Normal typedef. We need to detect and ignore the anonymous
+ // class typedef case described above since we already used
+ // this name to define the class.
+ //
+ if ((tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE) &&
+ TYPE_NAME (TYPE_MAIN_VARIANT (t)) == decl)
+ return 0;
+
+ path f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ type& node (emit_type (t, decl_access (decl), f, l, c));
+
+ // Omit inner self-typedefs (e.g., a class typedefs itself in its own
+ // scope). Such aliases don't buy us anything (in particular, they cannot
+ // be used to form an fq-name) but they do cause scoping cycles if this
+ // name happens to be used to find outer scope (see scope::scope_()).
+ // Note that this means we can now have class template instantiations that
+ // are not named and therefore don't belong to any scope.
+ //
+ // Note that emit_type() might still enter this decl as a hint. It's fuzzy
+ // whether this is harmless or not.
+ //
+ // Note also that using the normal scope hierarchy does not work in more
+ // complex cases where templates cross-self-typedef. So instead we now use
+ // a special-purpose mechanism (class_scopes_). Note for this to work
+ // correctly (hopefully), the class should be "in scope" for its bases.
+ // Here is a representative examples (inspired by code in Eigen):
+ //
+ // template <typename M>
+ // struct PlainObjectBase
+ // {
+ // typedef M Self;
+ // };
+ //
+ // template <typename T, int X, int Y>
+ // struct Matrix: PlainObjectBase<Matrix<T, X, Y>>
+ // {
+ // typedef PlainObjectBase<Matrix> Base;
+ // typedef Matrix Self;
+ // };
+ //
+ // typedef Matrix<double, 3, 1> Vector3d;
+ //
+ // Here we want both Self's (but not Base) to be skipped.
+ //
+ if (scope* s = dynamic_cast<scope*> (&node))
+ {
+ for (auto i (class_scopes_.rbegin ()); i != class_scopes_.rend (); ++i)
+ {
+ if (s == *i)
+ {
+ if (trace)
+ {
+ string s (emit_type_name (t, false));
+
+ ts << "omitting inner self-typedef " << s << " (" << &node
+ << ") -> " << name << " at " << f << ":" << l << endl;
+ }
+ return 0;
+ }
+ }
+ }
+
+ typedefs& edge (unit_->new_edge<typedefs> (*scope_, node, name));
+
+ // Find our hint.
+ //
+ if (tree ot = DECL_ORIGINAL_TYPE (decl))
+ {
+ if (names* hint = unit_->find_hint (ot))
+ edge.hint (*hint);
+ }
+
+ // Add this edge to the hint map. It may already be there if we
+ // are handling something like this:
+ //
+ // typedef foo bar;
+ // typedef bar foo;
+ //
+ // GCC also appears to re-purpose a node for another name (not
+ // sure if its a bug or a feature), so use the latest seen name.
+ //
+ unit_->insert_hint (t, edge);
+
+ if (trace)
+ {
+ string s (emit_type_name (t, false));
+
+ ts << "typedef " << s << " (" << &node << ") -> " << name
+ << " at " << f << ":" << l << endl;
+ }
+
+ return 0;
+ }
+}
+
+void parser::impl::
+emit_template_decl (tree decl)
+{
+ // Currently we only handle class/union templates.
+ //
+ tree t (TREE_TYPE (DECL_TEMPLATE_RESULT (decl)));
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ if (trace)
+ {
+ ts << gcc_tree_code_name(tc) << " template (" << decl << ") "
+ << IDENTIFIER_POINTER (DECL_NAME (decl)) << " (" << t << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ ts << "specializations:" << endl;
+
+ for (tree s (DECL_TEMPLATE_SPECIALIZATIONS (decl));
+ s != NULL_TREE; s = TREE_CHAIN (s))
+ {
+ tree t (TREE_TYPE (s));
+ tree d (TYPE_NAME (t));
+
+ ts << "\tspecialization " << t << " at "
+ << DECL_SOURCE_FILE (d) << ":"
+ << DECL_SOURCE_LINE (d) << endl;
+ }
+
+ ts << "instantiations:" << endl;
+
+ for (tree i (DECL_TEMPLATE_INSTANTIATIONS (decl));
+ i != NULL_TREE; i = TREE_CHAIN (i))
+ {
+ tree t (TREE_VALUE (i));
+ tree d (TYPE_NAME (t));
+
+ ts << "\tinstantiation " << t << " at "
+ << DECL_SOURCE_FILE (d) << ":"
+ << DECL_SOURCE_LINE (d) << endl;
+ }
+ }
+
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " template " << name << " at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ type_template* t_node (0);
+
+ if (tc == RECORD_TYPE)
+ t_node = &emit_class_template (decl);
+ else
+ t_node = &emit_union_template (decl);
+
+ if (COMPLETE_TYPE_P (t))
+ unit_->new_edge<defines> (*scope_, *t_node, name);
+ else
+ unit_->new_edge<declares> (*scope_, *t_node, name);
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " template " << name
+ << " (" << t_node << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+}
+
+class_template& parser::impl::
+emit_class_template (tree t, bool stub)
+{
+ // See if there is a stub already for this template.
+ //
+ class_template* ct_node (0);
+ tree c (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
+
+ if (node* n = unit_->find (t))
+ {
+ ct_node = &dynamic_cast<class_template&> (*n);
+ }
+ else
+ {
+ path f (DECL_SOURCE_FILE (t));
+ size_t ln (DECL_SOURCE_LINE (t));
+ size_t cl (DECL_SOURCE_COLUMN (t));
+
+ ct_node = &unit_->new_node<class_template> (f, ln, cl, c);
+ unit_->insert (t, *ct_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (c))
+ return *ct_node;
+
+ class_scopes_.push_back (ct_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order. For now we are only interested in
+ // nested class template declarations.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (c)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = ct_node;
+
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *ct_node;
+}
+
+union_template& parser::impl::
+emit_union_template (tree t, bool stub)
+{
+ // See if there is a stub already for this template.
+ //
+ union_template* ut_node (0);
+ tree u (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
+
+ if (node* n = unit_->find (t))
+ {
+ ut_node = &dynamic_cast<union_template&> (*n);
+ }
+ else
+ {
+ path f (DECL_SOURCE_FILE (t));
+ size_t l (DECL_SOURCE_LINE (t));
+ size_t c (DECL_SOURCE_COLUMN (t));
+
+ ut_node = &unit_->new_node<union_template> (f, l, c, u);
+ unit_->insert (t, *ut_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (u))
+ return *ut_node;
+
+ class_scopes_.push_back (ut_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order. For now we are only interested in
+ // nested class template declarations.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = ut_node;
+
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *ut_node;
+}
+
+enum_& parser::impl::
+emit_enum (tree e,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn,
+ bool stub)
+{
+ e = TYPE_MAIN_VARIANT (e);
+
+ // See if there is a stub already for this type.
+ //
+ enum_* e_node (0);
+
+ if (node* n = unit_->find (e))
+ e_node = &dynamic_cast<enum_&> (*n);
+ else
+ {
+ e_node = &unit_->new_node<enum_> (file, line, clmn, e);
+
+ // Set the underlying type even for incomplete (forward-declared) enums.
+ //
+ tree ut (ENUM_UNDERLYING_TYPE (e));
+ names* hint (unit_->find_hint (ut));
+ integral_type* un = dynamic_cast<integral_type*> (
+ unit_->find (TYPE_MAIN_VARIANT (ut)));
+
+ // For "old" enums GCC creates a distinct type node and the only way to
+ // get to one of the known integrals is via its name.
+ //
+ if (un == 0)
+ {
+ ut = TREE_TYPE (TYPE_NAME (ut));
+ un = dynamic_cast<integral_type*> (unit_->find (TYPE_MAIN_VARIANT (ut)));
+ }
+
+ underlies& edge (unit_->new_edge<underlies> (*un, *e_node));
+
+ if (hint != 0)
+ edge.hint (*hint);
+
+ unit_->insert (e, *e_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (e))
+ return *e_node;
+
+ // Traverse enumerators.
+ //
+ for (tree er (TYPE_VALUES (e)); er != NULL_TREE ; er = TREE_CHAIN (er))
+ {
+ char const* name (IDENTIFIER_POINTER (TREE_PURPOSE (er)));
+ tree decl (TREE_VALUE (er));
+ tree tval (DECL_INITIAL (decl));
+
+ unsigned long long val (integer_value (tval));
+
+ // There doesn't seem to be a way to get the proper position for
+ // each enumerator.
+ //
+ enumerator& er_node = unit_->new_node<enumerator> (
+ file, line, clmn, er, val);
+ unit_->new_edge<enumerates> (*e_node, er_node);
+ unit_->insert (decl, er_node);
+
+ // In C++11 the enumerators are always available in the enum's
+ // scope, even for old enums.
+ //
+ if (ops_.std () >= cxx_version::cxx11)
+ unit_->new_edge<names> (*e_node, er_node, name, access::public_);
+
+ // Inject enumerators into the outer scope unless this is an
+ // enum class.
+ //
+ if (UNSCOPED_ENUM_P (e))
+ unit_->new_edge<names> (*scope_, er_node, name, access);
+
+ if (trace)
+ ts << "\tenumerator " << name << " at " << file << ":" << line << endl;
+ }
+
+ return *e_node;
+}
+
+type& parser::impl::
+emit_type (tree t,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn)
+{
+ tree mv (TYPE_MAIN_VARIANT (t));
+
+ if (trace)
+ {
+ ts << gcc_tree_code_name(TREE_CODE (t)) << " " << t
+ << " main " << mv << endl;
+
+ for (tree v (TYPE_MAIN_VARIANT (t)); v != 0; v = TYPE_NEXT_VARIANT (v))
+ ts << "\tvariant " << v << " " << CP_TYPE_CONST_P (v) << endl;
+ }
+
+ node* n (unit_->find (mv));
+
+ type& r (n != 0
+ ? dynamic_cast<type&> (*n)
+ : create_type (t, access, file, line, clmn));
+
+ if (trace && n != 0)
+ ts << "found node " << &r << " for type " << mv << endl;
+
+ if (cp_type_quals (t) == TYPE_UNQUALIFIED)
+ {
+ unit_->insert (t, r); // Add this variant to the map.
+ return r;
+ }
+
+ // See if this type already has this variant.
+ //
+ bool qc (CP_TYPE_CONST_P (t));
+ bool qv (CP_TYPE_VOLATILE_P (t));
+ bool qr (CP_TYPE_RESTRICT_P (t));
+
+ for (type::qualified_iterator i (r.qualified_begin ());
+ i != r.qualified_end (); ++i)
+ {
+ qualifier& q (i->qualifier ());
+
+ if (q.const_ () == qc && q.volatile_ () == qv && q.restrict_ () == qr)
+ {
+ if (trace)
+ ts << "found qualifier variant " << &q << endl;
+
+ unit_->insert (t, q); // Add this variant to the map.
+ return q;
+ }
+ }
+
+ // No such variant yet. Create a new one. Qualified types are not
+ // unique in the tree so don't add this node to the map.
+ //
+ qualifier& q (unit_->new_node<qualifier> (file, line, clmn, t, qc, qv, qr));
+ qualifies& e (unit_->new_edge<qualifies> (q, r));
+ unit_->insert (t, q);
+
+ // See if there is a name hint for this type.
+ //
+ // If TREE_TYPE (TYPE_NAME (t)) != t then we have an inline qualifier,
+ // as in:
+ //
+ // const foo x;
+ //
+ // If they are equal, then there are two possible cases. The first is
+ // when we have a qualifier typedef, as in:
+ //
+ // typedef const foo cfoo;
+ // cfoo x;
+ //
+ // The second is when we have a qualifier typedef but what is actually
+ // used in the declaration is an inline qualifier, as in:
+ //
+ // typedef const foo cfoo;
+ // const foo x;
+ //
+ // Unfortunately, in GCC, these two cases are indistinguishable. In
+ // certain cases this can lead to a wrong hint being used for the base
+ // type, for example:
+ //
+ // typedef foo my_foo;
+ // typedef foo foo_t;
+ // typedef const foo_t cfoo;
+ //
+ // const my_foo x;
+ //
+ // Above, the hint will be foo_t while it should be my_foo.
+ //
+ tree bt (0);
+
+ if (tree decl = TYPE_NAME (t))
+ {
+ bt = TREE_TYPE (decl);
+
+ if (t == bt)
+ {
+ // A const type can be named only with a typedef. Get the
+ // original type.
+ //
+ tree ot (DECL_ORIGINAL_TYPE (decl));
+
+ // And chase it one more time to get rid of the qualification.
+ //
+ decl = TYPE_NAME (ot);
+ bt = decl != 0 ? TREE_TYPE (decl) : 0;
+ }
+ }
+
+ if (bt != 0)
+ {
+ if (names* hint = unit_->find_hint (bt))
+ e.hint (*hint);
+ }
+
+ process_named_pragmas (t, q);
+
+ return q;
+}
+
+type& parser::impl::
+create_type (tree t,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn)
+{
+ type* r (0);
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ switch (tc)
+ {
+ //
+ // User-defined types.
+ //
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ tree ti (TYPE_TEMPLATE_INFO (t));
+
+ if (ti == NULL_TREE)
+ {
+ // Ordinary class. There are two situations which can lead
+ // here. First is when we have an anonymous class that is
+ // part of the declaration, for example:
+ //
+ // typedef const struct {...} s;
+ //
+ // The second situation is a named class definition which
+ // we haven't parsed yet. In this case we are going to
+ // create a "stub" class node which will be processed and
+ // filled in later.
+ //
+
+ // Pointers to member functions are represented as record
+ // types. Detect and handle this case.
+ //
+ if (TYPE_PTRMEMFUNC_P (t))
+ {
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_member_function_type");
+ unit_->insert (t, *r);
+ }
+ else
+ {
+ tree d (TYPE_NAME (t));
+
+ if (trace)
+ ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
+ << file << ":" << line << endl;
+
+ if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ if (tc == RECORD_TYPE)
+ r = &emit_class<class_> (t, file, line, clmn);
+ else
+ r = &emit_union<union_> (t, file, line, clmn);
+ }
+ else
+ {
+ // Use the "defining" declaration's file, line, and column
+ // information to create the stub.
+ //
+ path f (DECL_SOURCE_FILE (d));
+ size_t l (DECL_SOURCE_LINE (d));
+ size_t c (DECL_SOURCE_COLUMN (d));
+
+ if (tc == RECORD_TYPE)
+ r = &emit_class<class_> (t, f, l, c, true);
+ else
+ r = &emit_union<union_> (t, f, l, c, true);
+ }
+
+ if (trace)
+ ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+ }
+ }
+ else
+ {
+ // Template instantiation.
+ //
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ type_template* t_node (0);
+
+ // Find the template node or create a stub if none exist.
+ //
+ if (node* n = unit_->find (decl))
+ t_node = &dynamic_cast<type_template&> (*n);
+ else
+ {
+ if (trace)
+ ts << "start stub " << gcc_tree_code_name(tc) << " template for ("
+ << decl << ") at " << file << ":" << line << endl;
+
+ if (tc == RECORD_TYPE)
+ t_node = &emit_class_template (decl, true);
+ else
+ t_node = &emit_union_template (decl, true);
+
+ if (trace)
+ ts << "end stub " << gcc_tree_code_name(tc) << " template ("
+ << t_node << ") at " << file << ":" << line << endl;
+ }
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " instantiation ("
+ << t << ") for template (" << t_node << ")"
+ << " at " << file << ":" << line << endl;
+
+ type_instantiation* i_node (0);
+
+ if (tc == RECORD_TYPE)
+ i_node = &emit_class<class_instantiation> (t, file, line, clmn);
+ else
+ i_node = &emit_union<union_instantiation> (t, file, line, clmn);
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " instantiation ("
+ << static_cast<type*> (i_node) << ")"
+ << " at " << file << ":" << line << endl;
+
+ unit_->new_edge<instantiates> (*i_node, *t_node);
+ process_named_pragmas (t, *i_node);
+ r = i_node;
+ }
+
+ break;
+ }
+ case ENUMERAL_TYPE:
+ {
+ // The same logic as in the "ordinary class" case above
+ // applies here.
+ //
+
+ t = TYPE_MAIN_VARIANT (t);
+ tree d (TYPE_NAME (t));
+
+ if (trace)
+ ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
+ << file << ":" << line << endl;
+
+ if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ r = &emit_enum (t, access, file, line, clmn);
+ }
+ else
+ {
+ // Use the "defining" declaration's file, line, and column
+ // information to create the stub.
+ //
+ path f (DECL_SOURCE_FILE (d));
+ size_t l (DECL_SOURCE_LINE (d));
+ size_t c (DECL_SOURCE_COLUMN (d));
+
+ r = &emit_enum (t, access, f, l, c, true);
+ }
+
+ if (trace)
+ ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+
+ break;
+ }
+
+ //
+ // Derived types.
+ //
+
+ case ARRAY_TYPE:
+ {
+ unsigned long long size (0);
+
+ if (tree index = TYPE_DOMAIN (t))
+ {
+ tree max (TYPE_MAX_VALUE (index));
+
+ if (TREE_CODE (max) == INTEGER_CST)
+ {
+ size = integer_value (max);
+
+ // The docs say that TYPE_DOMAIN will be NULL if the
+ // array doesn't specify bounds. In reality, it is
+ // set to ~0.
+ //
+ if (size == ~(unsigned long long) (0))
+ size = 0;
+
+ size++; // Convert max index to size.
+ }
+ else
+ {
+ error (file, line, clmn)
+ << "non-integer array index " <<
+ gcc_tree_code_name(TREE_CODE (max)) << endl;
+
+ throw failed ();
+ }
+ }
+
+ // In GCC tree a const array has both the array type itself and the
+ // element type marked as const. This doesn't bode well with our
+ // semantic graph model where we have a separate type node for
+ // qualifiers. To fix this, we are going to strip the const
+ // qualification from the element type and only preserve it in
+ // the array type. In other words, we view it as "constant array"
+ // rather than "array of constant elements".
+ //
+ using semantics::array; // vs std::array.
+
+ tree bt (TREE_TYPE (t));
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+ type& bt_node (emit_type (bt_mv, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ array& a (unit_->new_node<array> (file, line, clmn, t, size));
+ unit_->insert (t, a);
+ contains& edge (unit_->new_edge<contains> (a, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (
+ cp_type_quals (bt) == TYPE_UNQUALIFIED ? bt : bt_mv))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, a);
+ r = &a;
+ break;
+ }
+ case REFERENCE_TYPE:
+ {
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ reference& ref (unit_->new_node<reference> (file, line, clmn, t));
+ unit_->insert (t, ref);
+ references& edge (unit_->new_edge<references> (ref, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, ref);
+ r = &ref;
+ break;
+ }
+ case POINTER_TYPE:
+ {
+ if (!TYPE_PTRMEM_P (t))
+ {
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ pointer& p (unit_->new_node<pointer> (file, line, clmn, t));
+ unit_->insert (t, p);
+ points& edge (unit_->new_edge<points> (p, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, p);
+ r = &p;
+ }
+ else
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_data_member_type");
+ unit_->insert (t, *r);
+
+ if (trace)
+ ts << "unsupported pointer_to_data_member_type (" << r << ")"
+ << " at " << file << ":" << line << endl;
+ }
+ break;
+ }
+ default:
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, gcc_tree_code_name(tc));
+ unit_->insert (t, *r);
+
+ if (trace)
+ ts << "unsupported " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+
+ break;
+ }
+ }
+
+ return *r;
+}
+
+string parser::impl::
+emit_type_name (tree type, bool direct)
+{
+ // First see if there is a "direct" name for this type.
+ //
+ if (direct)
+ {
+ if (tree decl = TYPE_NAME (type))
+ {
+ tree t (TREE_TYPE (decl));
+
+ if (t != 0 && same_type_p (type, t))
+ return IDENTIFIER_POINTER (DECL_NAME (decl));
+ }
+ }
+
+ string r;
+
+ if (CP_TYPE_CONST_P (type))
+ r += " const";
+
+ if (CP_TYPE_VOLATILE_P (type))
+ r += " volatile";
+
+ if (CP_TYPE_RESTRICT_P (type))
+ r += " __restrict";
+
+ gcc_tree_code_type tc (TREE_CODE (type));
+
+ switch (tc)
+ {
+ //
+ // User-defined types.
+ //
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ {
+ tree ti (TYPE_TEMPLATE_INFO (type));
+
+ if (ti == NULL_TREE)
+ {
+ // Ordinary class.
+ //
+ type = TYPE_MAIN_VARIANT (type);
+
+ // Pointers to member functions are represented as record
+ // types and don't have names, not even the synthesized ones.
+ //
+ if (TYPE_PTRMEMFUNC_P (type))
+ r = "<pointer-to-member-function>" + r;
+ else
+ {
+ tree name (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (name)) + r;
+ }
+ }
+ else
+ {
+ // Template instantiation.
+ //
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+ string id (IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ id += '<';
+
+ tree args (INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)));
+
+ for (size_t i (0), n (TREE_VEC_LENGTH (args)); i < n ; ++i)
+ {
+ tree a (TREE_VEC_ELT (args, i));
+
+ if (i != 0)
+ id += ", ";
+
+ // Assume integer and type-only arguments.
+ //
+ if (TREE_CODE (a) == INTEGER_CST)
+ id += to_string (integer_value (a));
+ else
+ id += emit_type_name (a);
+ }
+
+ id += '>';
+
+ r = id + r;
+ }
+
+ break;
+ }
+
+ case ENUMERAL_TYPE:
+ {
+ type = TYPE_MAIN_VARIANT (type);
+ tree decl (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
+ break;
+ }
+
+ //
+ // Derived types.
+ //
+
+ case ARRAY_TYPE:
+ {
+ unsigned long long size (0);
+
+ if (tree index = TYPE_DOMAIN (type))
+ {
+ tree max (TYPE_MAX_VALUE (index));
+
+ if (TREE_CODE (max) == INTEGER_CST)
+ {
+ size = integer_value (max);
+
+ // Same as above.
+ //
+ if (size == ~(unsigned long long) (0))
+ size = 0;
+
+ size++;
+ }
+ else
+ {
+ // Non-integer array index which we do not support. The
+ // error has been/will be issued in emit_type.
+ //
+ }
+ }
+
+ tree t (TREE_TYPE (type));
+
+ if (size != 0)
+ {
+ ostringstream ostr;
+ ostr << size;
+ r = emit_type_name (t) + "[" + ostr.str () + "]" + r;
+ }
+ else
+ r = emit_type_name (t) + "[]" + r;
+
+ break;
+ }
+ case REFERENCE_TYPE:
+ {
+ tree t (TREE_TYPE (type));
+ r = emit_type_name (t) + "&" + r;
+ break;
+ }
+ case POINTER_TYPE:
+ {
+ if (!TYPE_PTRMEM_P (type))
+ {
+ tree t (TREE_TYPE (type));
+ r = emit_type_name (t) + "*" + r;
+ }
+ else
+ r = "<pointer_to_member_type>";
+
+ break;
+ }
+
+ //
+ // Fundamental types.
+ //
+
+ case VOID_TYPE:
+ case REAL_TYPE:
+ case BOOLEAN_TYPE:
+ case INTEGER_TYPE:
+ {
+ type = TYPE_MAIN_VARIANT (type);
+ tree decl (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
+ break;
+ }
+ default:
+ {
+ r = "<" + string (gcc_tree_code_name(tc)) + ">";
+ break;
+ }
+ }
+
+ return r;
+}
+
+void parser::impl::
+process_pragmas (declaration const& decl,
+ node& node,
+ string const& name,
+ decl_set::const_iterator begin,
+ decl_set::const_iterator cur,
+ decl_set::const_iterator /*end*/)
+{
+ // First process the position pragmas by iterating backwards until
+ // we get to the preceding non-pragma declaration that has been
+ // associated.
+ //
+ pragma_set prags;
+
+ if (cur != begin)
+ {
+ decl_set::const_iterator i (cur);
+ for (--i; i != begin && (i->prag != 0 || !i->assoc); --i) ;
+
+ for (; i != cur; ++i)
+ {
+ if (i->prag == 0) // Skip declarations.
+ continue;
+
+ assert (!i->assoc);
+
+ if (i->prag->check (decl, name, i->prag->pragma_name, i->prag->loc))
+ prags.insert (*i->prag);
+ else
+ error_++; // Diagnostic has already been issued.
+
+ i->assoc = true; // Mark this pragma as associated.
+ }
+
+ cur->assoc = true; // Mark the declaration as associated.
+ }
+
+ // Now see if there are any named pragmas for this declaration. By
+ // doing this after handling the position pragmas we ensure correct
+ // overriding.
+ //
+ {
+ decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
+
+ if (i != decl_pragmas_.end ())
+ prags.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Finally, copy the resulting pragma set to context.
+ //
+ for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
+ add_pragma (node, i->second);
+}
+
+void parser::impl::
+process_named_pragmas (declaration const& decl, node& node)
+{
+ pragma_set prags;
+
+ decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
+
+ if (i != decl_pragmas_.end ())
+ prags.insert (i->second.begin (), i->second.end ());
+
+ // Copy the resulting pragma set to context.
+ //
+ for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
+ add_pragma (node, i->second);
+}
+
+void parser::impl::
+add_pragma (node& n, pragma const& p)
+{
+ if (trace)
+ ts << "\t\t pragma " << p.pragma_name << endl;
+
+ string const& k (p.context_name);
+
+ if (p.add == 0)
+ {
+ n.set (k, p.value);
+ n.set (k + "-location", p.loc);
+ }
+ else
+ p.add (n, k, p.value, p.loc);
+}
+
+void parser::impl::
+diagnose_unassoc_pragmas (decl_set const& decls)
+{
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ if (i->prag && !i->assoc)
+ {
+ pragma const& p (*i->prag);
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "declaration" << endl;
+ error_++;
+ }
+ }
+}
+
+string parser::impl::
+fq_scope (tree decl)
+{
+ string s, tmp;
+
+ for (tree scope (CP_DECL_CONTEXT (decl));
+ scope != global_namespace;)
+ {
+ tree prev (CP_DECL_CONTEXT (scope));
+
+ // If this is an inline namespace, pretend it doesn't exist.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, scope, true))
+#else
+ if (!is_associated_namespace (prev, scope))
+#endif
+ {
+ tree n = DECL_NAME (scope);
+
+ tmp = "::";
+ tmp += (n != NULL_TREE ? IDENTIFIER_POINTER (n) : "");
+ tmp += s;
+ s.swap (tmp);
+ }
+
+ scope = prev;
+ }
+
+ return s;
+}
+
+//
+// parser
+//
+
+parser::
+~parser ()
+{
+ // Needs parser::impl definition.
+}
+
+parser::
+parser (options const& ops,
+ loc_pragmas& lp,
+ ns_loc_pragmas& nslp,
+ decl_pragmas& dp)
+ : impl_ (new impl (ops, lp, nslp, dp))
+{
+}
+
+unique_ptr<unit> parser::
+parse (tree global_scope, path const& main_file)
+{
+ return impl_->parse (global_scope, main_file);
+}
diff --git a/odb/odb/parser.hxx b/odb/odb/parser.hxx
new file mode 100644
index 0000000..b26c8f1
--- /dev/null
+++ b/odb/odb/parser.hxx
@@ -0,0 +1,37 @@
+// file : odb/parser.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PARSER_HXX
+#define ODB_PARSER_HXX
+
+#include <odb/gcc.hxx>
+
+#include <memory> // std::unique_ptr
+
+#include <odb/pragma.hxx>
+#include <odb/options.hxx>
+#include <odb/semantics/unit.hxx>
+
+class parser
+{
+public:
+ class failed {};
+
+ ~parser ();
+ parser (options const&, loc_pragmas&, ns_loc_pragmas&, decl_pragmas&);
+
+ std::unique_ptr<semantics::unit>
+ parse (tree global_scope, semantics::path const& main_file);
+
+private:
+ parser (parser const&);
+
+ parser&
+ operator= (parser const&);
+
+private:
+ class impl;
+ std::unique_ptr<impl> impl_;
+};
+
+#endif // ODB_PARSER_HXX
diff --git a/odb/odb/plugin.cxx b/odb/odb/plugin.cxx
new file mode 100644
index 0000000..c065a8a
--- /dev/null
+++ b/odb/odb/plugin.cxx
@@ -0,0 +1,458 @@
+// file : odb/plugin.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // Keep it first.
+
+#include <unistd.h> // stat()
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+#include <memory> // std::unique_ptr
+#include <string>
+#include <vector>
+#include <cstring> // std::strcpy, std::strstr
+#include <cassert>
+#include <iostream>
+
+#include <libcutl/re.hxx>
+#include <libcutl/fs/path.hxx>
+
+#include <odb/pragma.hxx>
+#include <odb/parser.hxx>
+#include <odb/options.hxx>
+#include <odb/option-functions.hxx>
+#include <odb/features.hxx>
+#include <odb/profile.hxx>
+#include <odb/version.hxx>
+#include <odb/validator.hxx>
+#include <odb/processor.hxx>
+#include <odb/generator.hxx>
+#include <odb/semantics/unit.hxx>
+
+using namespace std;
+using namespace semantics;
+
+using cutl::fs::path;
+using cutl::fs::invalid_path;
+
+typedef vector<path> paths;
+
+#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
+__declspec(dllexport)
+#endif
+int plugin_is_GPL_compatible;
+
+unique_ptr<options const> options_;
+paths profile_paths_;
+path file_; // File being compiled.
+paths inputs_; // List of input files in at-once mode or just file_.
+
+bool (*cpp_diagnostic_prev) (
+ cpp_reader*,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level,
+ cpp_warning_reason,
+#else
+ int,
+ int,
+#endif
+#if BUILDING_GCC_MAJOR >= 6
+ rich_location*,
+#else
+ location_t,
+ unsigned int,
+#endif
+ const char*,
+ va_list*);
+
+static bool
+cpp_diagnostic_filter (cpp_reader* r,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level level,
+ cpp_warning_reason reason,
+#else
+ int level,
+ int reason,
+#endif
+#if BUILDING_GCC_MAJOR >= 6
+ rich_location* l,
+#else
+ location_t l,
+ unsigned int column_override,
+#endif
+ const char* msg,
+ va_list* ap)
+{
+ // #pragma once in the main file. Note that the message that we get is
+ // potentially translated so we search for the substring (there is
+ // currently only one warning in libcpp that mentions #pragma once).
+ // Unfortunately, some translations translate the 'once' word (e.g,
+ // #pragma uno; I wonder if one can actually specify it like that in
+ // the source code). Oh, well, there is only so much we can do.
+ //
+ if (strstr (msg, "#pragma once") != 0)
+ return true;
+
+ return cpp_diagnostic_prev (
+ r,
+ level,
+ reason,
+#if BUILDING_GCC_MAJOR >= 6
+ l,
+#else
+ l,
+ column_override,
+#endif
+ msg,
+ ap);
+}
+
+// A prefix of the _cpp_file struct. This struct is not part of the
+// public interface so we have to resort to this technique (based on
+// libcpp/files.c).
+//
+struct cpp_file_prefix
+{
+ char const* name;
+ char const* path;
+ char const* pchname;
+ char const* dir_name;
+ _cpp_file* next_file;
+ const uchar* buffer;
+ const uchar* buffer_start;
+ const cpp_hashnode *cmacro;
+ cpp_dir *dir;
+ struct stat st;
+};
+
+extern "C" void
+start_unit_callback (void*, void*)
+{
+ // Set the preprocessor error callback to filter out useless diagnostics.
+ //
+ cpp_callbacks* cb (cpp_get_callbacks (parse_in));
+
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_prev = cb->diagnostic;
+ cb->diagnostic = &cpp_diagnostic_filter;
+#else
+ cpp_diagnostic_prev = cb->error;
+ cb->error = &cpp_diagnostic_filter;
+#endif
+
+ if (cpp_diagnostic_prev == 0)
+ {
+ cerr << "ice: expected cpp diagnostic callback to be set" << endl;
+ exit (1);
+ }
+
+ // Set the directory of the main file (stdin) to that of the orginal
+ // file so that relative inclusion works. Also adjust the path and
+ // re-stat the file so that #pragma once works.
+ //
+ cpp_buffer* b (cpp_get_buffer (parse_in));
+ _cpp_file* f (cpp_get_file (b));
+ cpp_dir* d (cpp_get_dir (f));
+ char const* p (cpp_get_path (f));
+
+ cpp_file_prefix* fp (reinterpret_cast<cpp_file_prefix*> (f));
+
+ // Perform some sanity checks.
+ //
+ if (p != 0 && *p == '\0' // The path should be empty (stdin).
+ && cpp_get_prev (b) == 0 // This is the only buffer (main file).
+ && fp->path == p // Our prefix corresponds to the actual type.
+ && fp->dir == d // Our prefix corresponds to the actual type.
+ && fp->dir_name == 0) // The directory part hasn't been initialized.
+ {
+ // The dir_name is initialized by libcpp lazily so we can preemptively
+ // set it to what we need.
+ //
+ path d (file_.directory ());
+ char* s;
+
+ if (d.empty ())
+ {
+ s = XNEWVEC (char, 1);
+ *s = '\0';
+ }
+ else
+ {
+ size_t n (d.string ().size ());
+ s = XNEWVEC (char, n + 2);
+ strcpy (s, d.string ().c_str ());
+ s[n] = path::traits::directory_separator;
+ s[n + 1] = '\0';
+ }
+
+ fp->dir_name = s;
+
+ // Unless we are in the at-once mode (where input files are actually
+ // #include'ed into the synthesized stdin), pretend that we are the
+ // actual input file. This is necessary for the #pragma once to work.
+ //
+ // All this relies on the way things are implemented in libcpp. In
+ // particular, the #pragma once code first checks if the mtime and
+ // size of files match (that's why we need stat() below). If they
+ // do, then it goes ahead and compares their contents. To re-load
+ // the contents of the file libcpp uses the path (that's why we
+ // need to adjust that as well).
+ //
+ if (inputs_.size () == 1)
+ {
+ string const& f (file_.string ());
+
+ XDELETEVEC (fp->path);
+ size_t n (f.size ());
+ char* p (XNEWVEC (char, n + 1));
+ strcpy (p, f.c_str ());
+ p[n] = '\0';
+ fp->path = p;
+
+ // This call shouldn't fail since we've just opened it in the driver.
+ stat (fp->path, &fp->st);
+ }
+ }
+ else
+ {
+ cerr << "ice: unable to initialize main file directory" << endl;
+ exit (1);
+ }
+}
+
+extern "C" void
+gate_callback (void*, void*)
+{
+ // If there were errors during compilation, let GCC handle the
+ // exit.
+ //
+ if (errorcount || sorrycount)
+ return;
+
+ int r (0);
+
+ try
+ {
+ // Post process pragmas.
+ //
+ post_process_pragmas ();
+
+ // Parse the GCC tree to semantic graph.
+ //
+ parser p (*options_, loc_pragmas_, ns_loc_pragmas_, decl_pragmas_);
+ unique_ptr<unit> u (p.parse (global_namespace, file_));
+
+ features f;
+
+ // Process, pass 1.
+ //
+ process (*options_, f, *u, file_, 1);
+
+ // Validate, pass 1.
+ //
+ validate (*options_, f, *u, file_, 1);
+
+ // Process, pass 2.
+ //
+ process (*options_, f, *u, file_, 2);
+
+ // Validate, pass 2.
+ //
+ validate (*options_, f, *u, file_, 2);
+
+ // Generate.
+ //
+ generate (*options_, f, *u, file_, inputs_);
+ }
+ catch (cutl::re::format const& e)
+ {
+ cerr << "error: invalid regex: '" << e.regex () << "': " <<
+ e.description () << endl;
+ r = 1;
+ }
+ catch (pragmas_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (parser::failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (validator_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (processor_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (generator_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+
+ exit (r);
+}
+
+static char const* const odb_version = ODB_COMPILER_VERSION_STR;
+
+typedef vector<string> strings;
+
+extern "C"
+#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
+__declspec(dllexport)
+#endif
+int
+plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*)
+{
+ int r (0);
+ plugin_info->version = odb_version;
+
+ try
+ {
+ // Parse options.
+ //
+ {
+ strings argv_str;
+ argv_str.push_back (plugin_info->base_name);
+
+ for (int i (0); i < plugin_info->argc; ++i)
+ {
+ plugin_argument& a (plugin_info->argv[i]);
+
+ // A value cannot contain '=' so it is passed as the backspace
+ // character.
+ //
+ string v (a.value != 0 ? a.value : "");
+ for (size_t i (0); i < v.size (); ++i)
+ if (v[i] == '\b')
+ v[i] = '=';
+
+ // Handle service options.
+ //
+ if (strcmp (a.key, "svc-path") == 0)
+ {
+ profile_paths_.push_back (path (v));
+ continue;
+ }
+
+ if (strcmp (a.key, "svc-file") == 0)
+ {
+ // First is the main file. Subsequent are inputs in the at-once
+ // mode.
+ //
+ if (file_.empty ())
+ file_ = path (v);
+ else
+ inputs_.push_back (path (v));
+
+ continue;
+ }
+
+ string opt (strlen (a.key) > 1 ? "--" : "-");
+ opt += a.key;
+
+ argv_str.push_back (opt);
+
+ if (a.value != 0)
+ argv_str.push_back (v);
+ }
+
+ vector<char*> argv;
+ for (strings::iterator i (argv_str.begin ()); i != argv_str.end (); ++i)
+ argv.push_back (const_cast<char*> (i->c_str ()));
+
+ int argc (static_cast<int> (argv.size ()));
+
+ if (inputs_.empty ())
+ inputs_.push_back (file_);
+
+ // Two-phase options parsing, similar to the driver.
+ //
+ cli::argv_file_scanner::option_info oi[3];
+ oi[0].option = "--options-file";
+ oi[0].search_func = 0;
+ oi[1].option = "-p";
+ oi[2].option = "--profile";
+
+ database db;
+ {
+ oi[1].search_func = &profile_search_ignore;
+ oi[2].search_func = &profile_search_ignore;
+
+ cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
+ options ops (scan);
+ assert (ops.database_specified ());
+ db = ops.database ()[0];
+ }
+
+ profile_data pd (profile_paths_, db, "odb plugin");
+ oi[1].search_func = &profile_search;
+ oi[2].search_func = &profile_search;
+ oi[1].arg = &pd;
+ oi[2].arg = &pd;
+
+ cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
+ unique_ptr<options> ops (
+ new options (scan, cli::unknown_mode::fail, cli::unknown_mode::fail));
+
+ // Process options.
+ //
+ process_options (*ops);
+
+ options_ = move (ops);
+ pragma_db_ = db;
+ pragma_multi_ = options_->multi_database ();
+ }
+
+ if (options_->trace ())
+ cerr << "starting plugin " << plugin_info->base_name << endl;
+
+ // Disable assembly output. GCC doesn't define HOST_BIT_BUCKET
+ // correctly for MinGW (it still used /dev/null which fails to
+ // open).
+ //
+#ifdef _WIN32
+ asm_file_name = "nul";
+#else
+ asm_file_name = HOST_BIT_BUCKET;
+#endif
+
+ // Register callbacks.
+ //
+ register_callback (plugin_info->base_name,
+ PLUGIN_PRAGMAS,
+ register_odb_pragmas,
+ 0);
+
+ register_callback (plugin_info->base_name,
+ PLUGIN_START_UNIT,
+ start_unit_callback,
+ 0);
+
+ register_callback (plugin_info->base_name,
+ PLUGIN_OVERRIDE_GATE,
+ &gate_callback,
+ 0);
+ }
+ catch (cli::exception const& ex)
+ {
+ cerr << ex << endl;
+ r = 1;
+ }
+
+ if (r != 0)
+ exit (r);
+
+ return r;
+}
diff --git a/odb/odb/pragma.cxx b/odb/odb/pragma.cxx
new file mode 100644
index 0000000..6668733
--- /dev/null
+++ b/odb/odb/pragma.cxx
@@ -0,0 +1,4442 @@
+// file : odb/pragma.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <cctype> // std::isalnum
+#include <limits>
+#include <vector>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/lookup.hxx>
+#include <odb/pragma.hxx>
+#include <odb/cxx-token.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/context.hxx>
+#include <odb/relational/context.hxx>
+
+using namespace std;
+using namespace cutl;
+
+using container::any;
+
+virt_declarations virt_declarations_;
+loc_pragmas loc_pragmas_;
+decl_pragmas decl_pragmas_;
+ns_loc_pragmas ns_loc_pragmas_;
+
+database pragma_db_;
+multi_database pragma_multi_;
+
+template <typename X>
+void
+accumulate (compiler::context& ctx, string const& k, any const& v, location_t)
+{
+ // Empty values are used to indicate that this pragma must be ignored.
+ //
+ if (v.empty ())
+ return;
+
+ typedef vector<X> container;
+
+ container& c (ctx.count (k)
+ ? ctx.get<container> (k)
+ : ctx.set (k, container ()));
+
+ c.push_back (v.value<X> ());
+}
+
+// Parse a qualified string. It can be in one of the following formats:
+//
+// "foo.bar.baz" (can have empty leading component, e.g., ".bar.baz")
+// "foo"."bar"."baz" (can have empty leading component, e.g., ""."bar"."baz")
+// ."foo.bar" (used to specify unqualifed names with periods)
+//
+// Empty leading components means fully qualified (similar to ::foo in C++).
+//
+static bool
+parse_qname (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ string const& p, // Pragma name for diagnostic.
+ qname& name,
+ bool* expr = 0, // If specified, detect an expression
+ string* expr_str = 0) // and store it here.
+{
+ assert (tt == CPP_STRING || tt == CPP_DOT);
+
+ // Handle the special case of unqualified name which can contain periods.
+ //
+ if (tt == CPP_DOT)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return false;
+ }
+
+ name = tl;
+ tt = l.next (tl, &tn);
+ return true;
+ }
+
+ name.clear ();
+ string str (tl);
+
+ // See what comes after the string.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_DOT)
+ {
+ name.append (str);
+
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return false;
+ }
+
+ name.append (tl);
+ }
+
+ return true;
+ }
+
+ if (expr != 0 && tt == CPP_PLUS)
+ {
+ *expr = true;
+ *expr_str = str;
+ return true;
+ }
+
+ // Scan the string looking for '.' as well as for non-identifier
+ // characters if we need to detect expressions.
+ //
+ string::size_type prev (string::npos);
+
+ for (size_t i (0); i < str.size (); ++i)
+ {
+ char c (str[i]);
+
+ if (c == '.')
+ {
+ if (prev == string::npos)
+ name.append (string (str, 0, i));
+ else
+ name.append (string (str, prev + 1, i - prev - 1));
+
+ prev = i;
+ }
+ else if (expr != 0 && !(isalnum (c) || c == '_'))
+ {
+ *expr = true;
+ *expr_str = str;
+ return true;
+ }
+ }
+
+ if (prev == string::npos)
+ name.append (str);
+ else
+ name.append (string (str, prev + 1, string::npos));
+
+ return true;
+}
+
+static bool
+parse_expression (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cxx_tokens& ts,
+ string const& prag)
+{
+ // Keep reading tokens until we see a mis-matching ')' or ',' while
+ // keeping track of the '()' and '{}' balance.
+ //
+ size_t p_balance (0), b_balance (0);
+
+ for (; tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ bool done (false);
+ cxx_token ct (l.location (), tt);
+
+ switch (tt)
+ {
+ case CPP_OPEN_BRACE:
+ {
+ b_balance++;
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ b_balance--;
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ p_balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (p_balance == 0 && b_balance == 0)
+ done = true;
+ else
+ p_balance--;
+ break;
+ }
+ case CPP_COMMA:
+ {
+ if (p_balance == 0 && b_balance == 0)
+ done = true;
+ else
+ break;
+ }
+ case CPP_STRING:
+ {
+ ct.literal = tl;
+ break;
+ }
+ case CPP_NAME:
+ //case CPP_KEYWORD: see default:
+ {
+ ct.literal = tl;
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ switch (TREE_CODE (tn))
+ {
+ case INTEGER_CST:
+ {
+ tree type (TREE_TYPE (tn));
+ unsigned long long v (integer_value (tn));
+
+ ostringstream os;
+ os << v;
+
+ if (type == long_long_integer_type_node)
+ os << "LL";
+ else if (type == long_long_unsigned_type_node)
+ os << "ULL";
+ else if (type == long_integer_type_node)
+ os << "L";
+ else if (type == long_unsigned_type_node)
+ os << "UL";
+ else if (
+ TYPE_UNSIGNED (type) &&
+ TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node))
+ os << "U";
+
+ ct.literal = os.str ();
+ break;
+ }
+ case REAL_CST:
+ {
+ tree type (TREE_TYPE (tn));
+ REAL_VALUE_TYPE val (TREE_REAL_CST (tn));
+
+ // This is the best we can do. val cannot be INF or NaN.
+ //
+ char tmp[256];
+ real_to_decimal (tmp, &val, sizeof (tmp), 0, true);
+ istringstream is (tmp);
+ ostringstream os;
+
+ if (type == float_type_node)
+ {
+ float f;
+ is >> f;
+ os << f << 'F';
+ }
+ else
+ {
+ double d;
+ is >> d;
+ os << d;
+ }
+
+ ct.literal = os.str ();
+ break;
+ }
+ default:
+ {
+ error (l) << "unexpected numeric constant in db pragma "
+ << prag << endl;
+ return false;
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ // CPP_KEYWORD is not in the cpp_ttype enumeration.
+ //
+ if (tt == CPP_KEYWORD)
+ ct.literal = tl;
+
+ break;
+ }
+ }
+
+ if (done)
+ break;
+
+ // We don't store the tree node in ct since we converted numbers to
+ // string literals.
+ //
+ ts.push_back (ct);
+ }
+
+ return true;
+}
+
+static string
+parse_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ string const& prag)
+{
+ try
+ {
+ return lookup::parse_scoped_name (l, tt, tl, tn);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (l) << "invalid name in db pragma " << prag << endl;
+ return "";
+ }
+}
+
+static tree
+resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ tree start_scope,
+ string& name,
+ bool is_type,
+ string const& prag,
+ bool trailing_scope = false,
+ cpp_ttype* prev_tt = 0)
+{
+ try
+ {
+ cpp_ttype ptt; // Not used.
+ tree r (
+ lookup::resolve_scoped_name (
+ l, tt, tl, tn, ptt, start_scope, name, is_type, trailing_scope));
+
+ if (prev_tt != 0)
+ *prev_tt = ptt;
+
+ return r;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (l) << "invalid name in db pragma " << prag << endl;
+ return 0;
+ }
+ catch (lookup::unable_to_resolve const& e)
+ {
+ if (e.last ())
+ error (l) << "unable to resolve " << (is_type ? "type " : "") << "name "
+ << "'" << e.name () << "' in db pragma " << prag << endl;
+ else
+ error (l) << "unable to resolve name '" << e.name () << "' in db pragma "
+ << prag << endl;
+
+ return 0;
+ }
+}
+
+// Resolve a data member in the specified scope taking into account virtual
+// member declarations.
+//
+declaration
+resolve_data_member (tree scope,
+ const cxx_tokens& name,
+ string& decl_name, // Note: appended to.
+ string const& prag)
+{
+ declaration decl;
+
+ if (name.size () == 1 && name.back ().type == CPP_NAME)
+ {
+ virt_declarations::iterator i (virt_declarations_.find (scope));
+
+ if (i != virt_declarations_.end ())
+ {
+ string const& n (name.back ().literal);
+
+ virt_declaration_set::const_iterator j (i->second.find (n, FIELD_DECL));
+
+ if (j != i->second.end ())
+ {
+ decl = declaration (*j);
+ decl_name += n;
+ }
+ }
+ }
+
+ if (!decl)
+ {
+ cxx_tokens_lexer l;
+ l.start (name);
+
+ tree tn;
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ tree d (resolve_scoped_name (
+ l, tt, tl, tn, scope, decl_name, false, prag));
+
+ if (d == 0)
+ return decl; // Diagnostics has already been issued.
+
+ decl = declaration (d);
+ }
+
+ return decl;
+}
+
+static bool
+check_spec_decl_type (declaration const& d,
+ string const& name,
+ string const& p,
+ location_t l)
+{
+ gcc_tree_code_type tc (d.tree_code ());
+ bool type (TREE_CODE_CLASS (tc) == tcc_type);
+
+ if (p == "no_id")
+ {
+ // No_id can be used on objects only.
+ //
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "id" ||
+ p == "auto" ||
+ p == "column" ||
+ p == "inverse" ||
+ p == "on_delete" ||
+ p == "points_to" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
+ p == "version" ||
+ p == "index" ||
+ p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access")
+ {
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "transient")
+ {
+ // Transient can be used for both data members and classes (object,
+ // view, or composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "added")
+ {
+ // Added can be used for data members only.
+ //
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "deleted")
+ {
+ // Deleted can be used for both data members and classes (object,
+ // view of composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "readonly")
+ {
+ // Readonly can be used for both data members and classes (object or
+ // composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "abstract" ||
+ p == "callback" ||
+ p == "query" ||
+ p == "object" ||
+ p == "optimistic" ||
+ p == "polymorphic" ||
+ p == "definition" ||
+ p == "sectionable" ||
+ p == "bulk")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "pointer")
+ {
+ // Table can be used for namespaces and classes (object or view).
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "table")
+ {
+ // Table can be used for namespaces, members (container), and types
+ // (container, object, or view).
+ //
+ if (tc != NAMESPACE_DECL && tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace, type, or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "session")
+ {
+ // Session can be used only for namespaces and objects.
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "schema")
+ {
+ // For now schema can be used only for namespaces and objects.
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type")
+ {
+ // Type can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "default")
+ {
+ // Default can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column")
+ {
+ // Container columns can be used for both members (container) and
+ // types (container).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options")
+ {
+ // Options can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null")
+ {
+ // Null pragmas can be used for both members and types (values,
+ // containers, and pointers).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "unordered")
+ {
+ // Unordered can be used for both members (container) and
+ // types (container).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "virtual")
+ {
+ // Virtual is specified for a member.
+ //
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "simple" ||
+ p == "container")
+ {
+ // Apply to both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return false;
+ }
+
+ return true;
+}
+
+static void
+add_pragma (pragma const& prag, declaration const& decl, bool ns)
+{
+ if (decl)
+ decl_pragmas_[decl].insert (prag);
+ else
+ {
+ tree scope (current_scope ());
+
+ if (!ns)
+ {
+ if (!CLASS_TYPE_P (scope))
+ scope = global_namespace;
+
+ loc_pragmas_[scope].push_back (prag);
+ }
+ else
+ ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
+ }
+}
+
+static void
+handle_pragma (cxx_lexer& l,
+ string db,
+ string p,
+ string const& qualifier,
+ any& qualifier_value,
+ declaration const& decl,
+ string const& decl_name,
+ bool ns) // True if this is a position namespace pragma.
+{
+ cpp_ttype tt;
+ string tl;
+ tree tn;
+
+ // See if there is a database prefix. The db argument may already
+ // contain it.
+ //
+ if (db.empty () &&
+ (p == "mysql" ||
+ p == "sqlite" ||
+ p == "pgsql" ||
+ p == "oracle" ||
+ p == "mssql"))
+ {
+ tt = l.next (tl);
+
+ if (tt != CPP_COLON)
+ {
+ error (l) << "':' expected after database prefix " << p << endl;
+ return;
+ }
+
+ // Specifier prefix.
+ //
+ db = p;
+ tt = l.next (p);
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after databas prefix " << db << endl;
+ return;
+ }
+ }
+
+ string name (p); // Pragma name.
+ any val; // Pragma value.
+ pragma::add_func adder (0); // Custom context adder.
+ location_t loc (l.location ()); // Pragma location.
+
+ if (qualifier == "model")
+ {
+ // version(unsigned long long base,
+ // unsigned long long current,
+ // open|closed)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+
+ if (p == "version")
+ {
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ model_version v;
+
+ // base
+ //
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as base version" << endl;
+ return;
+ }
+
+ v.base = integer_value (tn);
+
+ if (v.base == 0)
+ {
+ error (l) << "base version cannot be zero" << endl;
+ return;
+ }
+
+ // current
+ //
+ if (l.next (tl, &tn) != CPP_COMMA)
+ {
+ error (l) << "current version expected after base version" << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as current version" << endl;
+ return;
+ }
+
+ v.current = integer_value (tn);
+
+ if (v.current == 0)
+ {
+ error (l) << "current version cannot be zero" << endl;
+ return;
+ }
+
+ if (v.base > v.current)
+ {
+ error (l) << "current version should be greater than or equal to " <<
+ "base version" << endl;
+ return;
+ }
+
+ // open|closed
+ //
+ tt = l.next (tl, &tn);
+ if (tt == CPP_COMMA)
+ {
+ if (l.next (tl, &tn) != CPP_NAME || (tl != "open" && tl != "closed"))
+ {
+ error (l) << "open or closed expected after current version" << endl;
+ return;
+ }
+
+ v.open = (tl == "open");
+ tt = l.next (tl, &tn);
+ }
+ else
+ v.open = true;
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name = "model-version";
+ val = v;
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+ }
+ else if (qualifier == "map")
+ {
+ // type("<regex>") | type(<name>)
+ // as("<subst>") | as(<name>)
+ // to("<subst>") | to(<expr>)
+ // from("<subst>") | from(<expr>)
+ //
+
+ if (p != "type" &&
+ p != "as" &&
+ p != "to" &&
+ p != "from")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (qualifier_value.type_info () == typeid (custom_cxx_type))
+ {
+ // C++ type mapping.
+ //
+ custom_cxx_type& ct (qualifier_value.value<custom_cxx_type> ());
+
+ if (p == "type" || p == "as")
+ {
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ string name;
+ tree decl (
+ resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), name, true, p));
+
+ if (decl == 0)
+ return; // Diagnostics has already been issued.
+
+ if (TREE_CODE (decl) != TYPE_DECL)
+ {
+ error (loc) << "name '" << name << "' in db pragma "
+ << p << " does not refer to a type" << endl;
+ return;
+ }
+
+ (p == "type" ? ct.type_node : ct.as_node) = TREE_TYPE (decl);
+ (p == "type" ? ct.type_name : ct.as_name) = name;
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ else if (p == "to" || p == "from")
+ {
+ if (tt != CPP_CLOSE_PAREN) // Empty expression is ok.
+ {
+ if (!parse_expression (
+ l, tt, tl, tn, (p == "to" ? ct.to : ct.from), p))
+ return; // Diagnostics has already been issued.
+ }
+ }
+ }
+ else
+ {
+ using relational::custom_db_type;
+
+ // Database type mapping.
+ //
+ custom_db_type& ct (qualifier_value.value<custom_db_type> ());
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name regex expected in db pragma " << p << endl;
+ return;
+ }
+
+ try
+ {
+ // Make it case-insensitive.
+ //
+ ct.type.assign (tl, true);
+ }
+ catch (regex_format const& e)
+ {
+ error (l) << "invalid regex: '" << e.regex () << "' in db pragma "
+ << p << ": " << e.description () << endl;
+ return;
+ }
+ }
+ else if (p == "as")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.as = tl;
+ }
+ else if (p == "to")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.to = tl;
+ }
+ else if (p == "from")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.from = tl;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (qualifier == "index")
+ {
+ // unique
+ // type("<type>")
+ // method("<method>")
+ // options("<options>")
+ // member(<name>[, "<options>"])
+ // members(<name>[, <name>...])
+ //
+
+ if (p != "unique" &&
+ p != "type" &&
+ p != "method" &&
+ p != "options" &&
+ p != "member" &&
+ p != "members")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ using relational::index;
+ index& in (qualifier_value.value<index> ());
+
+ if (p == "unique")
+ in.type = "UNIQUE";
+ else
+ {
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index type expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.type = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "method")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index method expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.method = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "options")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index options expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.options = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "member")
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = loc;
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ // Parse member options, if any.
+ //
+ if (tt == CPP_COMMA)
+ {
+ if (l.next (tl, &tn) != CPP_STRING)
+ {
+ error (l) << "index member options expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.options = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ in.members.push_back (m);
+ }
+ else if (p == "members")
+ {
+ for (;;)
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = l.location ();
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ in.members.push_back (m);
+
+ if (tt == CPP_COMMA)
+ tt = l.next (tl, &tn);
+ else
+ break;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "index" ||
+ p == "unique")
+ {
+ // index
+ // unique
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "get" ||
+ p == "set" ||
+ p == "access")
+ {
+ // get(name|expr)
+ // set(name|expr)
+ // access(name|expr)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ member_access ma (loc, p == "set" ? "modifier" : "accessor", false);
+
+ tt = l.next (tl, &tn);
+ if (tt != CPP_CLOSE_PAREN) // Empty expression are ok.
+ {
+ if (!parse_expression (l, tt, tl, tn, ma.expr, p))
+ return; // Diagnostics has already been issued.
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+ }
+
+ val = ma;
+
+ // Convert access to the get/set pair.
+ //
+ if (p == "access")
+ {
+ if (db.empty () || db == pragma_db_.string ())
+ add_pragma (
+ pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns);
+
+ ma.kind = "modifier";
+ val = ma;
+ name = "set";
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "table")
+ {
+ // table (<name>)
+ // table (<name> [= "<alias>"] [type] [: "<cond>"] (view only)
+ //
+ // <name> := "name" | "name.name" | "name"."name"
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING && tt != CPP_DOT)
+ {
+ error (l) << "table name expected in db pragma " << p << endl;
+ return;
+ }
+
+ // The table specifier is used for both objects and views. In case
+ // of an object, the context values is just a string. In case of a
+ // view, the context value is a view_object entry. The problem is
+ // that at this stage we don't know whether we are dealing with an
+ // object or a view. To resolve this in a somewhat hackish way, we
+ // are going to create both a string and a view_object entry.
+ //
+ view_object vo;
+ vo.kind = view_object::table;
+
+ if (!parse_qname (l, tt, tl, tn, p, vo.tbl_name))
+ return; // Diagnostics has already been issued.
+
+ if (tt == CPP_EQ)
+ {
+ // We have an alias.
+ //
+ if (l.next (tl, &tn) != CPP_STRING)
+ {
+ error (l) << "table alias expected after '=' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ vo.alias = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
+ if (tt == CPP_COLON)
+ {
+ // We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (!parse_expression (l, tt, tl, tn, vo.cond, p))
+ return; // Diagnostics has already been issued.
+
+ if (vo.cond.empty ())
+ {
+ error (l) << "join condition expected after ':' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ // Add the "table" pragma.
+ //
+ if (vo.alias.empty () && vo.cond.empty ())
+ {
+ if (db.empty () || db == pragma_db_.string ())
+ {
+ add_pragma (
+ pragma (p, name, vo.tbl_name, loc, &check_spec_decl_type, 0),
+ decl,
+ ns);
+ }
+ }
+
+ vo.scope = current_scope ();
+ vo.loc = loc;
+ val = vo;
+ name = "objects";
+ adder = &accumulate<view_object>;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "session")
+ {
+ // session
+ // session (true|false)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_KEYWORD || (tl != "true" && tl != "false"))
+ {
+ error (l) << "true or false expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = (tl == "true");
+
+ tt = l.next (tl, &tn);
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ }
+ else if (p == "schema")
+ {
+ // schema (<name>)
+ //
+ // <name> := "name" | "name.name" | "name"."name"
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING && tt != CPP_DOT)
+ {
+ error (l) << "table name expected in db pragma " << p << endl;
+ return;
+ }
+
+ qname s;
+ if (!parse_qname (l, tt, tl, tn, p, s))
+ return; // Diagnostics has already been issued.
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = s;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "pointer")
+ {
+ // pointer (qname)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ class_pointer cp;
+ size_t pb (0);
+ bool punc (false);
+
+ for (tt = l.next (tl, &tn);
+ tt != CPP_EOF && (tt != CPP_CLOSE_PAREN || pb != 0);
+ tt = l.next (tl, &tn))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ cp.name += ' ';
+
+ punc = false;
+
+ if (tt == CPP_OPEN_PAREN)
+ pb++;
+ else if (tt == CPP_CLOSE_PAREN)
+ pb--;
+
+ // @@ Need to handle literals, at least integer.
+ //
+ switch (tt)
+ {
+ case CPP_LESS:
+ {
+ cp.name += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ cp.name += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ cp.name += ", ";
+ break;
+ }
+ case CPP_NAME:
+ // case CPP_KEYWORD: // see default:
+ {
+ cp.name += tl;
+ punc = true;
+ break;
+ }
+ default:
+ {
+ if (tt == CPP_KEYWORD)
+ {
+ cp.name += tl;
+ punc = true;
+ }
+ else if (tt <= CPP_LAST_PUNCTUATOR)
+ cp.name += cxx_lexer::token_spelling[tt];
+ else
+ {
+ error (l) << "unexpected token '" << cxx_lexer::token_spelling[tt]
+ << "' in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ if (cp.name.empty ())
+ {
+ error (l) << "expected pointer name in db pragma " << p << endl;
+ return;
+ }
+
+ cp.scope = current_scope ();
+ cp.loc = loc;
+ val = cp;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "abstract")
+ {
+ // abstract
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "optimistic")
+ {
+ // optimistic
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "polymorphic")
+ {
+ // polymorphic
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "definition")
+ {
+ // definition
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ val = l.location ();
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "sectionable")
+ {
+ // sectionable
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "callback")
+ {
+ // callback (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member function name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "bulk")
+ {
+ // bulk (batch-size)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as batch size" << endl;
+ return;
+ }
+
+ unsigned long long b (integer_value (tn));
+
+ if (b == 0 || b == 1)
+ {
+ error (l) << "batch size has to be greater than 1" << endl;
+ return;
+ }
+
+ val = b;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "query")
+ {
+ // query ()
+ // query ("statement")
+ // query (expr)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ view_query vq;
+
+ bool s (false);
+ string str;
+
+ if (tt == CPP_STRING)
+ {
+ s = true;
+ str = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ if (s)
+ vq.literal = str;
+ else
+ {
+ // Empty query() pragma indicates that the statement will be
+ // provided at runtime. Encode this case as empty literal
+ // and expression.
+ //
+ }
+ }
+ else
+ {
+ // Expression.
+ //
+ if (s)
+ vq.expr.push_back (cxx_token (0, CPP_STRING, str));
+
+ if (!parse_expression (l, tt, tl, tn, vq.expr, p))
+ return; // Diagnostics has already been issued.
+ }
+
+ // Disallow query(, distinct).
+ //
+ if (tt == CPP_COMMA && vq.expr.empty ())
+ {
+ error (l) << "query expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ // The query expression can be omitted with the modifier in its
+ // place. Handle this case.
+ //
+ if (vq.expr.size () == 1 && vq.expr.front ().type == CPP_NAME)
+ {
+ string const& n (vq.expr.front ().literal);
+
+ if (n == "distinct")
+ vq.distinct = true;
+ else if (n == "for_update")
+ vq.for_update = true;
+
+ if (vq.distinct || vq.for_update)
+ vq.expr.clear ();
+ }
+
+ if (tt == CPP_COMMA)
+ {
+ do
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "result modifier expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tl == "distinct")
+ vq.distinct = true;
+ else if (tl == "for_update")
+ vq.for_update = true;
+ else
+ {
+ error (l) << "unknown result modifier '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ } while (tt == CPP_COMMA);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ vq.scope = current_scope ();
+ vq.loc = loc;
+ val = vq;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "object")
+ {
+ // object (fq-name [ = name] [type] [: expr])
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME && tt != CPP_SCOPE)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ view_object vo;
+ vo.kind = view_object::object;
+ vo.obj_node = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), vo.obj_name, true, p);
+
+ if (vo.obj_node == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main variant.
+ //
+ if (TREE_CODE (vo.obj_node) == TYPE_DECL)
+ vo.obj_node = TREE_TYPE (vo.obj_node);
+
+ if (TYPE_P (vo.obj_node)) // Can be a template.
+ vo.obj_node = TYPE_MAIN_VARIANT (vo.obj_node);
+
+ if (tt == CPP_EQ)
+ {
+ // We have an alias.
+ //
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "alias name expected after '=' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ vo.alias = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
+ if (tt == CPP_COLON)
+ {
+ // We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (!parse_expression (l, tt, tl, tn, vo.cond, p))
+ return; // Diagnostics has already been issued.
+
+ if (vo.cond.empty ())
+ {
+ error (l) << "join condition expected after ':' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ vo.scope = current_scope ();
+ vo.loc = loc;
+ val = vo;
+ name = "objects"; // Change the context entry name.
+ adder = &accumulate<view_object>;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "id")
+ {
+ // id[(member-path)]
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ string name;
+
+ tt = l.next (tl, &tn);
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ name = tl;
+
+ for (tt = l.next (tl, &tn); tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ val = name;
+ }
+ else if (p == "no_id")
+ {
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ name = "id";
+ val = false;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "auto")
+ {
+ // auto
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "column")
+ {
+ // column ("<name>")
+ // column ("<name>.<name>") (view only)
+ // column ("<name>"."<name>") (view only)
+ // column (fq-name) (view only)
+ // column (expr) (view only)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ bool expr (false);
+ string expr_str;
+ if (tt == CPP_STRING || tt == CPP_DOT)
+ {
+ qname qn;
+
+ if (!parse_qname (l, tt, tl, tn, p, qn, &expr, &expr_str))
+ return; // Diagnostics has already been issued.
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ table_column tc;
+ tc.expr = expr;
+
+ if (expr)
+ tc.column = expr_str;
+ else
+ {
+ tc.table = qn.qualifier ();
+ tc.column = qn.uname ();
+ }
+
+ val = tc;
+ }
+ else if (!expr)
+ {
+ error (l) << "column name, expression, or data member name expected "
+ << "in db pragma " << p << endl;
+ return;
+ }
+ }
+
+ if (val.empty ())
+ {
+ // We have an expression.
+ //
+ column_expr e;
+
+ if (expr)
+ {
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::literal;
+ e.back ().value = expr_str;
+
+ if (tt != CPP_PLUS)
+ {
+ error (l) << "'+' or ')' expected in db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ for (;;)
+ {
+ if (tt == CPP_STRING)
+ {
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::literal;
+ e.back ().value = tl;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ string name (parse_scoped_name (l, tt, tl, tn, p));
+
+ if (name.empty ())
+ return; // Diagnostics has already been issued.
+
+ // Resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::reference;
+ e.back ().value = name;
+ e.back ().scope = current_scope ();
+ e.back ().loc = loc;
+ }
+ else
+ {
+ error (l) << "column name, expression, or data member name expected "
+ << "in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt == CPP_PLUS)
+ tt = l.next (tl, &tn);
+ else if (tt == CPP_CLOSE_PAREN)
+ break;
+ else
+ {
+ error (l) << "'+' or ')' expected in db pragma " << p << endl;
+ return;
+ }
+ }
+
+ e.loc = loc;
+ val = e;
+ name = "column-expr";
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column")
+ {
+ // value_column ("<name>")
+ // index_column ("<name>")
+ // key_column ("<name>")
+ // id_column ("<name>")
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "column name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options")
+ {
+ // options (["<name>"])
+ // value_options (["<name>"])
+ // index_options (["<name>"])
+ // key_options (["<name>"])
+ // id_options (["<name>"])
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_STRING)
+ {
+ // Ignore empty options strings. Internally, we use them to
+ // indicate options reset (see below).
+ //
+ if (!tl.empty ())
+ val = tl;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_CLOSE_PAREN)
+ {
+ // Empty options specifier signals options reset. Encode it as an
+ // empty string.
+ //
+ val = string ();
+ }
+ else
+ {
+ error (l) << "options string expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ adder = &accumulate<string>;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type")
+ {
+ // type ("<type>")
+ // id_type ("<type>")
+ // value_type ("<type>")
+ // index_type ("<type>")
+ // key_type ("<type>")
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null")
+ {
+ // null
+ // not_null
+ // key_null
+ // key_not_null
+ // value_null
+ // value_not_null
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "default")
+ {
+ // default () (<empty>)
+ // default (null) (n)
+ // default (true|false) (t|f)
+ // default ([+|-]<number>) (-|+)
+ // default ("string") (s)
+ // default (<enumerator>) (e)
+ //
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ default_value dv;
+
+ switch (tt)
+ {
+ case CPP_CLOSE_PAREN:
+ {
+ // Default value reset.
+ //
+ dv.kind = default_value::reset;
+ break;
+ }
+ case CPP_STRING:
+ {
+ dv.kind = default_value::string;
+ dv.literal = tl;
+ tt = l.next (tl, &tn);
+ break;
+ }
+ case CPP_NAME:
+ {
+ // This can be null or an enumerator name.
+ //
+ if (tl == "null")
+ {
+ dv.kind = default_value::null;
+ tt = l.next (tl, &tn);
+ break;
+ }
+ }
+ // Fall through.
+ case CPP_SCOPE:
+ {
+ // We have a potentially scopped enumerator name.
+ //
+ dv.enum_value = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), dv.literal, false, p);
+
+ if (dv.enum_value == 0)
+ return; // Diagnostics has already been issued.
+
+ dv.kind = default_value::enumerator;
+ break;
+ }
+ case CPP_MINUS:
+ case CPP_PLUS:
+ {
+ if (tt == CPP_MINUS)
+ dv.literal = "-";
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NUMBER)
+ {
+ error (l) << "expected numeric constant after '"
+ << (tt == CPP_MINUS ? "-" : "+") << "' in db pragma "
+ << p << endl;
+ return;
+ }
+ }
+ // Fall through.
+ case CPP_NUMBER:
+ {
+ switch (TREE_CODE (tn))
+ {
+ case INTEGER_CST:
+ {
+ dv.int_value = integer_value (tn);
+ dv.kind = default_value::integer;
+ break;
+ }
+ case REAL_CST:
+ {
+ REAL_VALUE_TYPE d (TREE_REAL_CST (tn));
+
+ if (REAL_VALUE_ISINF (d))
+ dv.float_value = numeric_limits<double>::infinity ();
+ else if (REAL_VALUE_ISNAN (d))
+ dv.float_value = numeric_limits<double>::quiet_NaN ();
+ else
+ {
+ char tmp[256];
+ real_to_decimal (tmp, &d, sizeof (tmp), 0, true);
+ istringstream is (tmp);
+ is >> dv.float_value;
+ }
+
+ if (dv.literal == "-")
+ dv.float_value = -dv.float_value;
+
+ dv.kind = default_value::floating;
+ break;
+ }
+ default:
+ {
+ error (l) << "unexpected numeric constant in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ tt = l.next (tl, &tn);
+ break;
+ }
+ default:
+ {
+ // This can be the true or false keyword.
+ //
+ if (tt == CPP_KEYWORD && (tl == "true" || tl == "false"))
+ {
+ dv.kind = default_value::boolean;
+ dv.literal = tl;
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "unexpected expression in db pragma " << p << endl;
+ return;
+ }
+
+ break;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = dv;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "inverse")
+ {
+ // inverse (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ string name (tl);
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = name;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "on_delete")
+ {
+ // on_delete (cascade|set_null)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NAME || (tl != "cascade" && tl != "set_null"))
+ {
+ error (l) << "cascade or set_null expected after '('" << endl;
+ return;
+ }
+
+ using semantics::relational::foreign_key;
+ val = (tl == "cascade" ? foreign_key::cascade : foreign_key::set_null);
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "points_to")
+ {
+ // points_to(<fq-name>)
+ //
+
+ tree type;
+ string type_name;
+
+ string p (tl);
+ location_t loc (l.location ());
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ type = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), type_name, true, p);
+
+ if (type == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (type) == TYPE_DECL)
+ type = TREE_TYPE (type);
+
+ if (TYPE_P (type)) // Can be a template.
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (TREE_CODE (type) != RECORD_TYPE)
+ {
+ error (loc) << "name '" << type_name << "' in db pragma " << p
+ << " does not refer to a class" << endl;
+ return;
+ }
+
+ val = type;
+ }
+ else
+ {
+ error (l) << "class name expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "section")
+ {
+ // section (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-member";
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "load")
+ {
+ // load (eager|lazy)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME || (tl != "eager" && tl != "lazy"))
+ {
+ error (l) << "eager or lazy expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-load";
+ val = (tl == "eager"
+ ? user_section::load_eager
+ : user_section::load_lazy);
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "update")
+ {
+ // update (always|change|manual)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME ||
+ (tl != "always" && tl != "change" && tl != "manual"))
+ {
+ error (l) << "always, change, or manual expected in db pragma " <<
+ p << endl;
+ return;
+ }
+
+ name = "section-update";
+
+ if (tl == "always")
+ val = user_section::update_always;
+ else if (tl == "change")
+ val = user_section::update_change;
+ else
+ val = user_section::update_manual;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "unordered")
+ {
+ // unordered
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "readonly")
+ {
+ // readonly
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "transient")
+ {
+ // transient
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "added" || p == "deleted")
+ {
+ // added (unsigned long long version)
+ // deleted (unsigned long long version)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ char const* n (p == "added" ? "addition" : "deletion");
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as " << n << " version" << endl;
+ return;
+ }
+
+ unsigned long long v (integer_value (tn));
+
+ if (v == 0)
+ {
+ error (l) << n << " version cannot be zero" << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = v;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "version")
+ {
+ // version
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "virtual")
+ {
+ // Stray virtual specifier (i.e., without explicit member name).
+ //
+ error (l) << "virtual member declaration requires name" << endl;
+ return;
+ }
+ else if (p == "before" || p == "after")
+ {
+ // Stray before/after specifier (i.e., without preceding virtual).
+ //
+ error (l) << p << " specifier must follow virtual" << endl;
+ return;
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Add the pragma unless was indicated otherwise.
+ //
+ if (!name.empty () && (db.empty () || db == pragma_db_.string ()))
+ {
+ // If the value is not specified and we don't use a custom adder,
+ // then make it bool (flag).
+ //
+ if (adder == 0 && val.empty ())
+ val = true;
+
+ // Convert '_' to '-' in the context name so we get foo-bar instead
+ // of foo_bar (that's the convention used).
+ //
+ for (size_t i (0); i < name.size (); ++i)
+ if (name[i] == '_')
+ name[i] = '-';
+
+ // Record this pragma.
+ //
+ add_pragma (
+ pragma (p, name, val, loc, &check_spec_decl_type, adder), decl, ns);
+ }
+
+ // Mark the type or member as simple value or container, depending
+ // on the pragma. For static multi-database support we only do it
+ // if the pragma applies to this database since in this case we can
+ // have different mappings for different databases (e.g., composite
+ // in one and simple in another). For dynamic multi-database support
+ // we do this regardless of the database since here the mapping
+ // should the consistent.
+ //
+ // @@ Did we add new simple value or container pragmas and forgot to
+ // account for them here?
+ //
+ if ((qualifier == "value" || qualifier == "member") &&
+ (pragma_multi_ == multi_database::dynamic || db.empty () ||
+ db == pragma_db_.string ()))
+ {
+ // We assume a data member is simple only if the database type was
+ // specified explicitly.
+ //
+ if (name == "type" ||
+ name == "id-type" ||
+ (qualifier == "value" &&
+ (name == "null" ||
+ name == "not-null" ||
+ name == "default" ||
+ name == "options")))
+ {
+ add_pragma (pragma (p, "simple", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ else if (name == "table" ||
+ name == "value-type" ||
+ name == "index-type" ||
+ name == "key-type" ||
+
+ name == "key-null" ||
+ name == "key-not-null" ||
+ name == "value-null" ||
+ name == "value-not-null" ||
+
+ name == "value-column" ||
+ name == "index-column" ||
+ name == "key-column" ||
+ name == "index-column" ||
+
+ name == "value-options" ||
+ name == "index-options" ||
+ name == "key-options" ||
+ name == "index-options" ||
+
+ name == "unordered")
+ {
+ add_pragma (pragma (p, "container", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ }
+
+ // See if there are any more pragmas.
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ handle_pragma (l,
+ "",
+ tl,
+ qualifier,
+ qualifier_value,
+ decl,
+ decl_name,
+ ns);
+ }
+ else if (tt != CPP_EOF)
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+}
+
+//
+// Qualifiers.
+//
+
+static bool
+check_qual_decl_type (declaration const& d,
+ string const& name,
+ string const& p,
+ location_t l)
+{
+ gcc_tree_code_type tc (d.tree_code ());
+ bool type (TREE_CODE_CLASS (tc) == tcc_type);
+
+ if (p == "model" ||
+ p == "map")
+ {
+ assert (d == global_namespace);
+ }
+ else if (p == "index")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ // For an index, name is not empty only if the class name was
+ // specified explicitly. Otherwise, the index definition scope
+ // is assumed.
+ //
+ if (name.empty ())
+ {
+ error (l) << "db pragma " << p << " outside of a class scope" << endl;
+ info (l) << "use the db pragma " << p << "(<class-name>) syntax "
+ << " instead" << endl;
+ }
+ else
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "namespace")
+ {
+ if (tc != NAMESPACE_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace" << endl;
+ return false;
+ }
+ }
+ else if (p == "object" ||
+ p == "view")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "value")
+ {
+ if (!type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type" << endl;
+ return false;
+ }
+ }
+ else if (p == "member")
+ {
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return false;
+ }
+
+ return true;
+}
+
+static void
+add_qual_entry (compiler::context& ctx,
+ string const& k,
+ any const& v,
+ location_t l)
+{
+ // Store the TYPE_DECL node that was referred to in the pragma. This
+ // can be used later as a name hint in case the type is a template
+ // instantiation. Also store the pragma location which is used as
+ // the "definition point" for this instantiation.
+ //
+ ctx.set ("tree-node", v);
+ ctx.set ("location", l);
+
+ ctx.set (k, true);
+}
+
+static void
+handle_pragma_qualifier (cxx_lexer& l, string p)
+{
+ cpp_ttype tt;
+ string tl;
+ tree tn;
+
+ declaration decl;
+ tree orig_decl (0); // Original declarations as used in the pragma.
+ string decl_name;
+
+ // Check for a database prefix.
+ //
+ string db;
+
+ if (p == "mysql" ||
+ p == "sqlite" ||
+ p == "pgsql" ||
+ p == "oracle" ||
+ p == "mssql")
+ {
+ tt = l.next (tl);
+
+ if (tt == CPP_COLON)
+ {
+ // Specifier prefix.
+ //
+ db = p;
+ tt = l.next (p);
+ }
+ else
+ {
+ // Qualifier prefix. Ignore the rest if the databases don't match.
+ //
+ if (p != pragma_db_.string ())
+ return;
+
+ p = tl;
+ }
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after db " << db << " pragma" << endl;
+ return;
+ }
+
+ // Make sure a qualifier prefix is not used before a specifier.
+ //
+ if (!db.empty () &&
+ (p == "model" ||
+ p == "map" ||
+ p == "namespace" ||
+ p == "object" ||
+ p == "view" ||
+ p == "value" ||
+ p == "member"))
+ {
+ error (l) << "specifier prefix '" << db << ":' used before qualifier " <<
+ p << endl;
+ return;
+ }
+ }
+
+ //
+ //
+ string name (p); // Pragma name.
+ any val; // Pragma value.
+ location_t loc (l.location ()); // Pragma location.
+ pragma::add_func adder (0); // Custom context adder.
+ bool ns (false); // Namespace location pragma.
+
+ cxx_tokens saved_tokens; // Saved token sequence to be replayed.
+
+ // Pragma qualifiers.
+ //
+ if (p == "model")
+ {
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "map")
+ {
+ // map type("<regex>") as("<subst>") [to("<subst>")] [from("<subst>")]
+ // map type(<c++-type>) as(<c++-type>) [to(<expr>)] [from(<expr>)]
+ //
+
+ // The thing that determines whether this is a database or C++ type
+ // mapping is what we have inside 'type'. So to handle this we are
+ // going to pre-scan the pragma looking for 'type' and saving the
+ // tokens. Once we determine what this is, we replay the saved
+ // tokens to actually parse them.
+ //
+
+ // Determine what this is by scanning the pragma until we see
+ // the 'type' qualifier or EOF.
+ //
+ bool db (true);
+
+ bool done (false);
+ size_t balance (0);
+ for (tt = l.next (tl, &tn);
+ !(done && balance == 0) && tt != CPP_EOF;
+ tt = l.next (tl, &tn))
+ {
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ continue;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ continue;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && tl == "type")
+ break;
+
+ continue;
+ }
+ default:
+ continue;
+ }
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ balance++;
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ db = (tt == CPP_STRING);
+ }
+
+ done = true; // Scan until the closing ')'.
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+
+ if (db)
+ {
+ using relational::custom_db_type;
+
+ custom_db_type ct;
+ ct.loc = loc;
+ val = ct;
+ name = "custom-db-types";
+ adder = &accumulate<custom_db_type>;
+ }
+ else
+ {
+ custom_cxx_type ct;
+ ct.loc = loc;
+ ct.scope = current_scope ();
+ val = ct;
+ name = "custom-cxx-types";
+ adder = &accumulate<custom_cxx_type>;
+ }
+ }
+ else if (p == "index")
+ {
+ // Index can be both a qualifier and a specifier. Things are complicated
+ // by the fact that when it is a specifier, it belongs to a member which
+ // means that the actual qualifier ('member') can be omitted. So we need
+ // to distinguish between cases like these:
+ //
+ // #pragma db index type("INTEGER") // specifier
+ // #pragma db index type("UNIQUE") member(foo_) // qualifier
+ //
+ // The thing that determines whether this is a qualifier or a specifier
+ // is the presence of the 'member' or 'members' specifier. So to handle
+ // this we are going to pre-scan the pragma looking for 'member' or
+ // 'members' and saving the tokens. Once we determine what this is,
+ // we replay the saved tokens to actually parse them.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_OPEN_PAREN)
+ {
+ // Determine what this is by scanning the pragma until we see
+ // the 'member' qualifier or EOF.
+ //
+ bool qual (false);
+ size_t balance (0);
+
+ for (; tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && (tl == "member" || tl == "members"))
+ qual = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (qual)
+ break;
+
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ if (qual)
+ {
+ // This is a qualifer. The saved tokens sequence contains tokens
+ // until the first 'member' or 'members' specifier. So we will
+ // first need to re-play these tokens and then continue parsing
+ // as if we just saw the 'member' or 'members' specifier. The
+ // token type (tt) and token literal (tl) variables should contain
+ // the correct values.
+ //
+
+ // Also check that no specifier prefix was used for this qualifer.
+ //
+ if (!db.empty ())
+ {
+ error (loc) << "specifier prefix '" << db << ":' used before " <<
+ "qualifier index" << endl;
+ return;
+ }
+
+ orig_decl = current_scope ();
+ decl = declaration (orig_decl);
+ }
+ else
+ {
+ // This is a specifier. The saved tokens sequence contains all the
+ // tokens in this pragma until EOF.
+ //
+ cxx_tokens_lexer l;
+ l.start (saved_tokens, loc);
+ handle_pragma (
+ l, db, "index", "member", val, declaration (), "", false);
+ return;
+ }
+ }
+
+ relational::index in;
+ in.loc = loc;
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ // Specifier with the class fq-name, index name, or both.
+ //
+ // index(<fq-name>)
+ // index("<name>")
+ // index(<fq-name>::"<name>")
+ //
+ tt = l.next (tl, &tn);
+
+ // Resolve class name, if any.
+ //
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ cpp_ttype ptt;
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p, true, &ptt);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (orig_decl) == TYPE_DECL)
+ orig_decl = TREE_TYPE (orig_decl);
+
+ if (TYPE_P (orig_decl)) // Can be a template.
+ decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
+ else
+ decl = declaration (orig_decl);
+
+ if (tt == CPP_STRING && ptt != CPP_SCOPE)
+ {
+ error (l) << "'::' expected before index name in db pragma " << p
+ << endl;
+ return;
+ }
+
+ if (tt != CPP_STRING && ptt == CPP_SCOPE)
+ {
+ error (l) << "index name expected after '::' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+ else
+ {
+ orig_decl = current_scope ();
+ decl = declaration (orig_decl);
+ }
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (tt == CPP_STRING)
+ {
+ in.name = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ val = in;
+ adder = &accumulate<relational::index>;
+ }
+ else if (p == "namespace")
+ {
+ // namespace [(<identifier>)]
+ // namespace () (refers to global namespace)
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, false, p);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (orig_decl, decl_name, p, loc))
+ return;
+
+ // Resolve namespace aliases if any.
+ //
+ orig_decl = ORIGINAL_NAMESPACE (orig_decl);
+ decl = declaration (orig_decl);
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_CLOSE_PAREN)
+ {
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ else
+ {
+ // Make sure we are in a namespace scope.
+ //
+ if (TREE_CODE (current_scope ()) != NAMESPACE_DECL)
+ {
+ error (l) << "db pragma " << p << " is not in a namespace scope"
+ << endl;
+ return;
+ }
+
+ ns = true;
+ }
+ }
+ else if (p == "object" ||
+ p == "view" ||
+ p == "value")
+ {
+ // object [(<identifier>)]
+ // view [(<identifier>)]
+ // value [(<identifier>)]
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (orig_decl) == TYPE_DECL)
+ orig_decl = TREE_TYPE (orig_decl);
+
+ if (TYPE_P (orig_decl)) // Can be a template.
+ decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
+ else
+ decl = declaration (orig_decl);
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ }
+ else if (p == "member")
+ {
+ // member [(<identifier>)]
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME && tt != CPP_SCOPE)
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ // We need to see if this is a virtual data member declaration. Also,
+ // if it is not, then the name can still refer to one so we need to
+ // take extra steps to handle that. But first, we save the name and
+ // look for the 'virtual' specifier.
+ //
+ cxx_tokens name_tokens;
+ for (; tt != CPP_CLOSE_PAREN && tt != CPP_EOF; tt = l.next (tl, &tn))
+ name_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ // Now scan the remainder of the pragma looking for the 'virtual'
+ // keyword and saving the tokens in between for later.
+ //
+ bool virt (false);
+ size_t balance (0);
+ for (tt = l.next (tl, &tn); tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ default:
+ {
+ if (balance == 0 && tt == CPP_KEYWORD && tl == "virtual")
+ virt = true;
+ break;
+ }
+ }
+
+ if (virt)
+ break;
+
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ // Regardless of whether this is a virtual member declaration or a
+ // reference, resolve its scope name (if one is specified), which
+ // should be a class. We will need it in both cases.
+ //
+ tree scope;
+ if (name_tokens.size () > 2) // scope::name
+ {
+ size_t n (name_tokens.size ());
+
+ if (name_tokens[n - 2].type != CPP_SCOPE ||
+ name_tokens[n - 1].type != CPP_NAME)
+ {
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+
+ cxx_tokens scope_tokens (1, name_tokens.back ());
+ name_tokens.pop_back (); // ::
+ name_tokens.pop_back (); // name
+ name_tokens.swap (scope_tokens);
+
+ cxx_tokens_lexer l;
+ l.start (scope_tokens);
+
+ tree tn;
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ scope = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p);
+
+ if (scope == 0)
+ return; // Diagnostics has already been issued.
+
+ scope = TYPE_MAIN_VARIANT (TREE_TYPE (scope));
+
+ if (tt != CPP_EOF)
+ {
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+
+ decl_name += "::";
+ }
+ else
+ scope = current_scope ();
+
+ if (virt)
+ {
+ // Should be a single name.
+ //
+ if (name_tokens.size () > 1 || name_tokens.back ().type != CPP_NAME)
+ {
+ location_t l (name_tokens.back ().loc);
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+ string const& name (name_tokens.back ().literal);
+
+ // Parse the remainder of the virtual specifier.
+ //
+ // virtual(<fq-name>)
+ //
+ tree type;
+ string type_name;
+ location_t ord (loc);
+ int ord_bias (0);
+ {
+ string p (tl);
+ location_t loc (l.location ());
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ type = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), type_name, true, p);
+
+ if (type == 0)
+ return; // Diagnostics has already been issued.
+
+ if (TREE_CODE (type) != TYPE_DECL)
+ {
+ error (loc) << "name '" << type_name << "' in db pragma "
+ << p << " does not refer to a type" << endl;
+ return;
+ }
+
+ type = TREE_TYPE (type);
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ // See if we have before/after specifiers.
+ //
+ if (tt == CPP_NAME && tl == "before")
+ {
+ // before[(<member>)]
+ //
+ // Before without the member name means first.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma before"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "before"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias - 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = -1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma before"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = 0;
+ }
+
+ if (tt == CPP_NAME && tl == "after")
+ {
+ // after[(<member>)]
+ //
+ // Before without the member name means last.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma after"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "after"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias + 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = 1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma after"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = ~location_t (0);
+ }
+ }
+
+ pair<virt_declaration_set::const_iterator, bool> r (
+ virt_declarations_[scope].insert (
+ virt_declaration (loc, ord, ord_bias, name, FIELD_DECL, type)));
+
+ if (!r.second)
+ {
+ error (loc) << "virtual data member declaration '" << name
+ << "' conflicts with a previous declaration" << endl;
+
+ info (r.first->loc) << "'" << name << "' was previously "
+ << "declared here" << endl;
+ return;
+ }
+
+ decl_name += name;
+ decl = declaration (*r.first);
+
+ // Mark it as virtual using the standard pragma machinery.
+ //
+ add_pragma (
+ pragma ("virtual", "virtual", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ else
+ {
+ // Not a virtual member declaration.
+ //
+ decl = resolve_data_member (scope, name_tokens, decl_name, p);
+
+ if (!decl)
+ return; // Diagnostics has already been issued.
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+ }
+ }
+ }
+ //
+ // The member qualifier can be omitted so we need to also handle all
+ // the member pragmas here.
+ //
+ else if (p == "id" ||
+ p == "auto" ||
+ p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access" ||
+ p == "column" ||
+ p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column" ||
+ p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options" ||
+ p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type" ||
+ p == "table" ||
+ p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null" ||
+ p == "default" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
+ p == "inverse" ||
+ p == "on_delete" ||
+ p == "points_to" ||
+ p == "unordered" ||
+ p == "readonly" ||
+ p == "transient" ||
+ p == "added" ||
+ p == "deleted" ||
+ p == "version" ||
+ p == "virtual")
+ {
+ handle_pragma (l, db, p, "member", val, declaration (), "", false);
+ return;
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Record this pragma. Delay this until after we process the
+ // specifiers for value (see comment below for the reason).
+ //
+ if (adder == 0)
+ val = orig_decl;
+
+ pragma prag (p,
+ name, // For now no need to translate '_' to '-'.
+ val,
+ loc,
+ &check_qual_decl_type,
+ adder != 0 ? adder : &add_qual_entry);
+
+ tree scope;
+ if (!decl)
+ {
+ scope = current_scope ();
+
+ if (!ns && !CLASS_TYPE_P (scope))
+ scope = global_namespace;
+ }
+
+ any* pval;
+ if (p != "value")
+ {
+ if (decl)
+ pval = &decl_pragmas_[decl].insert (prag).value;
+ else
+ {
+ if (!ns)
+ {
+ pragma_list& pl (loc_pragmas_[scope]);
+ pl.push_back (prag);
+ pval = &pl.back ().value;
+ }
+ else
+ {
+ ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
+ pval = &ns_loc_pragmas_.back ().pragma.value;
+ }
+ }
+ }
+ else
+ pval = &val;
+
+ // See if there are any saved tokens to replay.
+ //
+ if (!saved_tokens.empty ())
+ {
+ cxx_tokens_lexer l;
+ l.start (saved_tokens);
+
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
+
+ if (errorcount != 0) // Avoid parsing the rest if there was an error.
+ return;
+ }
+ else if (tt != CPP_EOF)
+ {
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+ return;
+ }
+ }
+
+ size_t count (0);
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ if (decl)
+ count = decl_pragmas_[decl].size ();
+ else
+ count = loc_pragmas_[scope].size ();
+
+ handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
+ }
+ else if (tt != CPP_EOF)
+ {
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+ return;
+ }
+
+ // Record the value pragma. Here things are complicated by the fact
+ // that we use the value pragma by itself to signify a composite value
+ // type declaration. Consider this example:
+ //
+ // #pragma db value pgsql:type("POINT")
+ // class point {...};
+ //
+ // Should this class be considered composite value type in other
+ // databases (because that's what would happen by default)? Probably
+ // not. So to overcome this we are going to detect and ignore cases
+ // where (a) some specifiers followed the value qualifier but (b)
+ // none of them are for the database that we are compiling for.
+ //
+ if (p == "value")
+ {
+ if (decl)
+ {
+ pragma_set& ps (decl_pragmas_[decl]);
+
+ if (tt == CPP_EOF || ps.size () > count)
+ ps.insert (prag);
+ }
+ else
+ {
+ pragma_list& pl (loc_pragmas_[scope]);
+
+ if (tt == CPP_EOF || pl.size () > count)
+ pl.push_back (prag);
+ }
+ }
+}
+
+/*
+extern "C" void
+handle_pragma_db_mysql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "mysql");
+}
+
+extern "C" void
+handle_pragma_db_sqlite (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "sqlite");
+}
+
+extern "C" void
+handle_pragma_db_pgsql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "pgsql");
+}
+
+extern "C" void
+handle_pragma_db_oracle (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "oracle");
+}
+
+extern "C" void
+handle_pragma_db_mssql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "mssql");
+}
+
+extern "C" void
+handle_pragma_db_model (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "model");
+}
+
+extern "C" void
+handle_pragma_db_map (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "map");
+}
+
+extern "C" void
+handle_pragma_db_namespace (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "namespace");
+}
+
+extern "C" void
+handle_pragma_db_object (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "object");
+}
+
+extern "C" void
+handle_pragma_db_view (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "view");
+}
+
+extern "C" void
+handle_pragma_db_value (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value");
+}
+
+extern "C" void
+handle_pragma_db_member (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "member");
+}
+
+extern "C" void
+handle_pragma_db_id (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id");
+}
+
+extern "C" void
+handle_pragma_db_auto (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "auto");
+}
+
+extern "C" void
+handle_pragma_db_column (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "column");
+}
+
+extern "C" void
+handle_pragma_db_vcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_column");
+}
+
+extern "C" void
+handle_pragma_db_icolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_column");
+}
+
+extern "C" void
+handle_pragma_db_kcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_column");
+}
+
+extern "C" void
+handle_pragma_db_idcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_column");
+}
+
+extern "C" void
+handle_pragma_db_options (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "options");
+}
+
+extern "C" void
+handle_pragma_db_voptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_options");
+}
+
+extern "C" void
+handle_pragma_db_ioptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_options");
+}
+
+extern "C" void
+handle_pragma_db_koptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_options");
+}
+
+extern "C" void
+handle_pragma_db_idoptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_options");
+}
+
+extern "C" void
+handle_pragma_db_type (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "type");
+}
+
+extern "C" void
+handle_pragma_db_id_type (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_type");
+}
+
+extern "C" void
+handle_pragma_db_vtype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_type");
+}
+
+extern "C" void
+handle_pragma_db_itype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_type");
+}
+
+extern "C" void
+handle_pragma_db_ktype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_type");
+}
+
+extern "C" void
+handle_pragma_db_table (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "table");
+}
+
+extern "C" void
+handle_pragma_db_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "null");
+}
+
+extern "C" void
+handle_pragma_db_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "not_null");
+}
+
+extern "C" void
+handle_pragma_db_value_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_null");
+}
+
+extern "C" void
+handle_pragma_db_value_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_not_null");
+}
+
+extern "C" void
+handle_pragma_db_key_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_null");
+}
+
+extern "C" void
+handle_pragma_db_key_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_not_null");
+}
+
+extern "C" void
+handle_pragma_db_default (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "default");
+}
+
+extern "C" void
+handle_pragma_db_section (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "section");
+}
+
+extern "C" void
+handle_pragma_db_load (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "load");
+}
+
+extern "C" void
+handle_pragma_db_update (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "update");
+}
+
+extern "C" void
+handle_pragma_db_inverse (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "inverse");
+}
+
+extern "C" void
+handle_pragma_db_on_delete (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "on_delete");
+}
+
+extern "C" void
+handle_pragma_db_points_to (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "points_to");
+}
+
+extern "C" void
+handle_pragma_db_unordered (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "unordered");
+}
+
+extern "C" void
+handle_pragma_db_readonly (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "readonly");
+}
+
+extern "C" void
+handle_pragma_db_transient (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "transient");
+}
+
+extern "C" void
+handle_pragma_db_added (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "added");
+}
+
+extern "C" void
+handle_pragma_db_deleted (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "deleted");
+}
+
+extern "C" void
+handle_pragma_db_version (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "version");
+}
+
+extern "C" void
+handle_pragma_db_virtual (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "virtual");
+}
+*/
+
+extern "C" void
+handle_pragma_db (cpp_reader*)
+{
+ cxx_pragma_lexer l;
+ l.start ();
+
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after db pragma" << endl;
+ return;
+ }
+
+ handle_pragma_qualifier (l, tl);
+}
+
+extern "C" void
+register_odb_pragmas (void*, void*)
+{
+ // GCC has a limited number of pragma slots and we have exhausted them.
+ // A workaround is to make 'db' a pragma rather than a namespace. This
+ // way we only have one pragma but the drawback of this approach is the
+ // fact that the specifier or qualifier name will now be macro-expanded
+ // (though this happens anyway if we have multiple specifiers in a single
+ // pragma). Once the GCC folks fix this, we can go back to the namespace
+ // approach.
+ //
+ c_register_pragma_with_expansion (0, "db", handle_pragma_db);
+
+ /*
+ c_register_pragma_with_expansion ("db", "mysql", handle_pragma_db_mysql);
+ c_register_pragma_with_expansion ("db", "sqlite", handle_pragma_db_sqlite);
+ c_register_pragma_with_expansion ("db", "pgsql", handle_pragma_db_pgsql);
+ c_register_pragma_with_expansion ("db", "oracle", handle_pragma_db_oracle);
+ c_register_pragma_with_expansion ("db", "mssql", handle_pragma_db_mssql);
+ c_register_pragma_with_expansion ("db", "model", handle_pragma_db_model);
+ c_register_pragma_with_expansion ("db", "map", handle_pragma_db_map);
+ c_register_pragma_with_expansion ("db", "namespace", handle_pragma_db_namespace);
+ c_register_pragma_with_expansion ("db", "object", handle_pragma_db_object);
+ c_register_pragma_with_expansion ("db", "view", handle_pragma_db_view);
+ c_register_pragma_with_expansion ("db", "value", handle_pragma_db_value);
+ c_register_pragma_with_expansion ("db", "member", handle_pragma_db_member);
+ c_register_pragma_with_expansion ("db", "id", handle_pragma_db_id);
+ c_register_pragma_with_expansion ("db", "auto", handle_pragma_db_auto);
+ c_register_pragma_with_expansion ("db", "column", handle_pragma_db_column);
+ c_register_pragma_with_expansion ("db", "value_column", handle_pragma_db_vcolumn);
+ c_register_pragma_with_expansion ("db", "index_column", handle_pragma_db_icolumn);
+ c_register_pragma_with_expansion ("db", "key_column", handle_pragma_db_kcolumn);
+ c_register_pragma_with_expansion ("db", "id_column", handle_pragma_db_idcolumn);
+ c_register_pragma_with_expansion ("db", "options", handle_pragma_db_options);
+ c_register_pragma_with_expansion ("db", "value_options", handle_pragma_db_voptions);
+ c_register_pragma_with_expansion ("db", "index_options", handle_pragma_db_ioptions);
+ c_register_pragma_with_expansion ("db", "key_options", handle_pragma_db_koptions);
+ c_register_pragma_with_expansion ("db", "id_options", handle_pragma_db_idoptions);
+ c_register_pragma_with_expansion ("db", "type", handle_pragma_db_type);
+ c_register_pragma_with_expansion ("db", "id_type", handle_pragma_db_id_type);
+ c_register_pragma_with_expansion ("db", "value_type", handle_pragma_db_vtype);
+ c_register_pragma_with_expansion ("db", "index_type", handle_pragma_db_itype);
+ c_register_pragma_with_expansion ("db", "key_type", handle_pragma_db_ktype);
+ c_register_pragma_with_expansion ("db", "table", handle_pragma_db_table);
+ c_register_pragma_with_expansion ("db", "null", handle_pragma_db_null);
+ c_register_pragma_with_expansion ("db", "not_null", handle_pragma_db_not_null);
+ c_register_pragma_with_expansion ("db", "key_null", handle_pragma_db_key_null);
+ c_register_pragma_with_expansion ("db", "key_not_null", handle_pragma_db_key_not_null);
+ c_register_pragma_with_expansion ("db", "value_null", handle_pragma_db_value_null);
+ c_register_pragma_with_expansion ("db", "value_not_null", handle_pragma_db_value_not_null);
+ c_register_pragma_with_expansion ("db", "default", handle_pragma_db_default);
+ c_register_pragma_with_expansion ("db", "section", handle_pragma_db_section);
+ c_register_pragma_with_expansion ("db", "load", handle_pragma_db_load);
+ c_register_pragma_with_expansion ("db", "update", handle_pragma_db_update);
+ c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
+ c_register_pragma_with_expansion ("db", "on_delete", handle_pragma_db_on_delete);
+ c_register_pragma_with_expansion ("db", "points_to", handle_pragma_db_points_to);
+ c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered);
+ c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly);
+ c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient);
+ c_register_pragma_with_expansion ("db", "added", handle_pragma_db_added);
+ c_register_pragma_with_expansion ("db", "deleted", handle_pragma_db_deleted);
+ c_register_pragma_with_expansion ("db", "version", handle_pragma_db_version);
+ c_register_pragma_with_expansion ("db", "virtual", handle_pragma_db_virtual);
+ */
+}
+
+void
+post_process_pragmas ()
+{
+ // Make sure object, view, and composite class template instantiations
+ // are fully instantiated.
+ //
+ for (decl_pragmas::iterator i (decl_pragmas_.begin ()),
+ e (decl_pragmas_.end ()); i != e; ++i)
+ {
+ if (i->first.virt)
+ continue;
+
+ tree type (i->first.decl.real);
+
+ if (!(CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type)))
+ continue;
+
+ // Check whether this is an object, view, or composite value type.
+ //
+ pragma const* p (0);
+ string diag_name;
+
+ for (pragma_set::iterator j (i->second.begin ()), e (i->second.end ());
+ j != e; ++j)
+ {
+ string const& name (j->second.context_name);
+
+ if (name == "object")
+ {
+ p = &j->second;
+ diag_name = "persistent object";
+ break;
+ }
+ else if (name == "view")
+ {
+ p = &j->second;
+ diag_name = "view";
+ break;
+ }
+ else if (name == "value")
+ {
+ p = &j->second;
+ diag_name = "composite value";
+ break;
+ }
+ // We don't want to instantiate simple values since they may be
+ // incomplete.
+ //
+ else if (name == "simple" || name == "container")
+ {
+ p = 0;
+ break;
+ }
+ }
+
+ if (p == 0)
+ continue;
+
+ // Make sure it is instantiated.
+ //
+ tree decl (TYPE_NAME (p->value.value<tree> ()));
+ location_t loc (real_source_location (decl));
+
+ // Reset input location so that we get nice diagnostics in case
+ // of an error.
+ //
+ input_location = loc;
+
+ if (instantiate_class_template (type) == error_mark_node ||
+ errorcount != 0 ||
+ !COMPLETE_TYPE_P (type))
+ {
+ error (loc) << "unable to instantiate " << diag_name << " class template"
+ << endl;
+ throw pragmas_failed ();
+ }
+ }
+}
diff --git a/odb/odb/pragma.hxx b/odb/odb/pragma.hxx
new file mode 100644
index 0000000..0d4d3f1
--- /dev/null
+++ b/odb/odb/pragma.hxx
@@ -0,0 +1,287 @@
+// file : odb/pragma.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PRAGMA_HXX
+#define ODB_PRAGMA_HXX
+
+#include <odb/gcc.hxx>
+
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+#include <odb/option-types.hxx> // database
+
+#include <libcutl/container/any.hxx>
+#include <libcutl/container/multi-index.hxx>
+#include <libcutl/compiler/context.hxx>
+
+struct virt_declaration
+{
+ virt_declaration (location_t l,
+ location_t o,
+ int ob,
+ std::string const& n,
+ gcc_tree_code_type tc,
+ tree t)
+ : loc (l), ord (o), ord_bias (ob), name (n), tree_code (tc), type (t) {}
+
+ location_t loc;
+ location_t ord; // Ordering location for before/after support.
+ int ord_bias; // Ordering bias for the same locations.
+ std::string name;
+ gcc_tree_code_type tree_code;
+ tree type; // Declaration's type.
+};
+
+// Note that we consider virtual declarations with the same name but
+// different tree codes unequal. If that is too loose, then the
+// inserting code must do additional checks.
+//
+struct virt_declaration_set
+{
+ typedef cutl::container::key<std::string, gcc_tree_code_type> key;
+ typedef std::map<key, virt_declaration> map;
+ typedef cutl::container::map_const_iterator<map> const_iterator;
+
+ std::pair<const_iterator, bool>
+ insert (const virt_declaration& v)
+ {
+ std::pair<map::iterator, bool> r (
+ map_.insert (map::value_type (key (v.name, v.tree_code), v)));
+
+ const_iterator i (r.first);
+
+ if (r.second)
+ r.first->first.assign (i->name, i->tree_code);
+
+ return std::make_pair (i, r.second);
+ }
+
+ const_iterator
+ find (std::string const& name, gcc_tree_code_type tree_code) const
+ {
+ return map_.find (key (name, tree_code));
+ }
+
+ const_iterator begin () const {return map_.begin ();}
+ const_iterator end () const {return map_.end ();}
+
+private:
+ map map_;
+};
+
+// Map of scopes (e.g., class, namespace) to sets of virtual declarations.
+//
+typedef std::map<tree, virt_declaration_set> virt_declarations;
+extern virt_declarations virt_declarations_;
+
+// Real or virtual declaration. If it is real, then it is a pointer to
+// the GCC tree node. Otherwise, it is a pointer to virt_declaration
+// from virt_declarations_ above.
+//
+struct declaration
+{
+ declaration (): virt (false) {decl.real = 0;}
+ declaration (tree d): virt (false) {decl.real = d;}
+ declaration (virt_declaration const& d): virt (true) {decl.virt = &d;}
+
+ bool virt;
+
+ union
+ {
+ tree real;
+ virt_declaration const* virt;
+ } decl;
+
+ gcc_tree_code_type
+ tree_code () const
+ {
+ return (virt ? decl.virt->tree_code : TREE_CODE (decl.real));
+ }
+
+ typedef bool declaration::*bool_convertible;
+ operator bool_convertible () const
+ {
+ return ptr () == 0 ? 0 : &declaration::virt;
+ }
+
+public:
+ bool
+ operator== (declaration const& x) const
+ {
+ return virt == x.virt && ptr () == x.ptr ();
+ }
+
+ bool
+ operator!= (declaration const& x) const
+ {
+ return !(*this == x);
+ }
+
+ bool
+ operator< (declaration const& x) const
+ {
+ return virt < x.virt || (virt == x.virt && ptr () < x.ptr ());
+ }
+
+public:
+ void const*
+ ptr () const
+ {
+ return virt
+ ? static_cast<void const*> (decl.virt)
+ : static_cast<void const*> (decl.real);
+ }
+};
+
+inline bool
+operator== (declaration const& x, tree y)
+{
+ return !x.virt && x.decl.real == y;
+}
+
+inline bool
+operator== (tree x, declaration const& y)
+{
+ return !y.virt && y.decl.real == x;
+}
+
+struct pragma
+{
+ // Check that the pragma is applicable to the declaration. Return true
+ // on success, complain and return false otherwise.
+ //
+ typedef bool (*check_func) (declaration const& decl,
+ std::string const& decl_name,
+ std::string const& prag_name,
+ location_t);
+
+ // Add the pragma value to the context.
+ //
+ typedef void (*add_func) (cutl::compiler::context&,
+ std::string const& key,
+ cutl::container::any const& value,
+ location_t);
+
+ pragma (std::string const& pn,
+ std::string const& cn,
+ cutl::container::any const& v,
+ location_t l,
+ check_func c,
+ add_func a)
+ : pragma_name (pn),
+ context_name (cn),
+ value (v),
+ loc (l),
+ check (c),
+ add (a)
+ {
+ }
+
+ std::string pragma_name; // Actual pragma name for diagnostics.
+ std::string context_name; // Context entry name.
+ cutl::container::any value;
+ location_t loc;
+ check_func check;
+ add_func add;
+};
+
+typedef std::vector<pragma> pragma_list;
+
+// A set of pragmas. Insertion of a pragma with the same name and no
+// custom add function overrides the old value.
+//
+struct pragma_set: std::multimap<std::string, pragma>
+{
+ typedef std::multimap<std::string, pragma> base;
+
+ pragma&
+ insert (pragma const& p)
+ {
+ std::string const& n (p.context_name);
+ std::pair<iterator, iterator> r (equal_range (n));
+
+ iterator i (end ());
+
+ if (p.add == 0)
+ {
+ if (r.first != r.second)
+ {
+ i = r.first;
+ assert (++r.first == r.second);
+
+ i->second = p;
+ }
+ }
+ else if (r.first != r.second)
+ assert ((--r.second)->second.loc <= p.loc);
+
+ if (i == end ())
+ i = base::insert (base::value_type (n, p));
+
+ return i->second;
+ }
+
+ void
+ insert (const_iterator begin, const_iterator end)
+ {
+ for (; begin != end; ++begin)
+ insert (begin->second);
+ }
+
+ // Return the last pragma in the equal range which (by construction) has the
+ // location greater or equal to all the other pragmas in this range.
+ //
+ iterator
+ find (std::string const& n)
+ {
+ return equal_range (n).second;
+ }
+};
+
+
+// Position pragmas inside a class or namespace. The key for the
+// namespace case is the global_namespace node.
+//
+typedef std::map<tree, pragma_list> loc_pragmas;
+extern loc_pragmas loc_pragmas_;
+
+// Position pragmas for namespaces. Because re-opened namespaces do
+// not have any representation in the GCC tree, these are handled in
+// a special way. They are stored as a list of pragmas and their outer
+// namespaces.
+//
+struct ns_loc_pragma
+{
+ typedef ::pragma pragma_type;
+ ns_loc_pragma (tree n, pragma_type const& p): ns (n), pragma (p) {}
+
+ tree ns;
+ pragma_type pragma;
+};
+
+typedef std::vector<ns_loc_pragma> ns_loc_pragmas;
+extern ns_loc_pragmas ns_loc_pragmas_;
+
+// Pragmas associated with specific declarations (real or virtual).
+//
+typedef std::map<declaration, pragma_set> decl_pragmas;
+extern decl_pragmas decl_pragmas_;
+
+// Database we are generating code for as well as multi-database support.
+// Used to ignore database-specific pragmas.
+//
+extern database pragma_db_;
+extern multi_database pragma_multi_;
+
+extern "C" void
+register_odb_pragmas (void*, void*);
+
+struct pragmas_failed {};
+
+void
+post_process_pragmas ();
+
+#endif // ODB_PRAGMA_HXX
diff --git a/odb/odb/pregenerated/odb/options.cxx b/odb/odb/pregenerated/odb/options.cxx
new file mode 100644
index 0000000..da22570
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.cxx
@@ -0,0 +1,4034 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+#include <odb/option-parsers.hxx>
+//
+// End prologue.
+
+#include <odb/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace cli
+{
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ void options::
+ push_back (const option& o)
+ {
+ container_type::size_type n (size ());
+ container_type::push_back (o);
+ map_[o.name ()] = n;
+
+ for (option_names::const_iterator i (o.aliases ().begin ());
+ i != o.aliases ().end (); ++i)
+ map_[*i] = n;
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+}
+
+#include <map>
+
+// options
+//
+
+options::
+options ()
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+}
+
+options::
+options (int& argc,
+ char** argv,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+}
+
+options::
+options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+}
+
+options::
+options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+}
+
+options::
+options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+}
+
+options::
+options (::cli::scanner& s,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ _parse (s, opt, arg);
+}
+
+::cli::usage_para options::
+print_usage (::std::ostream& os, ::cli::usage_para p)
+{
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p == ::cli::usage_para::text)
+ os << ::std::endl;
+
+ os << "--help Print usage information and exit." << ::std::endl;
+
+ os << "--version Print version and exit." << ::std::endl;
+
+ os << "-I <dir> Add <dir> to the beginning of the list of" << ::std::endl
+ << " directories to be searched for included header" << ::std::endl
+ << " files." << ::std::endl;
+
+ os << "-D <name>[=<def>] Define macro <name> with definition <def>." << ::std::endl;
+
+ os << "-U <name> Cancel any previous definitions of macro <name>," << ::std::endl
+ << " either built-in or provided with the -D option." << ::std::endl;
+
+ os << "--database|-d <db> Generate code for the <db> database." << ::std::endl;
+
+ os << "--multi-database|-m <type> Enable multi-database support and specify its" << ::std::endl
+ << " type." << ::std::endl;
+
+ os << "--default-database <db> When static multi-database support is used," << ::std::endl
+ << " specify the database that should be made the" << ::std::endl
+ << " default." << ::std::endl;
+
+ os << "--generate-query|-q Generate query support code." << ::std::endl;
+
+ os << "--generate-prepared Generate prepared query execution support code." << ::std::endl;
+
+ os << "--omit-unprepared Omit un-prepared (once-off) query execution" << ::std::endl
+ << " support code." << ::std::endl;
+
+ os << "--generate-session|-e Generate session support code." << ::std::endl;
+
+ os << "--generate-schema|-s Generate the database schema." << ::std::endl;
+
+ os << "--generate-schema-only Generate only the database schema." << ::std::endl;
+
+ os << "--suppress-migration Suppress the generation of database schema" << ::std::endl
+ << " migration statements." << ::std::endl;
+
+ os << "--suppress-schema-version Suppress the generation of schema version table." << ::std::endl;
+
+ os << "--schema-version-table <name> Specify the alternative schema version table name" << ::std::endl
+ << " instead of the default schema_version." << ::std::endl;
+
+ os << "--schema-format <format> Generate the database schema in the specified" << ::std::endl
+ << " format." << ::std::endl;
+
+ os << "--omit-drop Omit DROP statements from the generated database" << ::std::endl
+ << " schema." << ::std::endl;
+
+ os << "--omit-create Omit CREATE statements from the generated" << ::std::endl
+ << " database schema." << ::std::endl;
+
+ os << "--schema-name <name> Use <name> as the database schema name." << ::std::endl;
+
+ os << "--fkeys-deferrable-mode <m> Use constraint checking mode <m> in foreign keys" << ::std::endl
+ << " generated for object relationships." << ::std::endl;
+
+ os << "--default-pointer <ptr> Use <ptr> as the default pointer for persistent" << ::std::endl
+ << " objects and views." << ::std::endl;
+
+ os << "--session-type <type> Use <type> as the alternative session type" << ::std::endl
+ << " instead of the default odb::session." << ::std::endl;
+
+ os << "--profile|-p <name> Specify a profile that should be used during" << ::std::endl
+ << " compilation." << ::std::endl;
+
+ os << "--at-once Generate code for all the input files as well as" << ::std::endl
+ << " for all the files that they include at once." << ::std::endl;
+
+ os << "--schema <schema> Specify a database schema (database namespace)" << ::std::endl
+ << " that should be assigned to the persistent classes" << ::std::endl
+ << " in the file being compiled." << ::std::endl;
+
+ os << "--export-symbol <symbol> Insert <symbol> in places where DLL export/import" << ::std::endl
+ << " control statements" << ::std::endl
+ << " (__declspec(dllexport/dllimport)) are necessary." << ::std::endl;
+
+ os << "--extern-symbol <symbol> If <symbol> is defined, insert it in places where" << ::std::endl
+ << " a template instantiation must be declared extern." << ::std::endl;
+
+ os << "--std <version> Specify the C++ standard that should be used" << ::std::endl
+ << " during compilation." << ::std::endl;
+
+ os << "--warn-hard-add Warn about hard-added data members." << ::std::endl;
+
+ os << "--warn-hard-delete Warn about hard-deleted data members and" << ::std::endl
+ << " persistent classes." << ::std::endl;
+
+ os << "--warn-hard Warn about both hard-added and hard-deleted data" << ::std::endl
+ << " members and persistent classes." << ::std::endl;
+
+ os << "--output-dir|-o <dir> Write the generated files to <dir> instead of the" << ::std::endl
+ << " current directory." << ::std::endl;
+
+ os << "--input-name <name> Use <name> instead of the input file to derive" << ::std::endl
+ << " the names of the generated files." << ::std::endl;
+
+ os << "--changelog <file> Read/write changelog from/to <file> instead of" << ::std::endl
+ << " the default changelog file." << ::std::endl;
+
+ os << "--changelog-in <file> Read changelog from <file> instead of the default" << ::std::endl
+ << " changelog file." << ::std::endl;
+
+ os << "--changelog-out <file> Write changelog to <file> instead of the default" << ::std::endl
+ << " changelog file." << ::std::endl;
+
+ os << "--changelog-dir <dir> Use <dir> instead of the input file directory as" << ::std::endl
+ << " the changelog file directory." << ::std::endl;
+
+ os << "--init-changelog Force re-initialization of the changelog even if" << ::std::endl
+ << " one exists (all the existing change history will" << ::std::endl
+ << " be lost)." << ::std::endl;
+
+ os << "--odb-file-suffix <suffix> Use <suffix> to construct the names of the" << ::std::endl
+ << " generated C++ files." << ::std::endl;
+
+ os << "--sql-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
+ << " generated schema SQL file." << ::std::endl;
+
+ os << "--schema-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
+ << " generated schema C++ source file." << ::std::endl;
+
+ os << "--changelog-file-suffix <sfx> Use <sfx> to construct the name of the changelog" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--hxx-suffix <suffix> Use <suffix> instead of the default .hxx to" << ::std::endl
+ << " construct the name of the generated C++ header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--ixx-suffix <suffix> Use <suffix> instead of the default .ixx to" << ::std::endl
+ << " construct the name of the generated C++ inline" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--cxx-suffix <suffix> Use <suffix> instead of the default .cxx to" << ::std::endl
+ << " construct the name of the generated C++ source" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--sql-suffix <suffix> Use <suffix> instead of the default .sql to" << ::std::endl
+ << " construct the name of the generated database" << ::std::endl
+ << " schema file." << ::std::endl;
+
+ os << "--changelog-suffix <suffix> Use <suffix> instead of the default .xml to" << ::std::endl
+ << " construct the name of the changelog file." << ::std::endl;
+
+ os << "--hxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ source file." << ::std::endl;
+
+ os << "--schema-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " schema C++ source file." << ::std::endl;
+
+ os << "--sql-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--migration-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " database migration file." << ::std::endl;
+
+ os << "--sql-interlude <text> Insert <text> after all the DROP and before any" << ::std::endl
+ << " CREATE statements in the generated database" << ::std::endl
+ << " schema file." << ::std::endl;
+
+ os << "--hxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " header file." << ::std::endl;
+
+ os << "--ixx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " inline file." << ::std::endl;
+
+ os << "--cxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " source file." << ::std::endl;
+
+ os << "--schema-epilogue <text> Insert <text> at the end of the generated schema" << ::std::endl
+ << " C++ source file." << ::std::endl;
+
+ os << "--sql-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--migration-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
+ << " database migration file." << ::std::endl;
+
+ os << "--hxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ source file." << ::std::endl;
+
+ os << "--schema-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated schema C++ source file." << ::std::endl;
+
+ os << "--sql-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated database schema file." << ::std::endl;
+
+ os << "--migration-prologue-file <f> Insert the content of file <f> at the beginning" << ::std::endl
+ << " of the generated database migration file." << ::std::endl;
+
+ os << "--sql-interlude-file <file> Insert the content of <file> after all the DROP" << ::std::endl
+ << " and before any CREATE statements in the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--hxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ header file." << ::std::endl;
+
+ os << "--ixx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ source file." << ::std::endl;
+
+ os << "--schema-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated schema C++ source file." << ::std::endl;
+
+ os << "--sql-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated database schema file." << ::std::endl;
+
+ os << "--migration-epilogue-file <f> Insert the content of file <f> at the end of the" << ::std::endl
+ << " generated database migration file." << ::std::endl;
+
+ os << "--odb-prologue <text> Compile <text> before the input header file." << ::std::endl;
+
+ os << "--odb-prologue-file <file> Compile <file> contents before the input header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--odb-epilogue <text> Compile <text> after the input header file." << ::std::endl;
+
+ os << "--odb-epilogue-file <file> Compile <file> contents after the input header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--table-prefix <prefix> Add <prefix> to table names and, for databases" << ::std::endl
+ << " that have global index and/or foreign key names," << ::std::endl
+ << " to those names as well." << ::std::endl;
+
+ os << "--index-suffix <suffix> Use <suffix> instead of the default _i to" << ::std::endl
+ << " construct index names." << ::std::endl;
+
+ os << "--fkey-suffix <suffix> Use <suffix> instead of the default _fk to" << ::std::endl
+ << " construct foreign key names." << ::std::endl;
+
+ os << "--sequence-suffix <suffix> Use <suffix> instead of the default _seq to" << ::std::endl
+ << " construct sequence names." << ::std::endl;
+
+ os << "--sql-name-case <case> Convert all automatically-derived SQL names to" << ::std::endl
+ << " upper or lower case." << ::std::endl;
+
+ os << "--table-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " table names." << ::std::endl;
+
+ os << "--column-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " column names." << ::std::endl;
+
+ os << "--index-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " index names." << ::std::endl;
+
+ os << "--fkey-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " foreign key names." << ::std::endl;
+
+ os << "--sequence-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " sequence names." << ::std::endl;
+
+ os << "--statement-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " prepared statement names." << ::std::endl;
+
+ os << "--sql-name-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform all" << ::std::endl
+ << " automatically-derived SQL names." << ::std::endl;
+
+ os << "--sql-name-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the SQL name --*-regex options." << ::std::endl;
+
+ os << "--accessor-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform data member names to function" << ::std::endl
+ << " names when searching for a suitable accessor" << ::std::endl
+ << " function." << ::std::endl;
+
+ os << "--accessor-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --accessor-regex option." << ::std::endl;
+
+ os << "--modifier-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform data member names to function" << ::std::endl
+ << " names when searching for a suitable modifier" << ::std::endl
+ << " function." << ::std::endl;
+
+ os << "--modifier-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --modifier-regex option." << ::std::endl;
+
+ os << "--include-with-brackets Use angle brackets (<>) instead of quotes (\"\") in" << ::std::endl
+ << " the generated #include directives." << ::std::endl;
+
+ os << "--include-prefix <prefix> Add <prefix> to the generated #include directive" << ::std::endl
+ << " paths." << ::std::endl;
+
+ os << "--include-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform generated #include directive" << ::std::endl
+ << " paths." << ::std::endl;
+
+ os << "--include-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --include-regex option." << ::std::endl;
+
+ os << "--guard-prefix <prefix> Add <prefix> to the generated header inclusion" << ::std::endl
+ << " guards." << ::std::endl;
+
+ os << "--show-sloc Print the number of generated physical source" << ::std::endl
+ << " lines of code (SLOC)." << ::std::endl;
+
+ os << "--sloc-limit <num> Check that the number of generated physical" << ::std::endl
+ << " source lines of code (SLOC) does not exceed" << ::std::endl
+ << " <num>." << ::std::endl;
+
+ os << "--options-file <file> Read additional options from <file>." << ::std::endl;
+
+ os << "-x <option> Pass <option> to the underlying C++ compiler" << ::std::endl
+ << " (g++)." << ::std::endl;
+
+ os << "-v Print the commands executed to run the stages of" << ::std::endl
+ << " compilation." << ::std::endl;
+
+ os << "--trace Trace the compilation process." << ::std::endl;
+
+ os << "--mysql-engine <engine> Use <engine> instead of the default InnoDB in the" << ::std::endl
+ << " generated database schema file." << ::std::endl;
+
+ os << "--sqlite-override-null Make all columns in the generated database schema" << ::std::endl
+ << " allow NULL values." << ::std::endl;
+
+ os << "--sqlite-lax-auto-id Do not force monotonically increasing" << ::std::endl
+ << " automatically-assigned object ids." << ::std::endl;
+
+ os << "--pgsql-server-version <ver> Specify the minimum PostgreSQL server version" << ::std::endl
+ << " with which the generated C++ code and schema will" << ::std::endl
+ << " be used." << ::std::endl;
+
+ os << "--oracle-client-version <ver> Specify the minimum Oracle client library (OCI)" << ::std::endl
+ << " version with which the generated C++ code will be" << ::std::endl
+ << " linked." << ::std::endl;
+
+ os << "--oracle-warn-truncation Warn about SQL names that are longer than 30" << ::std::endl
+ << " characters and are therefore truncated." << ::std::endl;
+
+ os << "--mssql-server-version <ver> Specify the minimum SQL Server server version" << ::std::endl
+ << " with which the generated C++ code and schema will" << ::std::endl
+ << " be used." << ::std::endl;
+
+ os << "--mssql-short-limit <size> Specify the short data size limit." << ::std::endl;
+
+ p = ::cli::usage_para::option;
+
+ return p;
+}
+
+struct _cli_options_desc_type: ::cli::options
+{
+ _cli_options_desc_type ()
+ {
+ ::options::fill (*this);
+ }
+};
+
+static _cli_options_desc_type _cli_options_desc_;
+
+void options::
+fill (::cli::options& os)
+{
+ // --build2-metadata
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--build2-metadata", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --help
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--help", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--version", a, true, dv);
+ os.push_back (o);
+ }
+
+ // -I
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-I", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -D
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-D", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -U
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-U", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --database
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-d");
+ std::string dv;
+ ::cli::option o ("--database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --multi-database
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-m");
+ std::string dv;
+ ::cli::option o ("--multi-database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --default-database
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--default-database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --generate-query
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-q");
+ std::string dv;
+ ::cli::option o ("--generate-query", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-prepared
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--generate-prepared", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --omit-unprepared
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-unprepared", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-session
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-e");
+ std::string dv;
+ ::cli::option o ("--generate-session", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-schema
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-s");
+ std::string dv;
+ ::cli::option o ("--generate-schema", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-schema-only
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--generate-schema-only", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --suppress-migration
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--suppress-migration", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --suppress-schema-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--suppress-schema-version", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema-version-table
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-version-table", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-format
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-format", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --omit-drop
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-drop", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --omit-create
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-create", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema-name
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-name", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkeys-deferrable-mode
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkeys-deferrable-mode", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --default-pointer
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("*");
+ ::cli::option o ("--default-pointer", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --session-type
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("odb::session");
+ ::cli::option o ("--session-type", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --profile
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-p");
+ std::string dv;
+ ::cli::option o ("--profile", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --at-once
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--at-once", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --export-symbol
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--export-symbol", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --extern-symbol
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--extern-symbol", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --std
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--std", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard-add
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard-add", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard-delete
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard-delete", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --output-dir
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-o");
+ std::string dv;
+ ::cli::option o ("--output-dir", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --input-name
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--input-name", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-in
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-in", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-out
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-out", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-dir
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-dir", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --init-changelog
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--init-changelog", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --odb-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".hxx");
+ ::cli::option o ("--hxx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".ixx");
+ ::cli::option o ("--ixx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".cxx");
+ ::cli::option o ("--cxx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".sql");
+ ::cli::option o ("--sql-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".xml");
+ ::cli::option o ("--changelog-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-interlude
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-interlude", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-interlude-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-interlude-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --table-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--table-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --index-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--index-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkey-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkey-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sequence-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sequence-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-case
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-case", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --table-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--table-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --column-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--column-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --index-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--index-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkey-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkey-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sequence-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sequence-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --statement-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--statement-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --accessor-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--accessor-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --accessor-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--accessor-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --modifier-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--modifier-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --modifier-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--modifier-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --include-with-brackets
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-with-brackets", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --include-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --include-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --include-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --guard-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--guard-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --show-sloc
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--show-sloc", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --sloc-limit
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sloc-limit", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --options-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--options-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -x
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-x", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -v
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-v", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --mysql-engine
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("InnoDB");
+ ::cli::option o ("--mysql-engine", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sqlite-override-null
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sqlite-override-null", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --sqlite-lax-auto-id
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sqlite-lax-auto-id", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --pgsql-server-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--pgsql-server-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --oracle-client-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--oracle-client-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --oracle-warn-truncation
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--oracle-warn-truncation", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --mssql-server-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--mssql-server-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --mssql-short-limit
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("1024");
+ ::cli::option o ("--mssql-short-limit", a, false, dv);
+ os.push_back (o);
+ }
+}
+
+const ::cli::options& options::
+description ()
+{
+ return _cli_options_desc_;
+}
+
+typedef
+std::map<std::string, void (*) (options&, ::cli::scanner&)>
+_cli_options_map;
+
+static _cli_options_map _cli_options_map_;
+
+struct _cli_options_map_init
+{
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--build2-metadata"] =
+ &::cli::thunk< options, std::uint64_t, &options::build2_metadata_,
+ &options::build2_metadata_specified_ >;
+ _cli_options_map_["--help"] =
+ &::cli::thunk< options, &options::help_ >;
+ _cli_options_map_["--version"] =
+ &::cli::thunk< options, &options::version_ >;
+ _cli_options_map_["-I"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::I_,
+ &options::I_specified_ >;
+ _cli_options_map_["-D"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::D_,
+ &options::D_specified_ >;
+ _cli_options_map_["-U"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::U_,
+ &options::U_specified_ >;
+ _cli_options_map_["--database"] =
+ &::cli::thunk< options, std::vector< ::database>, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["-d"] =
+ &::cli::thunk< options, std::vector< ::database>, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--multi-database"] =
+ &::cli::thunk< options, ::multi_database, &options::multi_database_,
+ &options::multi_database_specified_ >;
+ _cli_options_map_["-m"] =
+ &::cli::thunk< options, ::multi_database, &options::multi_database_,
+ &options::multi_database_specified_ >;
+ _cli_options_map_["--default-database"] =
+ &::cli::thunk< options, ::database, &options::default_database_,
+ &options::default_database_specified_ >;
+ _cli_options_map_["--generate-query"] =
+ &::cli::thunk< options, &options::generate_query_ >;
+ _cli_options_map_["-q"] =
+ &::cli::thunk< options, &options::generate_query_ >;
+ _cli_options_map_["--generate-prepared"] =
+ &::cli::thunk< options, &options::generate_prepared_ >;
+ _cli_options_map_["--omit-unprepared"] =
+ &::cli::thunk< options, &options::omit_unprepared_ >;
+ _cli_options_map_["--generate-session"] =
+ &::cli::thunk< options, &options::generate_session_ >;
+ _cli_options_map_["-e"] =
+ &::cli::thunk< options, &options::generate_session_ >;
+ _cli_options_map_["--generate-schema"] =
+ &::cli::thunk< options, &options::generate_schema_ >;
+ _cli_options_map_["-s"] =
+ &::cli::thunk< options, &options::generate_schema_ >;
+ _cli_options_map_["--generate-schema-only"] =
+ &::cli::thunk< options, &options::generate_schema_only_ >;
+ _cli_options_map_["--suppress-migration"] =
+ &::cli::thunk< options, &options::suppress_migration_ >;
+ _cli_options_map_["--suppress-schema-version"] =
+ &::cli::thunk< options, &options::suppress_schema_version_ >;
+ _cli_options_map_["--schema-version-table"] =
+ &::cli::thunk< options, database_map<qname>, &options::schema_version_table_,
+ &options::schema_version_table_specified_ >;
+ _cli_options_map_["--schema-format"] =
+ &::cli::thunk< options, database_map<std::set< ::schema_format> >, &options::schema_format_,
+ &options::schema_format_specified_ >;
+ _cli_options_map_["--omit-drop"] =
+ &::cli::thunk< options, &options::omit_drop_ >;
+ _cli_options_map_["--omit-create"] =
+ &::cli::thunk< options, &options::omit_create_ >;
+ _cli_options_map_["--schema-name"] =
+ &::cli::thunk< options, database_map<std::string>, &options::schema_name_,
+ &options::schema_name_specified_ >;
+ _cli_options_map_["--fkeys-deferrable-mode"] =
+ &::cli::thunk< options, database_map<deferrable>, &options::fkeys_deferrable_mode_,
+ &options::fkeys_deferrable_mode_specified_ >;
+ _cli_options_map_["--default-pointer"] =
+ &::cli::thunk< options, std::string, &options::default_pointer_,
+ &options::default_pointer_specified_ >;
+ _cli_options_map_["--session-type"] =
+ &::cli::thunk< options, std::string, &options::session_type_,
+ &options::session_type_specified_ >;
+ _cli_options_map_["--profile"] =
+ &::cli::thunk< options, std::string, &options::profile_,
+ &options::profile_specified_ >;
+ _cli_options_map_["-p"] =
+ &::cli::thunk< options, std::string, &options::profile_,
+ &options::profile_specified_ >;
+ _cli_options_map_["--at-once"] =
+ &::cli::thunk< options, &options::at_once_ >;
+ _cli_options_map_["--schema"] =
+ &::cli::thunk< options, database_map<qname>, &options::schema_,
+ &options::schema_specified_ >;
+ _cli_options_map_["--export-symbol"] =
+ &::cli::thunk< options, database_map<std::string>, &options::export_symbol_,
+ &options::export_symbol_specified_ >;
+ _cli_options_map_["--extern-symbol"] =
+ &::cli::thunk< options, database_map<std::string>, &options::extern_symbol_,
+ &options::extern_symbol_specified_ >;
+ _cli_options_map_["--std"] =
+ &::cli::thunk< options, cxx_version, &options::std_,
+ &options::std_specified_ >;
+ _cli_options_map_["--warn-hard-add"] =
+ &::cli::thunk< options, &options::warn_hard_add_ >;
+ _cli_options_map_["--warn-hard-delete"] =
+ &::cli::thunk< options, &options::warn_hard_delete_ >;
+ _cli_options_map_["--warn-hard"] =
+ &::cli::thunk< options, &options::warn_hard_ >;
+ _cli_options_map_["--output-dir"] =
+ &::cli::thunk< options, std::string, &options::output_dir_,
+ &options::output_dir_specified_ >;
+ _cli_options_map_["-o"] =
+ &::cli::thunk< options, std::string, &options::output_dir_,
+ &options::output_dir_specified_ >;
+ _cli_options_map_["--input-name"] =
+ &::cli::thunk< options, std::string, &options::input_name_,
+ &options::input_name_specified_ >;
+ _cli_options_map_["--changelog"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_,
+ &options::changelog_specified_ >;
+ _cli_options_map_["--changelog-in"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_in_,
+ &options::changelog_in_specified_ >;
+ _cli_options_map_["--changelog-out"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_out_,
+ &options::changelog_out_specified_ >;
+ _cli_options_map_["--changelog-dir"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_dir_,
+ &options::changelog_dir_specified_ >;
+ _cli_options_map_["--init-changelog"] =
+ &::cli::thunk< options, &options::init_changelog_ >;
+ _cli_options_map_["--odb-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::odb_file_suffix_,
+ &options::odb_file_suffix_specified_ >;
+ _cli_options_map_["--sql-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::sql_file_suffix_,
+ &options::sql_file_suffix_specified_ >;
+ _cli_options_map_["--schema-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::schema_file_suffix_,
+ &options::schema_file_suffix_specified_ >;
+ _cli_options_map_["--changelog-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_file_suffix_,
+ &options::changelog_file_suffix_specified_ >;
+ _cli_options_map_["--hxx-suffix"] =
+ &::cli::thunk< options, std::string, &options::hxx_suffix_,
+ &options::hxx_suffix_specified_ >;
+ _cli_options_map_["--ixx-suffix"] =
+ &::cli::thunk< options, std::string, &options::ixx_suffix_,
+ &options::ixx_suffix_specified_ >;
+ _cli_options_map_["--cxx-suffix"] =
+ &::cli::thunk< options, std::string, &options::cxx_suffix_,
+ &options::cxx_suffix_specified_ >;
+ _cli_options_map_["--sql-suffix"] =
+ &::cli::thunk< options, std::string, &options::sql_suffix_,
+ &options::sql_suffix_specified_ >;
+ _cli_options_map_["--changelog-suffix"] =
+ &::cli::thunk< options, std::string, &options::changelog_suffix_,
+ &options::changelog_suffix_specified_ >;
+ _cli_options_map_["--hxx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_,
+ &options::hxx_prologue_specified_ >;
+ _cli_options_map_["--ixx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_,
+ &options::ixx_prologue_specified_ >;
+ _cli_options_map_["--cxx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_,
+ &options::cxx_prologue_specified_ >;
+ _cli_options_map_["--schema-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_,
+ &options::schema_prologue_specified_ >;
+ _cli_options_map_["--sql-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_,
+ &options::sql_prologue_specified_ >;
+ _cli_options_map_["--migration-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_,
+ &options::migration_prologue_specified_ >;
+ _cli_options_map_["--sql-interlude"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_,
+ &options::sql_interlude_specified_ >;
+ _cli_options_map_["--hxx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_,
+ &options::hxx_epilogue_specified_ >;
+ _cli_options_map_["--ixx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_,
+ &options::ixx_epilogue_specified_ >;
+ _cli_options_map_["--cxx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_,
+ &options::cxx_epilogue_specified_ >;
+ _cli_options_map_["--schema-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_,
+ &options::schema_epilogue_specified_ >;
+ _cli_options_map_["--sql-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_,
+ &options::sql_epilogue_specified_ >;
+ _cli_options_map_["--migration-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_,
+ &options::migration_epilogue_specified_ >;
+ _cli_options_map_["--hxx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_file_,
+ &options::hxx_prologue_file_specified_ >;
+ _cli_options_map_["--ixx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_file_,
+ &options::ixx_prologue_file_specified_ >;
+ _cli_options_map_["--cxx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_file_,
+ &options::cxx_prologue_file_specified_ >;
+ _cli_options_map_["--schema-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_file_,
+ &options::schema_prologue_file_specified_ >;
+ _cli_options_map_["--sql-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_file_,
+ &options::sql_prologue_file_specified_ >;
+ _cli_options_map_["--migration-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_file_,
+ &options::migration_prologue_file_specified_ >;
+ _cli_options_map_["--sql-interlude-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_file_,
+ &options::sql_interlude_file_specified_ >;
+ _cli_options_map_["--hxx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_file_,
+ &options::hxx_epilogue_file_specified_ >;
+ _cli_options_map_["--ixx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_file_,
+ &options::ixx_epilogue_file_specified_ >;
+ _cli_options_map_["--cxx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_file_,
+ &options::cxx_epilogue_file_specified_ >;
+ _cli_options_map_["--schema-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_file_,
+ &options::schema_epilogue_file_specified_ >;
+ _cli_options_map_["--sql-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_file_,
+ &options::sql_epilogue_file_specified_ >;
+ _cli_options_map_["--migration-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_file_,
+ &options::migration_epilogue_file_specified_ >;
+ _cli_options_map_["--odb-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_,
+ &options::odb_prologue_specified_ >;
+ _cli_options_map_["--odb-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_file_,
+ &options::odb_prologue_file_specified_ >;
+ _cli_options_map_["--odb-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_,
+ &options::odb_epilogue_specified_ >;
+ _cli_options_map_["--odb-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_file_,
+ &options::odb_epilogue_file_specified_ >;
+ _cli_options_map_["--table-prefix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::table_prefix_,
+ &options::table_prefix_specified_ >;
+ _cli_options_map_["--index-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::index_suffix_,
+ &options::index_suffix_specified_ >;
+ _cli_options_map_["--fkey-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::fkey_suffix_,
+ &options::fkey_suffix_specified_ >;
+ _cli_options_map_["--sequence-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::sequence_suffix_,
+ &options::sequence_suffix_specified_ >;
+ _cli_options_map_["--sql-name-case"] =
+ &::cli::thunk< options, database_map<name_case>, &options::sql_name_case_,
+ &options::sql_name_case_specified_ >;
+ _cli_options_map_["--table-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::table_regex_,
+ &options::table_regex_specified_ >;
+ _cli_options_map_["--column-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::column_regex_,
+ &options::column_regex_specified_ >;
+ _cli_options_map_["--index-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::index_regex_,
+ &options::index_regex_specified_ >;
+ _cli_options_map_["--fkey-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::fkey_regex_,
+ &options::fkey_regex_specified_ >;
+ _cli_options_map_["--sequence-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sequence_regex_,
+ &options::sequence_regex_specified_ >;
+ _cli_options_map_["--statement-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::statement_regex_,
+ &options::statement_regex_specified_ >;
+ _cli_options_map_["--sql-name-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_name_regex_,
+ &options::sql_name_regex_specified_ >;
+ _cli_options_map_["--sql-name-regex-trace"] =
+ &::cli::thunk< options, &options::sql_name_regex_trace_ >;
+ _cli_options_map_["--accessor-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::accessor_regex_,
+ &options::accessor_regex_specified_ >;
+ _cli_options_map_["--accessor-regex-trace"] =
+ &::cli::thunk< options, &options::accessor_regex_trace_ >;
+ _cli_options_map_["--modifier-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::modifier_regex_,
+ &options::modifier_regex_specified_ >;
+ _cli_options_map_["--modifier-regex-trace"] =
+ &::cli::thunk< options, &options::modifier_regex_trace_ >;
+ _cli_options_map_["--include-with-brackets"] =
+ &::cli::thunk< options, &options::include_with_brackets_ >;
+ _cli_options_map_["--include-prefix"] =
+ &::cli::thunk< options, std::string, &options::include_prefix_,
+ &options::include_prefix_specified_ >;
+ _cli_options_map_["--include-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::include_regex_,
+ &options::include_regex_specified_ >;
+ _cli_options_map_["--include-regex-trace"] =
+ &::cli::thunk< options, &options::include_regex_trace_ >;
+ _cli_options_map_["--guard-prefix"] =
+ &::cli::thunk< options, std::string, &options::guard_prefix_,
+ &options::guard_prefix_specified_ >;
+ _cli_options_map_["--show-sloc"] =
+ &::cli::thunk< options, &options::show_sloc_ >;
+ _cli_options_map_["--sloc-limit"] =
+ &::cli::thunk< options, std::size_t, &options::sloc_limit_,
+ &options::sloc_limit_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ _cli_options_map_["-x"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::x_,
+ &options::x_specified_ >;
+ _cli_options_map_["-v"] =
+ &::cli::thunk< options, &options::v_ >;
+ _cli_options_map_["--trace"] =
+ &::cli::thunk< options, &options::trace_ >;
+ _cli_options_map_["--mysql-engine"] =
+ &::cli::thunk< options, std::string, &options::mysql_engine_,
+ &options::mysql_engine_specified_ >;
+ _cli_options_map_["--sqlite-override-null"] =
+ &::cli::thunk< options, &options::sqlite_override_null_ >;
+ _cli_options_map_["--sqlite-lax-auto-id"] =
+ &::cli::thunk< options, &options::sqlite_lax_auto_id_ >;
+ _cli_options_map_["--pgsql-server-version"] =
+ &::cli::thunk< options, ::pgsql_version, &options::pgsql_server_version_,
+ &options::pgsql_server_version_specified_ >;
+ _cli_options_map_["--oracle-client-version"] =
+ &::cli::thunk< options, ::oracle_version, &options::oracle_client_version_,
+ &options::oracle_client_version_specified_ >;
+ _cli_options_map_["--oracle-warn-truncation"] =
+ &::cli::thunk< options, &options::oracle_warn_truncation_ >;
+ _cli_options_map_["--mssql-server-version"] =
+ &::cli::thunk< options, ::mssql_version, &options::mssql_server_version_,
+ &options::mssql_server_version_specified_ >;
+ _cli_options_map_["--mssql-short-limit"] =
+ &::cli::thunk< options, unsigned int, &options::mssql_short_limit_,
+ &options::mssql_short_limit_specified_ >;
+ }
+};
+
+static _cli_options_map_init _cli_options_map_init_;
+
+bool options::
+_parse (const char* o, ::cli::scanner& s)
+{
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+}
+
+bool options::
+_parse (::cli::scanner& s,
+ ::cli::unknown_mode opt_mode,
+ ::cli::unknown_mode arg_mode)
+{
+ // Can't skip combined flags (--no-combined-flags).
+ //
+ assert (opt_mode != ::cli::unknown_mode::skip);
+
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ // Handle combined flags.
+ //
+ char cf[3];
+ {
+ const char* p = o + 1;
+ for (; *p != '\0'; ++p)
+ {
+ if (!((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9')))
+ break;
+ }
+
+ if (*p == '\0')
+ {
+ for (p = o + 1; *p != '\0'; ++p)
+ {
+ std::strcpy (cf, "-");
+ cf[1] = *p;
+ cf[2] = '\0';
+
+ int ac (1);
+ char* av[] =
+ {
+ cf
+ };
+
+ ::cli::argv_scanner ns (0, ac, av);
+
+ if (!_parse (cf, ns))
+ break;
+ }
+
+ if (*p == '\0')
+ {
+ // All handled.
+ //
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = cf;
+ }
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::cli::unknown_mode::fail:
+ {
+ throw ::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::cli::unknown_mode::fail:
+ {
+ throw ::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/odb/odb/pregenerated/odb/options.hxx b/odb/odb/pregenerated/odb/options.hxx
new file mode 100644
index 0000000..74406a0
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.hxx
@@ -0,0 +1,2340 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef ODB_OPTIONS_HXX
+#define ODB_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <map>
+#include <vector>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace cli
+{
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ typedef std::vector<std::string> option_names;
+
+ class option
+ {
+ public:
+
+ const std::string&
+ name () const;
+
+ const option_names&
+ aliases () const;
+
+ bool
+ flag () const;
+
+ const std::string&
+ default_value () const;
+
+ public:option ();
+ option (const std::string& name,
+ const option_names& aliases,
+ bool flag,
+ const std::string& default_value);
+
+ private:
+ std::string name_;
+ option_names aliases_;
+ bool flag_;
+ std::string default_value_;
+ };
+
+ class options: public std::vector<option>
+ {
+ public:
+ typedef std::vector<option> container_type;
+
+ container_type::const_iterator
+ find (const std::string& name) const;
+
+ void
+ push_back (const option&);
+ private:
+ typedef std::map<std::string, container_type::size_type> map_type;
+ map_type map_;
+ };
+
+ template <typename X>
+ struct parser;
+}
+
+#include <set>
+
+#include <vector>
+
+#include <string>
+
+#include <cstddef>
+
+#include <cstdint>
+
+#include <odb/option-types.hxx>
+
+class options
+{
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (::cli::scanner&,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ // Option accessors and modifiers.
+ //
+ const std::uint64_t&
+ build2_metadata () const;
+
+ std::uint64_t&
+ build2_metadata ();
+
+ void
+ build2_metadata (const std::uint64_t&);
+
+ bool
+ build2_metadata_specified () const;
+
+ void
+ build2_metadata_specified (bool);
+
+ const bool&
+ help () const;
+
+ bool&
+ help ();
+
+ void
+ help (const bool&);
+
+ const bool&
+ version () const;
+
+ bool&
+ version ();
+
+ void
+ version (const bool&);
+
+ const std::vector<std::string>&
+ I () const;
+
+ std::vector<std::string>&
+ I ();
+
+ void
+ I (const std::vector<std::string>&);
+
+ bool
+ I_specified () const;
+
+ void
+ I_specified (bool);
+
+ const std::vector<std::string>&
+ D () const;
+
+ std::vector<std::string>&
+ D ();
+
+ void
+ D (const std::vector<std::string>&);
+
+ bool
+ D_specified () const;
+
+ void
+ D_specified (bool);
+
+ const std::vector<std::string>&
+ U () const;
+
+ std::vector<std::string>&
+ U ();
+
+ void
+ U (const std::vector<std::string>&);
+
+ bool
+ U_specified () const;
+
+ void
+ U_specified (bool);
+
+ const std::vector< ::database>&
+ database () const;
+
+ std::vector< ::database>&
+ database ();
+
+ void
+ database (const std::vector< ::database>&);
+
+ bool
+ database_specified () const;
+
+ void
+ database_specified (bool);
+
+ const ::multi_database&
+ multi_database () const;
+
+ ::multi_database&
+ multi_database ();
+
+ void
+ multi_database (const ::multi_database&);
+
+ bool
+ multi_database_specified () const;
+
+ void
+ multi_database_specified (bool);
+
+ const ::database&
+ default_database () const;
+
+ ::database&
+ default_database ();
+
+ void
+ default_database (const ::database&);
+
+ bool
+ default_database_specified () const;
+
+ void
+ default_database_specified (bool);
+
+ const bool&
+ generate_query () const;
+
+ bool&
+ generate_query ();
+
+ void
+ generate_query (const bool&);
+
+ const bool&
+ generate_prepared () const;
+
+ bool&
+ generate_prepared ();
+
+ void
+ generate_prepared (const bool&);
+
+ const bool&
+ omit_unprepared () const;
+
+ bool&
+ omit_unprepared ();
+
+ void
+ omit_unprepared (const bool&);
+
+ const bool&
+ generate_session () const;
+
+ bool&
+ generate_session ();
+
+ void
+ generate_session (const bool&);
+
+ const bool&
+ generate_schema () const;
+
+ bool&
+ generate_schema ();
+
+ void
+ generate_schema (const bool&);
+
+ const bool&
+ generate_schema_only () const;
+
+ bool&
+ generate_schema_only ();
+
+ void
+ generate_schema_only (const bool&);
+
+ const bool&
+ suppress_migration () const;
+
+ bool&
+ suppress_migration ();
+
+ void
+ suppress_migration (const bool&);
+
+ const bool&
+ suppress_schema_version () const;
+
+ bool&
+ suppress_schema_version ();
+
+ void
+ suppress_schema_version (const bool&);
+
+ const database_map<qname>&
+ schema_version_table () const;
+
+ database_map<qname>&
+ schema_version_table ();
+
+ void
+ schema_version_table (const database_map<qname>&);
+
+ bool
+ schema_version_table_specified () const;
+
+ void
+ schema_version_table_specified (bool);
+
+ const database_map<std::set< ::schema_format> >&
+ schema_format () const;
+
+ database_map<std::set< ::schema_format> >&
+ schema_format ();
+
+ void
+ schema_format (const database_map<std::set< ::schema_format> >&);
+
+ bool
+ schema_format_specified () const;
+
+ void
+ schema_format_specified (bool);
+
+ const bool&
+ omit_drop () const;
+
+ bool&
+ omit_drop ();
+
+ void
+ omit_drop (const bool&);
+
+ const bool&
+ omit_create () const;
+
+ bool&
+ omit_create ();
+
+ void
+ omit_create (const bool&);
+
+ const database_map<std::string>&
+ schema_name () const;
+
+ database_map<std::string>&
+ schema_name ();
+
+ void
+ schema_name (const database_map<std::string>&);
+
+ bool
+ schema_name_specified () const;
+
+ void
+ schema_name_specified (bool);
+
+ const database_map<deferrable>&
+ fkeys_deferrable_mode () const;
+
+ database_map<deferrable>&
+ fkeys_deferrable_mode ();
+
+ void
+ fkeys_deferrable_mode (const database_map<deferrable>&);
+
+ bool
+ fkeys_deferrable_mode_specified () const;
+
+ void
+ fkeys_deferrable_mode_specified (bool);
+
+ const std::string&
+ default_pointer () const;
+
+ std::string&
+ default_pointer ();
+
+ void
+ default_pointer (const std::string&);
+
+ bool
+ default_pointer_specified () const;
+
+ void
+ default_pointer_specified (bool);
+
+ const std::string&
+ session_type () const;
+
+ std::string&
+ session_type ();
+
+ void
+ session_type (const std::string&);
+
+ bool
+ session_type_specified () const;
+
+ void
+ session_type_specified (bool);
+
+ const std::string&
+ profile () const;
+
+ std::string&
+ profile ();
+
+ void
+ profile (const std::string&);
+
+ bool
+ profile_specified () const;
+
+ void
+ profile_specified (bool);
+
+ const bool&
+ at_once () const;
+
+ bool&
+ at_once ();
+
+ void
+ at_once (const bool&);
+
+ const database_map<qname>&
+ schema () const;
+
+ database_map<qname>&
+ schema ();
+
+ void
+ schema (const database_map<qname>&);
+
+ bool
+ schema_specified () const;
+
+ void
+ schema_specified (bool);
+
+ const database_map<std::string>&
+ export_symbol () const;
+
+ database_map<std::string>&
+ export_symbol ();
+
+ void
+ export_symbol (const database_map<std::string>&);
+
+ bool
+ export_symbol_specified () const;
+
+ void
+ export_symbol_specified (bool);
+
+ const database_map<std::string>&
+ extern_symbol () const;
+
+ database_map<std::string>&
+ extern_symbol ();
+
+ void
+ extern_symbol (const database_map<std::string>&);
+
+ bool
+ extern_symbol_specified () const;
+
+ void
+ extern_symbol_specified (bool);
+
+ const cxx_version&
+ std () const;
+
+ cxx_version&
+ std ();
+
+ void
+ std (const cxx_version&);
+
+ bool
+ std_specified () const;
+
+ void
+ std_specified (bool);
+
+ const bool&
+ warn_hard_add () const;
+
+ bool&
+ warn_hard_add ();
+
+ void
+ warn_hard_add (const bool&);
+
+ const bool&
+ warn_hard_delete () const;
+
+ bool&
+ warn_hard_delete ();
+
+ void
+ warn_hard_delete (const bool&);
+
+ const bool&
+ warn_hard () const;
+
+ bool&
+ warn_hard ();
+
+ void
+ warn_hard (const bool&);
+
+ const std::string&
+ output_dir () const;
+
+ std::string&
+ output_dir ();
+
+ void
+ output_dir (const std::string&);
+
+ bool
+ output_dir_specified () const;
+
+ void
+ output_dir_specified (bool);
+
+ const std::string&
+ input_name () const;
+
+ std::string&
+ input_name ();
+
+ void
+ input_name (const std::string&);
+
+ bool
+ input_name_specified () const;
+
+ void
+ input_name_specified (bool);
+
+ const database_map<std::string>&
+ changelog () const;
+
+ database_map<std::string>&
+ changelog ();
+
+ void
+ changelog (const database_map<std::string>&);
+
+ bool
+ changelog_specified () const;
+
+ void
+ changelog_specified (bool);
+
+ const database_map<std::string>&
+ changelog_in () const;
+
+ database_map<std::string>&
+ changelog_in ();
+
+ void
+ changelog_in (const database_map<std::string>&);
+
+ bool
+ changelog_in_specified () const;
+
+ void
+ changelog_in_specified (bool);
+
+ const database_map<std::string>&
+ changelog_out () const;
+
+ database_map<std::string>&
+ changelog_out ();
+
+ void
+ changelog_out (const database_map<std::string>&);
+
+ bool
+ changelog_out_specified () const;
+
+ void
+ changelog_out_specified (bool);
+
+ const database_map<std::string>&
+ changelog_dir () const;
+
+ database_map<std::string>&
+ changelog_dir ();
+
+ void
+ changelog_dir (const database_map<std::string>&);
+
+ bool
+ changelog_dir_specified () const;
+
+ void
+ changelog_dir_specified (bool);
+
+ const bool&
+ init_changelog () const;
+
+ bool&
+ init_changelog ();
+
+ void
+ init_changelog (const bool&);
+
+ const database_map<std::string>&
+ odb_file_suffix () const;
+
+ database_map<std::string>&
+ odb_file_suffix ();
+
+ void
+ odb_file_suffix (const database_map<std::string>&);
+
+ bool
+ odb_file_suffix_specified () const;
+
+ void
+ odb_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ sql_file_suffix () const;
+
+ database_map<std::string>&
+ sql_file_suffix ();
+
+ void
+ sql_file_suffix (const database_map<std::string>&);
+
+ bool
+ sql_file_suffix_specified () const;
+
+ void
+ sql_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ schema_file_suffix () const;
+
+ database_map<std::string>&
+ schema_file_suffix ();
+
+ void
+ schema_file_suffix (const database_map<std::string>&);
+
+ bool
+ schema_file_suffix_specified () const;
+
+ void
+ schema_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ changelog_file_suffix () const;
+
+ database_map<std::string>&
+ changelog_file_suffix ();
+
+ void
+ changelog_file_suffix (const database_map<std::string>&);
+
+ bool
+ changelog_file_suffix_specified () const;
+
+ void
+ changelog_file_suffix_specified (bool);
+
+ const std::string&
+ hxx_suffix () const;
+
+ std::string&
+ hxx_suffix ();
+
+ void
+ hxx_suffix (const std::string&);
+
+ bool
+ hxx_suffix_specified () const;
+
+ void
+ hxx_suffix_specified (bool);
+
+ const std::string&
+ ixx_suffix () const;
+
+ std::string&
+ ixx_suffix ();
+
+ void
+ ixx_suffix (const std::string&);
+
+ bool
+ ixx_suffix_specified () const;
+
+ void
+ ixx_suffix_specified (bool);
+
+ const std::string&
+ cxx_suffix () const;
+
+ std::string&
+ cxx_suffix ();
+
+ void
+ cxx_suffix (const std::string&);
+
+ bool
+ cxx_suffix_specified () const;
+
+ void
+ cxx_suffix_specified (bool);
+
+ const std::string&
+ sql_suffix () const;
+
+ std::string&
+ sql_suffix ();
+
+ void
+ sql_suffix (const std::string&);
+
+ bool
+ sql_suffix_specified () const;
+
+ void
+ sql_suffix_specified (bool);
+
+ const std::string&
+ changelog_suffix () const;
+
+ std::string&
+ changelog_suffix ();
+
+ void
+ changelog_suffix (const std::string&);
+
+ bool
+ changelog_suffix_specified () const;
+
+ void
+ changelog_suffix_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_prologue ();
+
+ void
+ hxx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_prologue_specified () const;
+
+ void
+ hxx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_prologue ();
+
+ void
+ ixx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_prologue_specified () const;
+
+ void
+ ixx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_prologue ();
+
+ void
+ cxx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_prologue_specified () const;
+
+ void
+ cxx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ schema_prologue ();
+
+ void
+ schema_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_prologue_specified () const;
+
+ void
+ schema_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ sql_prologue ();
+
+ void
+ sql_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_prologue_specified () const;
+
+ void
+ sql_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ migration_prologue ();
+
+ void
+ migration_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_prologue_specified () const;
+
+ void
+ migration_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_interlude () const;
+
+ database_map<std::vector<std::string> >&
+ sql_interlude ();
+
+ void
+ sql_interlude (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_interlude_specified () const;
+
+ void
+ sql_interlude_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_epilogue ();
+
+ void
+ hxx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_epilogue_specified () const;
+
+ void
+ hxx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_epilogue ();
+
+ void
+ ixx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_epilogue_specified () const;
+
+ void
+ ixx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_epilogue ();
+
+ void
+ cxx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_epilogue_specified () const;
+
+ void
+ cxx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ schema_epilogue ();
+
+ void
+ schema_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_epilogue_specified () const;
+
+ void
+ schema_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ sql_epilogue ();
+
+ void
+ sql_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_epilogue_specified () const;
+
+ void
+ sql_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ migration_epilogue ();
+
+ void
+ migration_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_epilogue_specified () const;
+
+ void
+ migration_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_prologue_file ();
+
+ void
+ hxx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_prologue_file_specified () const;
+
+ void
+ hxx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_prologue_file ();
+
+ void
+ ixx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_prologue_file_specified () const;
+
+ void
+ ixx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_prologue_file ();
+
+ void
+ cxx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_prologue_file_specified () const;
+
+ void
+ cxx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ schema_prologue_file ();
+
+ void
+ schema_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_prologue_file_specified () const;
+
+ void
+ schema_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_prologue_file ();
+
+ void
+ sql_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_prologue_file_specified () const;
+
+ void
+ sql_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ migration_prologue_file ();
+
+ void
+ migration_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_prologue_file_specified () const;
+
+ void
+ migration_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_interlude_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_interlude_file ();
+
+ void
+ sql_interlude_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_interlude_file_specified () const;
+
+ void
+ sql_interlude_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_epilogue_file ();
+
+ void
+ hxx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_epilogue_file_specified () const;
+
+ void
+ hxx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_epilogue_file ();
+
+ void
+ ixx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_epilogue_file_specified () const;
+
+ void
+ ixx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_epilogue_file ();
+
+ void
+ cxx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_epilogue_file_specified () const;
+
+ void
+ cxx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ schema_epilogue_file ();
+
+ void
+ schema_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_epilogue_file_specified () const;
+
+ void
+ schema_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_epilogue_file ();
+
+ void
+ sql_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_epilogue_file_specified () const;
+
+ void
+ sql_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ migration_epilogue_file ();
+
+ void
+ migration_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_epilogue_file_specified () const;
+
+ void
+ migration_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ odb_prologue ();
+
+ void
+ odb_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_prologue_specified () const;
+
+ void
+ odb_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ odb_prologue_file ();
+
+ void
+ odb_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_prologue_file_specified () const;
+
+ void
+ odb_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ odb_epilogue ();
+
+ void
+ odb_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_epilogue_specified () const;
+
+ void
+ odb_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ odb_epilogue_file ();
+
+ void
+ odb_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_epilogue_file_specified () const;
+
+ void
+ odb_epilogue_file_specified (bool);
+
+ const database_map<std::string>&
+ table_prefix () const;
+
+ database_map<std::string>&
+ table_prefix ();
+
+ void
+ table_prefix (const database_map<std::string>&);
+
+ bool
+ table_prefix_specified () const;
+
+ void
+ table_prefix_specified (bool);
+
+ const database_map<std::string>&
+ index_suffix () const;
+
+ database_map<std::string>&
+ index_suffix ();
+
+ void
+ index_suffix (const database_map<std::string>&);
+
+ bool
+ index_suffix_specified () const;
+
+ void
+ index_suffix_specified (bool);
+
+ const database_map<std::string>&
+ fkey_suffix () const;
+
+ database_map<std::string>&
+ fkey_suffix ();
+
+ void
+ fkey_suffix (const database_map<std::string>&);
+
+ bool
+ fkey_suffix_specified () const;
+
+ void
+ fkey_suffix_specified (bool);
+
+ const database_map<std::string>&
+ sequence_suffix () const;
+
+ database_map<std::string>&
+ sequence_suffix ();
+
+ void
+ sequence_suffix (const database_map<std::string>&);
+
+ bool
+ sequence_suffix_specified () const;
+
+ void
+ sequence_suffix_specified (bool);
+
+ const database_map<name_case>&
+ sql_name_case () const;
+
+ database_map<name_case>&
+ sql_name_case ();
+
+ void
+ sql_name_case (const database_map<name_case>&);
+
+ bool
+ sql_name_case_specified () const;
+
+ void
+ sql_name_case_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ table_regex () const;
+
+ database_map<std::vector<std::string> >&
+ table_regex ();
+
+ void
+ table_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ table_regex_specified () const;
+
+ void
+ table_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ column_regex () const;
+
+ database_map<std::vector<std::string> >&
+ column_regex ();
+
+ void
+ column_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ column_regex_specified () const;
+
+ void
+ column_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ index_regex () const;
+
+ database_map<std::vector<std::string> >&
+ index_regex ();
+
+ void
+ index_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ index_regex_specified () const;
+
+ void
+ index_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ fkey_regex () const;
+
+ database_map<std::vector<std::string> >&
+ fkey_regex ();
+
+ void
+ fkey_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ fkey_regex_specified () const;
+
+ void
+ fkey_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sequence_regex () const;
+
+ database_map<std::vector<std::string> >&
+ sequence_regex ();
+
+ void
+ sequence_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ sequence_regex_specified () const;
+
+ void
+ sequence_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ statement_regex () const;
+
+ database_map<std::vector<std::string> >&
+ statement_regex ();
+
+ void
+ statement_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ statement_regex_specified () const;
+
+ void
+ statement_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_name_regex () const;
+
+ database_map<std::vector<std::string> >&
+ sql_name_regex ();
+
+ void
+ sql_name_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_name_regex_specified () const;
+
+ void
+ sql_name_regex_specified (bool);
+
+ const bool&
+ sql_name_regex_trace () const;
+
+ bool&
+ sql_name_regex_trace ();
+
+ void
+ sql_name_regex_trace (const bool&);
+
+ const std::vector<std::string>&
+ accessor_regex () const;
+
+ std::vector<std::string>&
+ accessor_regex ();
+
+ void
+ accessor_regex (const std::vector<std::string>&);
+
+ bool
+ accessor_regex_specified () const;
+
+ void
+ accessor_regex_specified (bool);
+
+ const bool&
+ accessor_regex_trace () const;
+
+ bool&
+ accessor_regex_trace ();
+
+ void
+ accessor_regex_trace (const bool&);
+
+ const std::vector<std::string>&
+ modifier_regex () const;
+
+ std::vector<std::string>&
+ modifier_regex ();
+
+ void
+ modifier_regex (const std::vector<std::string>&);
+
+ bool
+ modifier_regex_specified () const;
+
+ void
+ modifier_regex_specified (bool);
+
+ const bool&
+ modifier_regex_trace () const;
+
+ bool&
+ modifier_regex_trace ();
+
+ void
+ modifier_regex_trace (const bool&);
+
+ const bool&
+ include_with_brackets () const;
+
+ bool&
+ include_with_brackets ();
+
+ void
+ include_with_brackets (const bool&);
+
+ const std::string&
+ include_prefix () const;
+
+ std::string&
+ include_prefix ();
+
+ void
+ include_prefix (const std::string&);
+
+ bool
+ include_prefix_specified () const;
+
+ void
+ include_prefix_specified (bool);
+
+ const std::vector<std::string>&
+ include_regex () const;
+
+ std::vector<std::string>&
+ include_regex ();
+
+ void
+ include_regex (const std::vector<std::string>&);
+
+ bool
+ include_regex_specified () const;
+
+ void
+ include_regex_specified (bool);
+
+ const bool&
+ include_regex_trace () const;
+
+ bool&
+ include_regex_trace ();
+
+ void
+ include_regex_trace (const bool&);
+
+ const std::string&
+ guard_prefix () const;
+
+ std::string&
+ guard_prefix ();
+
+ void
+ guard_prefix (const std::string&);
+
+ bool
+ guard_prefix_specified () const;
+
+ void
+ guard_prefix_specified (bool);
+
+ const bool&
+ show_sloc () const;
+
+ bool&
+ show_sloc ();
+
+ void
+ show_sloc (const bool&);
+
+ const std::size_t&
+ sloc_limit () const;
+
+ std::size_t&
+ sloc_limit ();
+
+ void
+ sloc_limit (const std::size_t&);
+
+ bool
+ sloc_limit_specified () const;
+
+ void
+ sloc_limit_specified (bool);
+
+ const std::string&
+ options_file () const;
+
+ std::string&
+ options_file ();
+
+ void
+ options_file (const std::string&);
+
+ bool
+ options_file_specified () const;
+
+ void
+ options_file_specified (bool);
+
+ const std::vector<std::string>&
+ x () const;
+
+ std::vector<std::string>&
+ x ();
+
+ void
+ x (const std::vector<std::string>&);
+
+ bool
+ x_specified () const;
+
+ void
+ x_specified (bool);
+
+ const bool&
+ v () const;
+
+ bool&
+ v ();
+
+ void
+ v (const bool&);
+
+ const bool&
+ trace () const;
+
+ bool&
+ trace ();
+
+ void
+ trace (const bool&);
+
+ const std::string&
+ mysql_engine () const;
+
+ std::string&
+ mysql_engine ();
+
+ void
+ mysql_engine (const std::string&);
+
+ bool
+ mysql_engine_specified () const;
+
+ void
+ mysql_engine_specified (bool);
+
+ const bool&
+ sqlite_override_null () const;
+
+ bool&
+ sqlite_override_null ();
+
+ void
+ sqlite_override_null (const bool&);
+
+ const bool&
+ sqlite_lax_auto_id () const;
+
+ bool&
+ sqlite_lax_auto_id ();
+
+ void
+ sqlite_lax_auto_id (const bool&);
+
+ const ::pgsql_version&
+ pgsql_server_version () const;
+
+ ::pgsql_version&
+ pgsql_server_version ();
+
+ void
+ pgsql_server_version (const ::pgsql_version&);
+
+ bool
+ pgsql_server_version_specified () const;
+
+ void
+ pgsql_server_version_specified (bool);
+
+ const ::oracle_version&
+ oracle_client_version () const;
+
+ ::oracle_version&
+ oracle_client_version ();
+
+ void
+ oracle_client_version (const ::oracle_version&);
+
+ bool
+ oracle_client_version_specified () const;
+
+ void
+ oracle_client_version_specified (bool);
+
+ const bool&
+ oracle_warn_truncation () const;
+
+ bool&
+ oracle_warn_truncation ();
+
+ void
+ oracle_warn_truncation (const bool&);
+
+ const ::mssql_version&
+ mssql_server_version () const;
+
+ ::mssql_version&
+ mssql_server_version ();
+
+ void
+ mssql_server_version (const ::mssql_version&);
+
+ bool
+ mssql_server_version_specified () const;
+
+ void
+ mssql_server_version_specified (bool);
+
+ const unsigned int&
+ mssql_short_limit () const;
+
+ unsigned int&
+ mssql_short_limit ();
+
+ void
+ mssql_short_limit (const unsigned int&);
+
+ bool
+ mssql_short_limit_specified () const;
+
+ void
+ mssql_short_limit_specified (bool);
+
+ // Print usage information.
+ //
+ static ::cli::usage_para
+ print_usage (::std::ostream&,
+ ::cli::usage_para = ::cli::usage_para::none);
+
+ // Option description.
+ //
+ static const ::cli::options&
+ description ();
+
+ // Implementation details.
+ //
+ protected:
+ friend struct _cli_options_desc_type;
+
+ static void
+ fill (::cli::options&);
+
+ bool
+ _parse (const char*, ::cli::scanner&);
+
+ private:
+ bool
+ _parse (::cli::scanner&,
+ ::cli::unknown_mode option,
+ ::cli::unknown_mode argument);
+
+ public:
+ std::uint64_t build2_metadata_;
+ bool build2_metadata_specified_;
+ bool help_;
+ bool version_;
+ std::vector<std::string> I_;
+ bool I_specified_;
+ std::vector<std::string> D_;
+ bool D_specified_;
+ std::vector<std::string> U_;
+ bool U_specified_;
+ std::vector< ::database> database_;
+ bool database_specified_;
+ ::multi_database multi_database_;
+ bool multi_database_specified_;
+ ::database default_database_;
+ bool default_database_specified_;
+ bool generate_query_;
+ bool generate_prepared_;
+ bool omit_unprepared_;
+ bool generate_session_;
+ bool generate_schema_;
+ bool generate_schema_only_;
+ bool suppress_migration_;
+ bool suppress_schema_version_;
+ database_map<qname> schema_version_table_;
+ bool schema_version_table_specified_;
+ database_map<std::set< ::schema_format> > schema_format_;
+ bool schema_format_specified_;
+ bool omit_drop_;
+ bool omit_create_;
+ database_map<std::string> schema_name_;
+ bool schema_name_specified_;
+ database_map<deferrable> fkeys_deferrable_mode_;
+ bool fkeys_deferrable_mode_specified_;
+ std::string default_pointer_;
+ bool default_pointer_specified_;
+ std::string session_type_;
+ bool session_type_specified_;
+ std::string profile_;
+ bool profile_specified_;
+ bool at_once_;
+ database_map<qname> schema_;
+ bool schema_specified_;
+ database_map<std::string> export_symbol_;
+ bool export_symbol_specified_;
+ database_map<std::string> extern_symbol_;
+ bool extern_symbol_specified_;
+ cxx_version std_;
+ bool std_specified_;
+ bool warn_hard_add_;
+ bool warn_hard_delete_;
+ bool warn_hard_;
+ std::string output_dir_;
+ bool output_dir_specified_;
+ std::string input_name_;
+ bool input_name_specified_;
+ database_map<std::string> changelog_;
+ bool changelog_specified_;
+ database_map<std::string> changelog_in_;
+ bool changelog_in_specified_;
+ database_map<std::string> changelog_out_;
+ bool changelog_out_specified_;
+ database_map<std::string> changelog_dir_;
+ bool changelog_dir_specified_;
+ bool init_changelog_;
+ database_map<std::string> odb_file_suffix_;
+ bool odb_file_suffix_specified_;
+ database_map<std::string> sql_file_suffix_;
+ bool sql_file_suffix_specified_;
+ database_map<std::string> schema_file_suffix_;
+ bool schema_file_suffix_specified_;
+ database_map<std::string> changelog_file_suffix_;
+ bool changelog_file_suffix_specified_;
+ std::string hxx_suffix_;
+ bool hxx_suffix_specified_;
+ std::string ixx_suffix_;
+ bool ixx_suffix_specified_;
+ std::string cxx_suffix_;
+ bool cxx_suffix_specified_;
+ std::string sql_suffix_;
+ bool sql_suffix_specified_;
+ std::string changelog_suffix_;
+ bool changelog_suffix_specified_;
+ database_map<std::vector<std::string> > hxx_prologue_;
+ bool hxx_prologue_specified_;
+ database_map<std::vector<std::string> > ixx_prologue_;
+ bool ixx_prologue_specified_;
+ database_map<std::vector<std::string> > cxx_prologue_;
+ bool cxx_prologue_specified_;
+ database_map<std::vector<std::string> > schema_prologue_;
+ bool schema_prologue_specified_;
+ database_map<std::vector<std::string> > sql_prologue_;
+ bool sql_prologue_specified_;
+ database_map<std::vector<std::string> > migration_prologue_;
+ bool migration_prologue_specified_;
+ database_map<std::vector<std::string> > sql_interlude_;
+ bool sql_interlude_specified_;
+ database_map<std::vector<std::string> > hxx_epilogue_;
+ bool hxx_epilogue_specified_;
+ database_map<std::vector<std::string> > ixx_epilogue_;
+ bool ixx_epilogue_specified_;
+ database_map<std::vector<std::string> > cxx_epilogue_;
+ bool cxx_epilogue_specified_;
+ database_map<std::vector<std::string> > schema_epilogue_;
+ bool schema_epilogue_specified_;
+ database_map<std::vector<std::string> > sql_epilogue_;
+ bool sql_epilogue_specified_;
+ database_map<std::vector<std::string> > migration_epilogue_;
+ bool migration_epilogue_specified_;
+ database_map<std::vector<std::string> > hxx_prologue_file_;
+ bool hxx_prologue_file_specified_;
+ database_map<std::vector<std::string> > ixx_prologue_file_;
+ bool ixx_prologue_file_specified_;
+ database_map<std::vector<std::string> > cxx_prologue_file_;
+ bool cxx_prologue_file_specified_;
+ database_map<std::vector<std::string> > schema_prologue_file_;
+ bool schema_prologue_file_specified_;
+ database_map<std::vector<std::string> > sql_prologue_file_;
+ bool sql_prologue_file_specified_;
+ database_map<std::vector<std::string> > migration_prologue_file_;
+ bool migration_prologue_file_specified_;
+ database_map<std::vector<std::string> > sql_interlude_file_;
+ bool sql_interlude_file_specified_;
+ database_map<std::vector<std::string> > hxx_epilogue_file_;
+ bool hxx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > ixx_epilogue_file_;
+ bool ixx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > cxx_epilogue_file_;
+ bool cxx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > schema_epilogue_file_;
+ bool schema_epilogue_file_specified_;
+ database_map<std::vector<std::string> > sql_epilogue_file_;
+ bool sql_epilogue_file_specified_;
+ database_map<std::vector<std::string> > migration_epilogue_file_;
+ bool migration_epilogue_file_specified_;
+ database_map<std::vector<std::string> > odb_prologue_;
+ bool odb_prologue_specified_;
+ database_map<std::vector<std::string> > odb_prologue_file_;
+ bool odb_prologue_file_specified_;
+ database_map<std::vector<std::string> > odb_epilogue_;
+ bool odb_epilogue_specified_;
+ database_map<std::vector<std::string> > odb_epilogue_file_;
+ bool odb_epilogue_file_specified_;
+ database_map<std::string> table_prefix_;
+ bool table_prefix_specified_;
+ database_map<std::string> index_suffix_;
+ bool index_suffix_specified_;
+ database_map<std::string> fkey_suffix_;
+ bool fkey_suffix_specified_;
+ database_map<std::string> sequence_suffix_;
+ bool sequence_suffix_specified_;
+ database_map<name_case> sql_name_case_;
+ bool sql_name_case_specified_;
+ database_map<std::vector<std::string> > table_regex_;
+ bool table_regex_specified_;
+ database_map<std::vector<std::string> > column_regex_;
+ bool column_regex_specified_;
+ database_map<std::vector<std::string> > index_regex_;
+ bool index_regex_specified_;
+ database_map<std::vector<std::string> > fkey_regex_;
+ bool fkey_regex_specified_;
+ database_map<std::vector<std::string> > sequence_regex_;
+ bool sequence_regex_specified_;
+ database_map<std::vector<std::string> > statement_regex_;
+ bool statement_regex_specified_;
+ database_map<std::vector<std::string> > sql_name_regex_;
+ bool sql_name_regex_specified_;
+ bool sql_name_regex_trace_;
+ std::vector<std::string> accessor_regex_;
+ bool accessor_regex_specified_;
+ bool accessor_regex_trace_;
+ std::vector<std::string> modifier_regex_;
+ bool modifier_regex_specified_;
+ bool modifier_regex_trace_;
+ bool include_with_brackets_;
+ std::string include_prefix_;
+ bool include_prefix_specified_;
+ std::vector<std::string> include_regex_;
+ bool include_regex_specified_;
+ bool include_regex_trace_;
+ std::string guard_prefix_;
+ bool guard_prefix_specified_;
+ bool show_sloc_;
+ std::size_t sloc_limit_;
+ bool sloc_limit_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ std::vector<std::string> x_;
+ bool x_specified_;
+ bool v_;
+ bool trace_;
+ std::string mysql_engine_;
+ bool mysql_engine_specified_;
+ bool sqlite_override_null_;
+ bool sqlite_lax_auto_id_;
+ ::pgsql_version pgsql_server_version_;
+ bool pgsql_server_version_specified_;
+ ::oracle_version oracle_client_version_;
+ bool oracle_client_version_specified_;
+ bool oracle_warn_truncation_;
+ ::mssql_version mssql_server_version_;
+ bool mssql_server_version_specified_;
+ unsigned int mssql_short_limit_;
+ bool mssql_short_limit_specified_;
+};
+
+#include <odb/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // ODB_OPTIONS_HXX
diff --git a/odb/odb/pregenerated/odb/options.ixx b/odb/odb/pregenerated/odb/options.ixx
new file mode 100644
index 0000000..9a78a2e
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.ixx
@@ -0,0 +1,3471 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace cli
+{
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+
+ inline const std::string& option::
+ name () const
+ {
+ return name_;
+ }
+
+ inline const option_names& option::
+ aliases () const
+ {
+ return aliases_;
+ }
+
+ inline bool option::
+ flag () const
+ {
+ return flag_;
+ }
+
+ inline const std::string& option::
+ default_value () const
+ {
+ return default_value_;
+ }
+
+ inline option::
+ option ()
+ {
+ }
+
+ inline option::
+ option (const std::string& n,
+ const option_names& a,
+ bool f,
+ const std::string& dv)
+ : name_ (n), aliases_ (a), flag_ (f), default_value_ (dv)
+ {
+ }
+
+ inline options::container_type::const_iterator options::
+ find (const std::string& name) const
+ {
+ map_type::const_iterator i (map_.find (name));
+ return i != map_.end () ? begin () + i->second : end ();
+ }
+}
+
+// options
+//
+
+inline const std::uint64_t& options::
+build2_metadata () const
+{
+ return this->build2_metadata_;
+}
+
+inline std::uint64_t& options::
+build2_metadata ()
+{
+ return this->build2_metadata_;
+}
+
+inline void options::
+build2_metadata (const std::uint64_t& x)
+{
+ this->build2_metadata_ = x;
+}
+
+inline bool options::
+build2_metadata_specified () const
+{
+ return this->build2_metadata_specified_;
+}
+
+inline void options::
+build2_metadata_specified (bool x)
+{
+ this->build2_metadata_specified_ = x;
+}
+
+inline const bool& options::
+help () const
+{
+ return this->help_;
+}
+
+inline bool& options::
+help ()
+{
+ return this->help_;
+}
+
+inline void options::
+help (const bool& x)
+{
+ this->help_ = x;
+}
+
+inline const bool& options::
+version () const
+{
+ return this->version_;
+}
+
+inline bool& options::
+version ()
+{
+ return this->version_;
+}
+
+inline void options::
+version (const bool& x)
+{
+ this->version_ = x;
+}
+
+inline const std::vector<std::string>& options::
+I () const
+{
+ return this->I_;
+}
+
+inline std::vector<std::string>& options::
+I ()
+{
+ return this->I_;
+}
+
+inline void options::
+I (const std::vector<std::string>& x)
+{
+ this->I_ = x;
+}
+
+inline bool options::
+I_specified () const
+{
+ return this->I_specified_;
+}
+
+inline void options::
+I_specified (bool x)
+{
+ this->I_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+D () const
+{
+ return this->D_;
+}
+
+inline std::vector<std::string>& options::
+D ()
+{
+ return this->D_;
+}
+
+inline void options::
+D (const std::vector<std::string>& x)
+{
+ this->D_ = x;
+}
+
+inline bool options::
+D_specified () const
+{
+ return this->D_specified_;
+}
+
+inline void options::
+D_specified (bool x)
+{
+ this->D_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+U () const
+{
+ return this->U_;
+}
+
+inline std::vector<std::string>& options::
+U ()
+{
+ return this->U_;
+}
+
+inline void options::
+U (const std::vector<std::string>& x)
+{
+ this->U_ = x;
+}
+
+inline bool options::
+U_specified () const
+{
+ return this->U_specified_;
+}
+
+inline void options::
+U_specified (bool x)
+{
+ this->U_specified_ = x;
+}
+
+inline const std::vector< ::database>& options::
+database () const
+{
+ return this->database_;
+}
+
+inline std::vector< ::database>& options::
+database ()
+{
+ return this->database_;
+}
+
+inline void options::
+database (const std::vector< ::database>& x)
+{
+ this->database_ = x;
+}
+
+inline bool options::
+database_specified () const
+{
+ return this->database_specified_;
+}
+
+inline void options::
+database_specified (bool x)
+{
+ this->database_specified_ = x;
+}
+
+inline const ::multi_database& options::
+multi_database () const
+{
+ return this->multi_database_;
+}
+
+inline ::multi_database& options::
+multi_database ()
+{
+ return this->multi_database_;
+}
+
+inline void options::
+multi_database (const ::multi_database& x)
+{
+ this->multi_database_ = x;
+}
+
+inline bool options::
+multi_database_specified () const
+{
+ return this->multi_database_specified_;
+}
+
+inline void options::
+multi_database_specified (bool x)
+{
+ this->multi_database_specified_ = x;
+}
+
+inline const ::database& options::
+default_database () const
+{
+ return this->default_database_;
+}
+
+inline ::database& options::
+default_database ()
+{
+ return this->default_database_;
+}
+
+inline void options::
+default_database (const ::database& x)
+{
+ this->default_database_ = x;
+}
+
+inline bool options::
+default_database_specified () const
+{
+ return this->default_database_specified_;
+}
+
+inline void options::
+default_database_specified (bool x)
+{
+ this->default_database_specified_ = x;
+}
+
+inline const bool& options::
+generate_query () const
+{
+ return this->generate_query_;
+}
+
+inline bool& options::
+generate_query ()
+{
+ return this->generate_query_;
+}
+
+inline void options::
+generate_query (const bool& x)
+{
+ this->generate_query_ = x;
+}
+
+inline const bool& options::
+generate_prepared () const
+{
+ return this->generate_prepared_;
+}
+
+inline bool& options::
+generate_prepared ()
+{
+ return this->generate_prepared_;
+}
+
+inline void options::
+generate_prepared (const bool& x)
+{
+ this->generate_prepared_ = x;
+}
+
+inline const bool& options::
+omit_unprepared () const
+{
+ return this->omit_unprepared_;
+}
+
+inline bool& options::
+omit_unprepared ()
+{
+ return this->omit_unprepared_;
+}
+
+inline void options::
+omit_unprepared (const bool& x)
+{
+ this->omit_unprepared_ = x;
+}
+
+inline const bool& options::
+generate_session () const
+{
+ return this->generate_session_;
+}
+
+inline bool& options::
+generate_session ()
+{
+ return this->generate_session_;
+}
+
+inline void options::
+generate_session (const bool& x)
+{
+ this->generate_session_ = x;
+}
+
+inline const bool& options::
+generate_schema () const
+{
+ return this->generate_schema_;
+}
+
+inline bool& options::
+generate_schema ()
+{
+ return this->generate_schema_;
+}
+
+inline void options::
+generate_schema (const bool& x)
+{
+ this->generate_schema_ = x;
+}
+
+inline const bool& options::
+generate_schema_only () const
+{
+ return this->generate_schema_only_;
+}
+
+inline bool& options::
+generate_schema_only ()
+{
+ return this->generate_schema_only_;
+}
+
+inline void options::
+generate_schema_only (const bool& x)
+{
+ this->generate_schema_only_ = x;
+}
+
+inline const bool& options::
+suppress_migration () const
+{
+ return this->suppress_migration_;
+}
+
+inline bool& options::
+suppress_migration ()
+{
+ return this->suppress_migration_;
+}
+
+inline void options::
+suppress_migration (const bool& x)
+{
+ this->suppress_migration_ = x;
+}
+
+inline const bool& options::
+suppress_schema_version () const
+{
+ return this->suppress_schema_version_;
+}
+
+inline bool& options::
+suppress_schema_version ()
+{
+ return this->suppress_schema_version_;
+}
+
+inline void options::
+suppress_schema_version (const bool& x)
+{
+ this->suppress_schema_version_ = x;
+}
+
+inline const database_map<qname>& options::
+schema_version_table () const
+{
+ return this->schema_version_table_;
+}
+
+inline database_map<qname>& options::
+schema_version_table ()
+{
+ return this->schema_version_table_;
+}
+
+inline void options::
+schema_version_table (const database_map<qname>& x)
+{
+ this->schema_version_table_ = x;
+}
+
+inline bool options::
+schema_version_table_specified () const
+{
+ return this->schema_version_table_specified_;
+}
+
+inline void options::
+schema_version_table_specified (bool x)
+{
+ this->schema_version_table_specified_ = x;
+}
+
+inline const database_map<std::set< ::schema_format> >& options::
+schema_format () const
+{
+ return this->schema_format_;
+}
+
+inline database_map<std::set< ::schema_format> >& options::
+schema_format ()
+{
+ return this->schema_format_;
+}
+
+inline void options::
+schema_format (const database_map<std::set< ::schema_format> >& x)
+{
+ this->schema_format_ = x;
+}
+
+inline bool options::
+schema_format_specified () const
+{
+ return this->schema_format_specified_;
+}
+
+inline void options::
+schema_format_specified (bool x)
+{
+ this->schema_format_specified_ = x;
+}
+
+inline const bool& options::
+omit_drop () const
+{
+ return this->omit_drop_;
+}
+
+inline bool& options::
+omit_drop ()
+{
+ return this->omit_drop_;
+}
+
+inline void options::
+omit_drop (const bool& x)
+{
+ this->omit_drop_ = x;
+}
+
+inline const bool& options::
+omit_create () const
+{
+ return this->omit_create_;
+}
+
+inline bool& options::
+omit_create ()
+{
+ return this->omit_create_;
+}
+
+inline void options::
+omit_create (const bool& x)
+{
+ this->omit_create_ = x;
+}
+
+inline const database_map<std::string>& options::
+schema_name () const
+{
+ return this->schema_name_;
+}
+
+inline database_map<std::string>& options::
+schema_name ()
+{
+ return this->schema_name_;
+}
+
+inline void options::
+schema_name (const database_map<std::string>& x)
+{
+ this->schema_name_ = x;
+}
+
+inline bool options::
+schema_name_specified () const
+{
+ return this->schema_name_specified_;
+}
+
+inline void options::
+schema_name_specified (bool x)
+{
+ this->schema_name_specified_ = x;
+}
+
+inline const database_map<deferrable>& options::
+fkeys_deferrable_mode () const
+{
+ return this->fkeys_deferrable_mode_;
+}
+
+inline database_map<deferrable>& options::
+fkeys_deferrable_mode ()
+{
+ return this->fkeys_deferrable_mode_;
+}
+
+inline void options::
+fkeys_deferrable_mode (const database_map<deferrable>& x)
+{
+ this->fkeys_deferrable_mode_ = x;
+}
+
+inline bool options::
+fkeys_deferrable_mode_specified () const
+{
+ return this->fkeys_deferrable_mode_specified_;
+}
+
+inline void options::
+fkeys_deferrable_mode_specified (bool x)
+{
+ this->fkeys_deferrable_mode_specified_ = x;
+}
+
+inline const std::string& options::
+default_pointer () const
+{
+ return this->default_pointer_;
+}
+
+inline std::string& options::
+default_pointer ()
+{
+ return this->default_pointer_;
+}
+
+inline void options::
+default_pointer (const std::string& x)
+{
+ this->default_pointer_ = x;
+}
+
+inline bool options::
+default_pointer_specified () const
+{
+ return this->default_pointer_specified_;
+}
+
+inline void options::
+default_pointer_specified (bool x)
+{
+ this->default_pointer_specified_ = x;
+}
+
+inline const std::string& options::
+session_type () const
+{
+ return this->session_type_;
+}
+
+inline std::string& options::
+session_type ()
+{
+ return this->session_type_;
+}
+
+inline void options::
+session_type (const std::string& x)
+{
+ this->session_type_ = x;
+}
+
+inline bool options::
+session_type_specified () const
+{
+ return this->session_type_specified_;
+}
+
+inline void options::
+session_type_specified (bool x)
+{
+ this->session_type_specified_ = x;
+}
+
+inline const std::string& options::
+profile () const
+{
+ return this->profile_;
+}
+
+inline std::string& options::
+profile ()
+{
+ return this->profile_;
+}
+
+inline void options::
+profile (const std::string& x)
+{
+ this->profile_ = x;
+}
+
+inline bool options::
+profile_specified () const
+{
+ return this->profile_specified_;
+}
+
+inline void options::
+profile_specified (bool x)
+{
+ this->profile_specified_ = x;
+}
+
+inline const bool& options::
+at_once () const
+{
+ return this->at_once_;
+}
+
+inline bool& options::
+at_once ()
+{
+ return this->at_once_;
+}
+
+inline void options::
+at_once (const bool& x)
+{
+ this->at_once_ = x;
+}
+
+inline const database_map<qname>& options::
+schema () const
+{
+ return this->schema_;
+}
+
+inline database_map<qname>& options::
+schema ()
+{
+ return this->schema_;
+}
+
+inline void options::
+schema (const database_map<qname>& x)
+{
+ this->schema_ = x;
+}
+
+inline bool options::
+schema_specified () const
+{
+ return this->schema_specified_;
+}
+
+inline void options::
+schema_specified (bool x)
+{
+ this->schema_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+export_symbol () const
+{
+ return this->export_symbol_;
+}
+
+inline database_map<std::string>& options::
+export_symbol ()
+{
+ return this->export_symbol_;
+}
+
+inline void options::
+export_symbol (const database_map<std::string>& x)
+{
+ this->export_symbol_ = x;
+}
+
+inline bool options::
+export_symbol_specified () const
+{
+ return this->export_symbol_specified_;
+}
+
+inline void options::
+export_symbol_specified (bool x)
+{
+ this->export_symbol_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+extern_symbol () const
+{
+ return this->extern_symbol_;
+}
+
+inline database_map<std::string>& options::
+extern_symbol ()
+{
+ return this->extern_symbol_;
+}
+
+inline void options::
+extern_symbol (const database_map<std::string>& x)
+{
+ this->extern_symbol_ = x;
+}
+
+inline bool options::
+extern_symbol_specified () const
+{
+ return this->extern_symbol_specified_;
+}
+
+inline void options::
+extern_symbol_specified (bool x)
+{
+ this->extern_symbol_specified_ = x;
+}
+
+inline const cxx_version& options::
+std () const
+{
+ return this->std_;
+}
+
+inline cxx_version& options::
+std ()
+{
+ return this->std_;
+}
+
+inline void options::
+std (const cxx_version& x)
+{
+ this->std_ = x;
+}
+
+inline bool options::
+std_specified () const
+{
+ return this->std_specified_;
+}
+
+inline void options::
+std_specified (bool x)
+{
+ this->std_specified_ = x;
+}
+
+inline const bool& options::
+warn_hard_add () const
+{
+ return this->warn_hard_add_;
+}
+
+inline bool& options::
+warn_hard_add ()
+{
+ return this->warn_hard_add_;
+}
+
+inline void options::
+warn_hard_add (const bool& x)
+{
+ this->warn_hard_add_ = x;
+}
+
+inline const bool& options::
+warn_hard_delete () const
+{
+ return this->warn_hard_delete_;
+}
+
+inline bool& options::
+warn_hard_delete ()
+{
+ return this->warn_hard_delete_;
+}
+
+inline void options::
+warn_hard_delete (const bool& x)
+{
+ this->warn_hard_delete_ = x;
+}
+
+inline const bool& options::
+warn_hard () const
+{
+ return this->warn_hard_;
+}
+
+inline bool& options::
+warn_hard ()
+{
+ return this->warn_hard_;
+}
+
+inline void options::
+warn_hard (const bool& x)
+{
+ this->warn_hard_ = x;
+}
+
+inline const std::string& options::
+output_dir () const
+{
+ return this->output_dir_;
+}
+
+inline std::string& options::
+output_dir ()
+{
+ return this->output_dir_;
+}
+
+inline void options::
+output_dir (const std::string& x)
+{
+ this->output_dir_ = x;
+}
+
+inline bool options::
+output_dir_specified () const
+{
+ return this->output_dir_specified_;
+}
+
+inline void options::
+output_dir_specified (bool x)
+{
+ this->output_dir_specified_ = x;
+}
+
+inline const std::string& options::
+input_name () const
+{
+ return this->input_name_;
+}
+
+inline std::string& options::
+input_name ()
+{
+ return this->input_name_;
+}
+
+inline void options::
+input_name (const std::string& x)
+{
+ this->input_name_ = x;
+}
+
+inline bool options::
+input_name_specified () const
+{
+ return this->input_name_specified_;
+}
+
+inline void options::
+input_name_specified (bool x)
+{
+ this->input_name_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog () const
+{
+ return this->changelog_;
+}
+
+inline database_map<std::string>& options::
+changelog ()
+{
+ return this->changelog_;
+}
+
+inline void options::
+changelog (const database_map<std::string>& x)
+{
+ this->changelog_ = x;
+}
+
+inline bool options::
+changelog_specified () const
+{
+ return this->changelog_specified_;
+}
+
+inline void options::
+changelog_specified (bool x)
+{
+ this->changelog_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_in () const
+{
+ return this->changelog_in_;
+}
+
+inline database_map<std::string>& options::
+changelog_in ()
+{
+ return this->changelog_in_;
+}
+
+inline void options::
+changelog_in (const database_map<std::string>& x)
+{
+ this->changelog_in_ = x;
+}
+
+inline bool options::
+changelog_in_specified () const
+{
+ return this->changelog_in_specified_;
+}
+
+inline void options::
+changelog_in_specified (bool x)
+{
+ this->changelog_in_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_out () const
+{
+ return this->changelog_out_;
+}
+
+inline database_map<std::string>& options::
+changelog_out ()
+{
+ return this->changelog_out_;
+}
+
+inline void options::
+changelog_out (const database_map<std::string>& x)
+{
+ this->changelog_out_ = x;
+}
+
+inline bool options::
+changelog_out_specified () const
+{
+ return this->changelog_out_specified_;
+}
+
+inline void options::
+changelog_out_specified (bool x)
+{
+ this->changelog_out_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_dir () const
+{
+ return this->changelog_dir_;
+}
+
+inline database_map<std::string>& options::
+changelog_dir ()
+{
+ return this->changelog_dir_;
+}
+
+inline void options::
+changelog_dir (const database_map<std::string>& x)
+{
+ this->changelog_dir_ = x;
+}
+
+inline bool options::
+changelog_dir_specified () const
+{
+ return this->changelog_dir_specified_;
+}
+
+inline void options::
+changelog_dir_specified (bool x)
+{
+ this->changelog_dir_specified_ = x;
+}
+
+inline const bool& options::
+init_changelog () const
+{
+ return this->init_changelog_;
+}
+
+inline bool& options::
+init_changelog ()
+{
+ return this->init_changelog_;
+}
+
+inline void options::
+init_changelog (const bool& x)
+{
+ this->init_changelog_ = x;
+}
+
+inline const database_map<std::string>& options::
+odb_file_suffix () const
+{
+ return this->odb_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+odb_file_suffix ()
+{
+ return this->odb_file_suffix_;
+}
+
+inline void options::
+odb_file_suffix (const database_map<std::string>& x)
+{
+ this->odb_file_suffix_ = x;
+}
+
+inline bool options::
+odb_file_suffix_specified () const
+{
+ return this->odb_file_suffix_specified_;
+}
+
+inline void options::
+odb_file_suffix_specified (bool x)
+{
+ this->odb_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+sql_file_suffix () const
+{
+ return this->sql_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+sql_file_suffix ()
+{
+ return this->sql_file_suffix_;
+}
+
+inline void options::
+sql_file_suffix (const database_map<std::string>& x)
+{
+ this->sql_file_suffix_ = x;
+}
+
+inline bool options::
+sql_file_suffix_specified () const
+{
+ return this->sql_file_suffix_specified_;
+}
+
+inline void options::
+sql_file_suffix_specified (bool x)
+{
+ this->sql_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+schema_file_suffix () const
+{
+ return this->schema_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+schema_file_suffix ()
+{
+ return this->schema_file_suffix_;
+}
+
+inline void options::
+schema_file_suffix (const database_map<std::string>& x)
+{
+ this->schema_file_suffix_ = x;
+}
+
+inline bool options::
+schema_file_suffix_specified () const
+{
+ return this->schema_file_suffix_specified_;
+}
+
+inline void options::
+schema_file_suffix_specified (bool x)
+{
+ this->schema_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_file_suffix () const
+{
+ return this->changelog_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+changelog_file_suffix ()
+{
+ return this->changelog_file_suffix_;
+}
+
+inline void options::
+changelog_file_suffix (const database_map<std::string>& x)
+{
+ this->changelog_file_suffix_ = x;
+}
+
+inline bool options::
+changelog_file_suffix_specified () const
+{
+ return this->changelog_file_suffix_specified_;
+}
+
+inline void options::
+changelog_file_suffix_specified (bool x)
+{
+ this->changelog_file_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+hxx_suffix () const
+{
+ return this->hxx_suffix_;
+}
+
+inline std::string& options::
+hxx_suffix ()
+{
+ return this->hxx_suffix_;
+}
+
+inline void options::
+hxx_suffix (const std::string& x)
+{
+ this->hxx_suffix_ = x;
+}
+
+inline bool options::
+hxx_suffix_specified () const
+{
+ return this->hxx_suffix_specified_;
+}
+
+inline void options::
+hxx_suffix_specified (bool x)
+{
+ this->hxx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+ixx_suffix () const
+{
+ return this->ixx_suffix_;
+}
+
+inline std::string& options::
+ixx_suffix ()
+{
+ return this->ixx_suffix_;
+}
+
+inline void options::
+ixx_suffix (const std::string& x)
+{
+ this->ixx_suffix_ = x;
+}
+
+inline bool options::
+ixx_suffix_specified () const
+{
+ return this->ixx_suffix_specified_;
+}
+
+inline void options::
+ixx_suffix_specified (bool x)
+{
+ this->ixx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+cxx_suffix () const
+{
+ return this->cxx_suffix_;
+}
+
+inline std::string& options::
+cxx_suffix ()
+{
+ return this->cxx_suffix_;
+}
+
+inline void options::
+cxx_suffix (const std::string& x)
+{
+ this->cxx_suffix_ = x;
+}
+
+inline bool options::
+cxx_suffix_specified () const
+{
+ return this->cxx_suffix_specified_;
+}
+
+inline void options::
+cxx_suffix_specified (bool x)
+{
+ this->cxx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+sql_suffix () const
+{
+ return this->sql_suffix_;
+}
+
+inline std::string& options::
+sql_suffix ()
+{
+ return this->sql_suffix_;
+}
+
+inline void options::
+sql_suffix (const std::string& x)
+{
+ this->sql_suffix_ = x;
+}
+
+inline bool options::
+sql_suffix_specified () const
+{
+ return this->sql_suffix_specified_;
+}
+
+inline void options::
+sql_suffix_specified (bool x)
+{
+ this->sql_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+changelog_suffix () const
+{
+ return this->changelog_suffix_;
+}
+
+inline std::string& options::
+changelog_suffix ()
+{
+ return this->changelog_suffix_;
+}
+
+inline void options::
+changelog_suffix (const std::string& x)
+{
+ this->changelog_suffix_ = x;
+}
+
+inline bool options::
+changelog_suffix_specified () const
+{
+ return this->changelog_suffix_specified_;
+}
+
+inline void options::
+changelog_suffix_specified (bool x)
+{
+ this->changelog_suffix_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_prologue () const
+{
+ return this->hxx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_prologue ()
+{
+ return this->hxx_prologue_;
+}
+
+inline void options::
+hxx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_prologue_ = x;
+}
+
+inline bool options::
+hxx_prologue_specified () const
+{
+ return this->hxx_prologue_specified_;
+}
+
+inline void options::
+hxx_prologue_specified (bool x)
+{
+ this->hxx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_prologue () const
+{
+ return this->ixx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_prologue ()
+{
+ return this->ixx_prologue_;
+}
+
+inline void options::
+ixx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_prologue_ = x;
+}
+
+inline bool options::
+ixx_prologue_specified () const
+{
+ return this->ixx_prologue_specified_;
+}
+
+inline void options::
+ixx_prologue_specified (bool x)
+{
+ this->ixx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_prologue () const
+{
+ return this->cxx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_prologue ()
+{
+ return this->cxx_prologue_;
+}
+
+inline void options::
+cxx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_prologue_ = x;
+}
+
+inline bool options::
+cxx_prologue_specified () const
+{
+ return this->cxx_prologue_specified_;
+}
+
+inline void options::
+cxx_prologue_specified (bool x)
+{
+ this->cxx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_prologue () const
+{
+ return this->schema_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_prologue ()
+{
+ return this->schema_prologue_;
+}
+
+inline void options::
+schema_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_prologue_ = x;
+}
+
+inline bool options::
+schema_prologue_specified () const
+{
+ return this->schema_prologue_specified_;
+}
+
+inline void options::
+schema_prologue_specified (bool x)
+{
+ this->schema_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_prologue () const
+{
+ return this->sql_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_prologue ()
+{
+ return this->sql_prologue_;
+}
+
+inline void options::
+sql_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_prologue_ = x;
+}
+
+inline bool options::
+sql_prologue_specified () const
+{
+ return this->sql_prologue_specified_;
+}
+
+inline void options::
+sql_prologue_specified (bool x)
+{
+ this->sql_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_prologue () const
+{
+ return this->migration_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_prologue ()
+{
+ return this->migration_prologue_;
+}
+
+inline void options::
+migration_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_prologue_ = x;
+}
+
+inline bool options::
+migration_prologue_specified () const
+{
+ return this->migration_prologue_specified_;
+}
+
+inline void options::
+migration_prologue_specified (bool x)
+{
+ this->migration_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_interlude () const
+{
+ return this->sql_interlude_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_interlude ()
+{
+ return this->sql_interlude_;
+}
+
+inline void options::
+sql_interlude (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_interlude_ = x;
+}
+
+inline bool options::
+sql_interlude_specified () const
+{
+ return this->sql_interlude_specified_;
+}
+
+inline void options::
+sql_interlude_specified (bool x)
+{
+ this->sql_interlude_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_epilogue () const
+{
+ return this->hxx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_epilogue ()
+{
+ return this->hxx_epilogue_;
+}
+
+inline void options::
+hxx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_epilogue_ = x;
+}
+
+inline bool options::
+hxx_epilogue_specified () const
+{
+ return this->hxx_epilogue_specified_;
+}
+
+inline void options::
+hxx_epilogue_specified (bool x)
+{
+ this->hxx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_epilogue () const
+{
+ return this->ixx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_epilogue ()
+{
+ return this->ixx_epilogue_;
+}
+
+inline void options::
+ixx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_epilogue_ = x;
+}
+
+inline bool options::
+ixx_epilogue_specified () const
+{
+ return this->ixx_epilogue_specified_;
+}
+
+inline void options::
+ixx_epilogue_specified (bool x)
+{
+ this->ixx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_epilogue () const
+{
+ return this->cxx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_epilogue ()
+{
+ return this->cxx_epilogue_;
+}
+
+inline void options::
+cxx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_epilogue_ = x;
+}
+
+inline bool options::
+cxx_epilogue_specified () const
+{
+ return this->cxx_epilogue_specified_;
+}
+
+inline void options::
+cxx_epilogue_specified (bool x)
+{
+ this->cxx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_epilogue () const
+{
+ return this->schema_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_epilogue ()
+{
+ return this->schema_epilogue_;
+}
+
+inline void options::
+schema_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_epilogue_ = x;
+}
+
+inline bool options::
+schema_epilogue_specified () const
+{
+ return this->schema_epilogue_specified_;
+}
+
+inline void options::
+schema_epilogue_specified (bool x)
+{
+ this->schema_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_epilogue () const
+{
+ return this->sql_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_epilogue ()
+{
+ return this->sql_epilogue_;
+}
+
+inline void options::
+sql_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_epilogue_ = x;
+}
+
+inline bool options::
+sql_epilogue_specified () const
+{
+ return this->sql_epilogue_specified_;
+}
+
+inline void options::
+sql_epilogue_specified (bool x)
+{
+ this->sql_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_epilogue () const
+{
+ return this->migration_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_epilogue ()
+{
+ return this->migration_epilogue_;
+}
+
+inline void options::
+migration_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_epilogue_ = x;
+}
+
+inline bool options::
+migration_epilogue_specified () const
+{
+ return this->migration_epilogue_specified_;
+}
+
+inline void options::
+migration_epilogue_specified (bool x)
+{
+ this->migration_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_prologue_file () const
+{
+ return this->hxx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_prologue_file ()
+{
+ return this->hxx_prologue_file_;
+}
+
+inline void options::
+hxx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_prologue_file_ = x;
+}
+
+inline bool options::
+hxx_prologue_file_specified () const
+{
+ return this->hxx_prologue_file_specified_;
+}
+
+inline void options::
+hxx_prologue_file_specified (bool x)
+{
+ this->hxx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_prologue_file () const
+{
+ return this->ixx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_prologue_file ()
+{
+ return this->ixx_prologue_file_;
+}
+
+inline void options::
+ixx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_prologue_file_ = x;
+}
+
+inline bool options::
+ixx_prologue_file_specified () const
+{
+ return this->ixx_prologue_file_specified_;
+}
+
+inline void options::
+ixx_prologue_file_specified (bool x)
+{
+ this->ixx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_prologue_file () const
+{
+ return this->cxx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_prologue_file ()
+{
+ return this->cxx_prologue_file_;
+}
+
+inline void options::
+cxx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_prologue_file_ = x;
+}
+
+inline bool options::
+cxx_prologue_file_specified () const
+{
+ return this->cxx_prologue_file_specified_;
+}
+
+inline void options::
+cxx_prologue_file_specified (bool x)
+{
+ this->cxx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_prologue_file () const
+{
+ return this->schema_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_prologue_file ()
+{
+ return this->schema_prologue_file_;
+}
+
+inline void options::
+schema_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_prologue_file_ = x;
+}
+
+inline bool options::
+schema_prologue_file_specified () const
+{
+ return this->schema_prologue_file_specified_;
+}
+
+inline void options::
+schema_prologue_file_specified (bool x)
+{
+ this->schema_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_prologue_file () const
+{
+ return this->sql_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_prologue_file ()
+{
+ return this->sql_prologue_file_;
+}
+
+inline void options::
+sql_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_prologue_file_ = x;
+}
+
+inline bool options::
+sql_prologue_file_specified () const
+{
+ return this->sql_prologue_file_specified_;
+}
+
+inline void options::
+sql_prologue_file_specified (bool x)
+{
+ this->sql_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_prologue_file () const
+{
+ return this->migration_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_prologue_file ()
+{
+ return this->migration_prologue_file_;
+}
+
+inline void options::
+migration_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_prologue_file_ = x;
+}
+
+inline bool options::
+migration_prologue_file_specified () const
+{
+ return this->migration_prologue_file_specified_;
+}
+
+inline void options::
+migration_prologue_file_specified (bool x)
+{
+ this->migration_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_interlude_file () const
+{
+ return this->sql_interlude_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_interlude_file ()
+{
+ return this->sql_interlude_file_;
+}
+
+inline void options::
+sql_interlude_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_interlude_file_ = x;
+}
+
+inline bool options::
+sql_interlude_file_specified () const
+{
+ return this->sql_interlude_file_specified_;
+}
+
+inline void options::
+sql_interlude_file_specified (bool x)
+{
+ this->sql_interlude_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_epilogue_file () const
+{
+ return this->hxx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_epilogue_file ()
+{
+ return this->hxx_epilogue_file_;
+}
+
+inline void options::
+hxx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_epilogue_file_ = x;
+}
+
+inline bool options::
+hxx_epilogue_file_specified () const
+{
+ return this->hxx_epilogue_file_specified_;
+}
+
+inline void options::
+hxx_epilogue_file_specified (bool x)
+{
+ this->hxx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_epilogue_file () const
+{
+ return this->ixx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_epilogue_file ()
+{
+ return this->ixx_epilogue_file_;
+}
+
+inline void options::
+ixx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_epilogue_file_ = x;
+}
+
+inline bool options::
+ixx_epilogue_file_specified () const
+{
+ return this->ixx_epilogue_file_specified_;
+}
+
+inline void options::
+ixx_epilogue_file_specified (bool x)
+{
+ this->ixx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_epilogue_file () const
+{
+ return this->cxx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_epilogue_file ()
+{
+ return this->cxx_epilogue_file_;
+}
+
+inline void options::
+cxx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_epilogue_file_ = x;
+}
+
+inline bool options::
+cxx_epilogue_file_specified () const
+{
+ return this->cxx_epilogue_file_specified_;
+}
+
+inline void options::
+cxx_epilogue_file_specified (bool x)
+{
+ this->cxx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_epilogue_file () const
+{
+ return this->schema_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_epilogue_file ()
+{
+ return this->schema_epilogue_file_;
+}
+
+inline void options::
+schema_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_epilogue_file_ = x;
+}
+
+inline bool options::
+schema_epilogue_file_specified () const
+{
+ return this->schema_epilogue_file_specified_;
+}
+
+inline void options::
+schema_epilogue_file_specified (bool x)
+{
+ this->schema_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_epilogue_file () const
+{
+ return this->sql_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_epilogue_file ()
+{
+ return this->sql_epilogue_file_;
+}
+
+inline void options::
+sql_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_epilogue_file_ = x;
+}
+
+inline bool options::
+sql_epilogue_file_specified () const
+{
+ return this->sql_epilogue_file_specified_;
+}
+
+inline void options::
+sql_epilogue_file_specified (bool x)
+{
+ this->sql_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_epilogue_file () const
+{
+ return this->migration_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_epilogue_file ()
+{
+ return this->migration_epilogue_file_;
+}
+
+inline void options::
+migration_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_epilogue_file_ = x;
+}
+
+inline bool options::
+migration_epilogue_file_specified () const
+{
+ return this->migration_epilogue_file_specified_;
+}
+
+inline void options::
+migration_epilogue_file_specified (bool x)
+{
+ this->migration_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_prologue () const
+{
+ return this->odb_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_prologue ()
+{
+ return this->odb_prologue_;
+}
+
+inline void options::
+odb_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_prologue_ = x;
+}
+
+inline bool options::
+odb_prologue_specified () const
+{
+ return this->odb_prologue_specified_;
+}
+
+inline void options::
+odb_prologue_specified (bool x)
+{
+ this->odb_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_prologue_file () const
+{
+ return this->odb_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_prologue_file ()
+{
+ return this->odb_prologue_file_;
+}
+
+inline void options::
+odb_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_prologue_file_ = x;
+}
+
+inline bool options::
+odb_prologue_file_specified () const
+{
+ return this->odb_prologue_file_specified_;
+}
+
+inline void options::
+odb_prologue_file_specified (bool x)
+{
+ this->odb_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_epilogue () const
+{
+ return this->odb_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_epilogue ()
+{
+ return this->odb_epilogue_;
+}
+
+inline void options::
+odb_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_epilogue_ = x;
+}
+
+inline bool options::
+odb_epilogue_specified () const
+{
+ return this->odb_epilogue_specified_;
+}
+
+inline void options::
+odb_epilogue_specified (bool x)
+{
+ this->odb_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_epilogue_file () const
+{
+ return this->odb_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_epilogue_file ()
+{
+ return this->odb_epilogue_file_;
+}
+
+inline void options::
+odb_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_epilogue_file_ = x;
+}
+
+inline bool options::
+odb_epilogue_file_specified () const
+{
+ return this->odb_epilogue_file_specified_;
+}
+
+inline void options::
+odb_epilogue_file_specified (bool x)
+{
+ this->odb_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+table_prefix () const
+{
+ return this->table_prefix_;
+}
+
+inline database_map<std::string>& options::
+table_prefix ()
+{
+ return this->table_prefix_;
+}
+
+inline void options::
+table_prefix (const database_map<std::string>& x)
+{
+ this->table_prefix_ = x;
+}
+
+inline bool options::
+table_prefix_specified () const
+{
+ return this->table_prefix_specified_;
+}
+
+inline void options::
+table_prefix_specified (bool x)
+{
+ this->table_prefix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+index_suffix () const
+{
+ return this->index_suffix_;
+}
+
+inline database_map<std::string>& options::
+index_suffix ()
+{
+ return this->index_suffix_;
+}
+
+inline void options::
+index_suffix (const database_map<std::string>& x)
+{
+ this->index_suffix_ = x;
+}
+
+inline bool options::
+index_suffix_specified () const
+{
+ return this->index_suffix_specified_;
+}
+
+inline void options::
+index_suffix_specified (bool x)
+{
+ this->index_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+fkey_suffix () const
+{
+ return this->fkey_suffix_;
+}
+
+inline database_map<std::string>& options::
+fkey_suffix ()
+{
+ return this->fkey_suffix_;
+}
+
+inline void options::
+fkey_suffix (const database_map<std::string>& x)
+{
+ this->fkey_suffix_ = x;
+}
+
+inline bool options::
+fkey_suffix_specified () const
+{
+ return this->fkey_suffix_specified_;
+}
+
+inline void options::
+fkey_suffix_specified (bool x)
+{
+ this->fkey_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+sequence_suffix () const
+{
+ return this->sequence_suffix_;
+}
+
+inline database_map<std::string>& options::
+sequence_suffix ()
+{
+ return this->sequence_suffix_;
+}
+
+inline void options::
+sequence_suffix (const database_map<std::string>& x)
+{
+ this->sequence_suffix_ = x;
+}
+
+inline bool options::
+sequence_suffix_specified () const
+{
+ return this->sequence_suffix_specified_;
+}
+
+inline void options::
+sequence_suffix_specified (bool x)
+{
+ this->sequence_suffix_specified_ = x;
+}
+
+inline const database_map<name_case>& options::
+sql_name_case () const
+{
+ return this->sql_name_case_;
+}
+
+inline database_map<name_case>& options::
+sql_name_case ()
+{
+ return this->sql_name_case_;
+}
+
+inline void options::
+sql_name_case (const database_map<name_case>& x)
+{
+ this->sql_name_case_ = x;
+}
+
+inline bool options::
+sql_name_case_specified () const
+{
+ return this->sql_name_case_specified_;
+}
+
+inline void options::
+sql_name_case_specified (bool x)
+{
+ this->sql_name_case_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+table_regex () const
+{
+ return this->table_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+table_regex ()
+{
+ return this->table_regex_;
+}
+
+inline void options::
+table_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->table_regex_ = x;
+}
+
+inline bool options::
+table_regex_specified () const
+{
+ return this->table_regex_specified_;
+}
+
+inline void options::
+table_regex_specified (bool x)
+{
+ this->table_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+column_regex () const
+{
+ return this->column_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+column_regex ()
+{
+ return this->column_regex_;
+}
+
+inline void options::
+column_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->column_regex_ = x;
+}
+
+inline bool options::
+column_regex_specified () const
+{
+ return this->column_regex_specified_;
+}
+
+inline void options::
+column_regex_specified (bool x)
+{
+ this->column_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+index_regex () const
+{
+ return this->index_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+index_regex ()
+{
+ return this->index_regex_;
+}
+
+inline void options::
+index_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->index_regex_ = x;
+}
+
+inline bool options::
+index_regex_specified () const
+{
+ return this->index_regex_specified_;
+}
+
+inline void options::
+index_regex_specified (bool x)
+{
+ this->index_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+fkey_regex () const
+{
+ return this->fkey_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+fkey_regex ()
+{
+ return this->fkey_regex_;
+}
+
+inline void options::
+fkey_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->fkey_regex_ = x;
+}
+
+inline bool options::
+fkey_regex_specified () const
+{
+ return this->fkey_regex_specified_;
+}
+
+inline void options::
+fkey_regex_specified (bool x)
+{
+ this->fkey_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sequence_regex () const
+{
+ return this->sequence_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sequence_regex ()
+{
+ return this->sequence_regex_;
+}
+
+inline void options::
+sequence_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->sequence_regex_ = x;
+}
+
+inline bool options::
+sequence_regex_specified () const
+{
+ return this->sequence_regex_specified_;
+}
+
+inline void options::
+sequence_regex_specified (bool x)
+{
+ this->sequence_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+statement_regex () const
+{
+ return this->statement_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+statement_regex ()
+{
+ return this->statement_regex_;
+}
+
+inline void options::
+statement_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->statement_regex_ = x;
+}
+
+inline bool options::
+statement_regex_specified () const
+{
+ return this->statement_regex_specified_;
+}
+
+inline void options::
+statement_regex_specified (bool x)
+{
+ this->statement_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_name_regex () const
+{
+ return this->sql_name_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_name_regex ()
+{
+ return this->sql_name_regex_;
+}
+
+inline void options::
+sql_name_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_name_regex_ = x;
+}
+
+inline bool options::
+sql_name_regex_specified () const
+{
+ return this->sql_name_regex_specified_;
+}
+
+inline void options::
+sql_name_regex_specified (bool x)
+{
+ this->sql_name_regex_specified_ = x;
+}
+
+inline const bool& options::
+sql_name_regex_trace () const
+{
+ return this->sql_name_regex_trace_;
+}
+
+inline bool& options::
+sql_name_regex_trace ()
+{
+ return this->sql_name_regex_trace_;
+}
+
+inline void options::
+sql_name_regex_trace (const bool& x)
+{
+ this->sql_name_regex_trace_ = x;
+}
+
+inline const std::vector<std::string>& options::
+accessor_regex () const
+{
+ return this->accessor_regex_;
+}
+
+inline std::vector<std::string>& options::
+accessor_regex ()
+{
+ return this->accessor_regex_;
+}
+
+inline void options::
+accessor_regex (const std::vector<std::string>& x)
+{
+ this->accessor_regex_ = x;
+}
+
+inline bool options::
+accessor_regex_specified () const
+{
+ return this->accessor_regex_specified_;
+}
+
+inline void options::
+accessor_regex_specified (bool x)
+{
+ this->accessor_regex_specified_ = x;
+}
+
+inline const bool& options::
+accessor_regex_trace () const
+{
+ return this->accessor_regex_trace_;
+}
+
+inline bool& options::
+accessor_regex_trace ()
+{
+ return this->accessor_regex_trace_;
+}
+
+inline void options::
+accessor_regex_trace (const bool& x)
+{
+ this->accessor_regex_trace_ = x;
+}
+
+inline const std::vector<std::string>& options::
+modifier_regex () const
+{
+ return this->modifier_regex_;
+}
+
+inline std::vector<std::string>& options::
+modifier_regex ()
+{
+ return this->modifier_regex_;
+}
+
+inline void options::
+modifier_regex (const std::vector<std::string>& x)
+{
+ this->modifier_regex_ = x;
+}
+
+inline bool options::
+modifier_regex_specified () const
+{
+ return this->modifier_regex_specified_;
+}
+
+inline void options::
+modifier_regex_specified (bool x)
+{
+ this->modifier_regex_specified_ = x;
+}
+
+inline const bool& options::
+modifier_regex_trace () const
+{
+ return this->modifier_regex_trace_;
+}
+
+inline bool& options::
+modifier_regex_trace ()
+{
+ return this->modifier_regex_trace_;
+}
+
+inline void options::
+modifier_regex_trace (const bool& x)
+{
+ this->modifier_regex_trace_ = x;
+}
+
+inline const bool& options::
+include_with_brackets () const
+{
+ return this->include_with_brackets_;
+}
+
+inline bool& options::
+include_with_brackets ()
+{
+ return this->include_with_brackets_;
+}
+
+inline void options::
+include_with_brackets (const bool& x)
+{
+ this->include_with_brackets_ = x;
+}
+
+inline const std::string& options::
+include_prefix () const
+{
+ return this->include_prefix_;
+}
+
+inline std::string& options::
+include_prefix ()
+{
+ return this->include_prefix_;
+}
+
+inline void options::
+include_prefix (const std::string& x)
+{
+ this->include_prefix_ = x;
+}
+
+inline bool options::
+include_prefix_specified () const
+{
+ return this->include_prefix_specified_;
+}
+
+inline void options::
+include_prefix_specified (bool x)
+{
+ this->include_prefix_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+include_regex () const
+{
+ return this->include_regex_;
+}
+
+inline std::vector<std::string>& options::
+include_regex ()
+{
+ return this->include_regex_;
+}
+
+inline void options::
+include_regex (const std::vector<std::string>& x)
+{
+ this->include_regex_ = x;
+}
+
+inline bool options::
+include_regex_specified () const
+{
+ return this->include_regex_specified_;
+}
+
+inline void options::
+include_regex_specified (bool x)
+{
+ this->include_regex_specified_ = x;
+}
+
+inline const bool& options::
+include_regex_trace () const
+{
+ return this->include_regex_trace_;
+}
+
+inline bool& options::
+include_regex_trace ()
+{
+ return this->include_regex_trace_;
+}
+
+inline void options::
+include_regex_trace (const bool& x)
+{
+ this->include_regex_trace_ = x;
+}
+
+inline const std::string& options::
+guard_prefix () const
+{
+ return this->guard_prefix_;
+}
+
+inline std::string& options::
+guard_prefix ()
+{
+ return this->guard_prefix_;
+}
+
+inline void options::
+guard_prefix (const std::string& x)
+{
+ this->guard_prefix_ = x;
+}
+
+inline bool options::
+guard_prefix_specified () const
+{
+ return this->guard_prefix_specified_;
+}
+
+inline void options::
+guard_prefix_specified (bool x)
+{
+ this->guard_prefix_specified_ = x;
+}
+
+inline const bool& options::
+show_sloc () const
+{
+ return this->show_sloc_;
+}
+
+inline bool& options::
+show_sloc ()
+{
+ return this->show_sloc_;
+}
+
+inline void options::
+show_sloc (const bool& x)
+{
+ this->show_sloc_ = x;
+}
+
+inline const std::size_t& options::
+sloc_limit () const
+{
+ return this->sloc_limit_;
+}
+
+inline std::size_t& options::
+sloc_limit ()
+{
+ return this->sloc_limit_;
+}
+
+inline void options::
+sloc_limit (const std::size_t& x)
+{
+ this->sloc_limit_ = x;
+}
+
+inline bool options::
+sloc_limit_specified () const
+{
+ return this->sloc_limit_specified_;
+}
+
+inline void options::
+sloc_limit_specified (bool x)
+{
+ this->sloc_limit_specified_ = x;
+}
+
+inline const std::string& options::
+options_file () const
+{
+ return this->options_file_;
+}
+
+inline std::string& options::
+options_file ()
+{
+ return this->options_file_;
+}
+
+inline void options::
+options_file (const std::string& x)
+{
+ this->options_file_ = x;
+}
+
+inline bool options::
+options_file_specified () const
+{
+ return this->options_file_specified_;
+}
+
+inline void options::
+options_file_specified (bool x)
+{
+ this->options_file_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+x () const
+{
+ return this->x_;
+}
+
+inline std::vector<std::string>& options::
+x ()
+{
+ return this->x_;
+}
+
+inline void options::
+x (const std::vector<std::string>& x)
+{
+ this->x_ = x;
+}
+
+inline bool options::
+x_specified () const
+{
+ return this->x_specified_;
+}
+
+inline void options::
+x_specified (bool x)
+{
+ this->x_specified_ = x;
+}
+
+inline const bool& options::
+v () const
+{
+ return this->v_;
+}
+
+inline bool& options::
+v ()
+{
+ return this->v_;
+}
+
+inline void options::
+v (const bool& x)
+{
+ this->v_ = x;
+}
+
+inline const bool& options::
+trace () const
+{
+ return this->trace_;
+}
+
+inline bool& options::
+trace ()
+{
+ return this->trace_;
+}
+
+inline void options::
+trace (const bool& x)
+{
+ this->trace_ = x;
+}
+
+inline const std::string& options::
+mysql_engine () const
+{
+ return this->mysql_engine_;
+}
+
+inline std::string& options::
+mysql_engine ()
+{
+ return this->mysql_engine_;
+}
+
+inline void options::
+mysql_engine (const std::string& x)
+{
+ this->mysql_engine_ = x;
+}
+
+inline bool options::
+mysql_engine_specified () const
+{
+ return this->mysql_engine_specified_;
+}
+
+inline void options::
+mysql_engine_specified (bool x)
+{
+ this->mysql_engine_specified_ = x;
+}
+
+inline const bool& options::
+sqlite_override_null () const
+{
+ return this->sqlite_override_null_;
+}
+
+inline bool& options::
+sqlite_override_null ()
+{
+ return this->sqlite_override_null_;
+}
+
+inline void options::
+sqlite_override_null (const bool& x)
+{
+ this->sqlite_override_null_ = x;
+}
+
+inline const bool& options::
+sqlite_lax_auto_id () const
+{
+ return this->sqlite_lax_auto_id_;
+}
+
+inline bool& options::
+sqlite_lax_auto_id ()
+{
+ return this->sqlite_lax_auto_id_;
+}
+
+inline void options::
+sqlite_lax_auto_id (const bool& x)
+{
+ this->sqlite_lax_auto_id_ = x;
+}
+
+inline const ::pgsql_version& options::
+pgsql_server_version () const
+{
+ return this->pgsql_server_version_;
+}
+
+inline ::pgsql_version& options::
+pgsql_server_version ()
+{
+ return this->pgsql_server_version_;
+}
+
+inline void options::
+pgsql_server_version (const ::pgsql_version& x)
+{
+ this->pgsql_server_version_ = x;
+}
+
+inline bool options::
+pgsql_server_version_specified () const
+{
+ return this->pgsql_server_version_specified_;
+}
+
+inline void options::
+pgsql_server_version_specified (bool x)
+{
+ this->pgsql_server_version_specified_ = x;
+}
+
+inline const ::oracle_version& options::
+oracle_client_version () const
+{
+ return this->oracle_client_version_;
+}
+
+inline ::oracle_version& options::
+oracle_client_version ()
+{
+ return this->oracle_client_version_;
+}
+
+inline void options::
+oracle_client_version (const ::oracle_version& x)
+{
+ this->oracle_client_version_ = x;
+}
+
+inline bool options::
+oracle_client_version_specified () const
+{
+ return this->oracle_client_version_specified_;
+}
+
+inline void options::
+oracle_client_version_specified (bool x)
+{
+ this->oracle_client_version_specified_ = x;
+}
+
+inline const bool& options::
+oracle_warn_truncation () const
+{
+ return this->oracle_warn_truncation_;
+}
+
+inline bool& options::
+oracle_warn_truncation ()
+{
+ return this->oracle_warn_truncation_;
+}
+
+inline void options::
+oracle_warn_truncation (const bool& x)
+{
+ this->oracle_warn_truncation_ = x;
+}
+
+inline const ::mssql_version& options::
+mssql_server_version () const
+{
+ return this->mssql_server_version_;
+}
+
+inline ::mssql_version& options::
+mssql_server_version ()
+{
+ return this->mssql_server_version_;
+}
+
+inline void options::
+mssql_server_version (const ::mssql_version& x)
+{
+ this->mssql_server_version_ = x;
+}
+
+inline bool options::
+mssql_server_version_specified () const
+{
+ return this->mssql_server_version_specified_;
+}
+
+inline void options::
+mssql_server_version_specified (bool x)
+{
+ this->mssql_server_version_specified_ = x;
+}
+
+inline const unsigned int& options::
+mssql_short_limit () const
+{
+ return this->mssql_short_limit_;
+}
+
+inline unsigned int& options::
+mssql_short_limit ()
+{
+ return this->mssql_short_limit_;
+}
+
+inline void options::
+mssql_short_limit (const unsigned int& x)
+{
+ this->mssql_short_limit_ = x;
+}
+
+inline bool options::
+mssql_short_limit_specified () const
+{
+ return this->mssql_short_limit_specified_;
+}
+
+inline void options::
+mssql_short_limit_specified (bool x)
+{
+ this->mssql_short_limit_specified_ = x;
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/odb/odb/processor.cxx b/odb/odb/processor.cxx
new file mode 100644
index 0000000..d48baa7
--- /dev/null
+++ b/odb/odb/processor.cxx
@@ -0,0 +1,3255 @@
+// file : odb/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <iostream>
+#include <algorithm> // std::find
+
+#include <odb/common.hxx>
+#include <odb/lookup.hxx>
+#include <odb/context.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/processor.hxx>
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace
+{
+ // Find name hint for this type decl.
+ //
+ static semantics::names*
+ find_hint (semantics::unit& u, tree decl)
+ {
+ semantics::names* r (0);
+
+ for (tree ot (DECL_ORIGINAL_TYPE (decl));
+ ot != 0;
+ ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
+ {
+ if ((r = u.find_hint (ot)))
+ break;
+
+ decl = TYPE_NAME (ot);
+ }
+
+ return r;
+ }
+
+ // Indirect (dynamic) context values.
+ //
+ static semantics::type*
+ id_tree_type (semantics::names*& hint)
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return &c.utype (id, hint);
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // See if this member is a section.
+ //
+ if (t.fq_name () == "::odb::section")
+ {
+ using semantics::class_;
+
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+ class_* poly_root (polymorphic (c));
+ semantics::data_member* opt (optimistic (c));
+
+ // If we have sections in a polymorphic optimistic hierarchy,
+ // then the version member should be in the root.
+ //
+ if (poly_root == &c && opt != 0 && &opt->scope () != &c)
+ {
+ error (m.location ()) << "version must be a direct data member " <<
+ "of a class that contains sections" << endl;
+ info (opt->location ()) << "version member is declared here" << endl;
+ throw operation_failed ();
+ }
+
+ process_user_section (m, c);
+
+ // We don't need a modifier but the accessor should be by-reference.
+ //
+ process_access (m, "get");
+
+ member_access& ma (m.get<member_access> ("get"));
+ if (ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a section" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // Mark this member as transient since we don't store it in the
+ // database.
+ //
+ m.set ("transient", true);
+
+ features.section = true;
+ return;
+ }
+ else
+ {
+ process_access (m, "get");
+ process_access (m, "set");
+ }
+
+ // See if this member belongs to a section.
+ //
+ if (m.count ("section-member") != 0)
+ process_section_member (m);
+
+ // We don't need to do any further processing for common if we
+ // are generating static multi-database code.
+ //
+ if (multi_static && options.database ()[0] == database::common)
+ return;
+
+ // Handle wrappers.
+ //
+ semantics::type* wt (0), *qwt (0);
+ semantics::names* whint (0);
+ if (process_wrapper (t))
+ {
+ qwt = t.get<semantics::type*> ("wrapper-type");
+ whint = t.get<semantics::names*> ("wrapper-hint");
+ wt = &utype (*qwt, whint);
+ }
+
+ // If the type is const and the member is not id, version, or
+ // inverse, then mark it as readonly. In case of a wrapper,
+ // both the wrapper type and the wrapped type must be const.
+ // To see why, consider these possibilities:
+ //
+ // unique_ptr<const T> - can modify by setting a new pointer
+ // const unique_ptr<T> - can modify by changing the pointed-to value
+ //
+ if (const_member (m) && !(id (m) || version (m) || m.count ("inverse")))
+ {
+ if (qwt == 0 || const_type (*qwt))
+ m.set ("readonly", true);
+ }
+
+ process_points_to (m);
+
+ if (composite_wrapper (t))
+ return;
+
+ // Process object pointer. The resulting column will be a simple
+ // or composite value.
+ //
+ if (process_object_pointer (m, t))
+ return;
+
+ // Before checking if this is a container, check if this member
+ // or its type were deduced to be a simple value based on the
+ // pragmas. This is necessary because a container member (e.g.,
+ // vector<char>) can be "overridden" into a simple value (e.g.,
+ // BLOB) with a pragma.
+ //
+ if (m.count ("simple") ||
+ t.count ("simple") ||
+ (wt != 0 && wt->count ("simple")))
+ return;
+
+ process_container (m, (wt != 0 ? *wt : t));
+ }
+
+ //
+ // Process member access expressions.
+ //
+
+ enum found_type
+ {
+ found_none,
+ found_some, // Found something but keep looking for a better one.
+ found_best
+ };
+
+ // Check if a function is a suitable accessor for this member.
+ //
+ found_type
+ check_accessor (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ // Must be const.
+ //
+ if (!DECL_CONST_MEMFUNC_P (f))
+ return found_none;
+
+ // Accessor is a function with no arguments (other than 'this').
+ //
+ if (FUNCTION_FIRST_USER_PARMTYPE (f) != void_list_node)
+ return found_none;
+
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // In the strict mode make sure the function returns for non-array
+ // types a value or a (const) reference to the member type and for
+ // array types a (const) pointer to element type. In the lax mode
+ // we just check that the return value is not void.
+ //
+ if (strict)
+ {
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+ else if (r == void_type_node)
+ return found_none;
+
+ cxx_tokens& e (ma.expr);
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // See if it returns by value.
+ //
+ ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE);
+
+ return found_best;
+ }
+
+ // Check if a function is a suitable modifier for this member.
+ //
+ found_type
+ check_modifier (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ tree a (FUNCTION_FIRST_USER_PARMTYPE (f));
+
+ // For a modifier, it can either be a function that returns a non-
+ // const reference (or non-const pointer, in case the member is an
+ // array) or a by-value modifier that sets a new value. If both are
+ // available, we prefer the former for efficiency.
+ //
+ cxx_tokens& e (ma.expr);
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (a == void_list_node)
+ {
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // By-reference modifier. Should return a reference or a pointer.
+ //
+ if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE))
+ return found_none;
+
+ // The base type should not be const and, in strict mode, should
+ // match the member type.
+ //
+ tree bt (TREE_TYPE (r));
+
+ if (CP_TYPE_CONST_P (bt))
+ return found_none;
+
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+
+ e.clear (); // Could contain by value modifier.
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ return found_best;
+ }
+ // Otherwise look for a by value modifier, which is a function
+ // with a single argument.
+ //
+ else if (TREE_CHAIN (a) == void_list_node)
+ {
+ // In the lax mode any function with a single argument works
+ // for us. And we don't care what it returns.
+ //
+ if (strict)
+ {
+ // In the strict mode make sure the argument matches the
+ // member. This is exactly the same logic as in accessor
+ // with regards to arrays, references, etc.
+ //
+ tree at (TREE_VALUE (a));
+ gcc_tree_code_type tc (TREE_CODE (at));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // Continue searching in case there is version that returns a
+ // non-const reference which we prefer for efficiency.
+ //
+ return found_some;
+ }
+ else
+ return found_none; // We didn't find anything better.
+ }
+
+ return found_none;
+ }
+
+ void
+ process_access (semantics::data_member& m, std::string const& k)
+ {
+ bool virt (m.count ("virtual"));
+
+ // Ignore certain special virtual members.
+ //
+ if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator")))
+ return;
+
+ char const* kind (k == "get" ? "accessor" : "modifier");
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+
+ // If we don't have an access expression, try to come up with
+ // one.
+ //
+ if (!m.count (k))
+ {
+ found_type found (found_none);
+ semantics::access const& a (m.named ().access ());
+ member_access& ma (
+ m.set (
+ k, member_access (m.location (), kind, true)));
+
+ // If this member is not virtual and is either public or if we
+ // are a friend of this class, then go for the member directly.
+ //
+ if (!virt && (a == semantics::access::public_ ||
+ c.get<bool> ("friend")))
+ {
+ ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ ma.expr.push_back (cxx_token (0, CPP_DOT));
+ ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ()));
+ found = found_best;
+ }
+
+ // Otherwise, try to find a suitable accessor/modifier.
+ //
+
+ // First try the original name. If that doesn't produce anything,
+ // then try the public name.
+ //
+ bool t (k == "get"
+ ? options.accessor_regex_trace ()
+ : options.modifier_regex_trace ());
+ regex_mapping const& re (
+ k == "get" ? accessor_regex : modifier_regex);
+
+ for (unsigned short j (0); found != found_best && j != 2; ++j)
+ {
+ string b (j == 0 ? m.name () : public_name (m, false));
+
+ // Skip the second pass if original and public names are the same.
+ //
+ if (j == 1 && b == m.name ())
+ continue;
+
+ if (t)
+ cerr << kind << (j == 0 ? " original" : " public")
+ << " name '" << b << "'" << endl;
+
+ for (regex_mapping::const_iterator i (re.begin ());
+ found != found_best && i != re.end ();
+ ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (!i->match (b))
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ string n (i->replace (b));
+
+ if (t)
+ cerr << "'" << n << "' : ";
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in public non-static member
+ // functions. Note that TREE_PUBLIC() returns something
+ // other than what we need.
+ //
+ if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) ||
+ TREE_PRIVATE (f) || TREE_PROTECTED (f))
+ continue;
+
+ found_type r (k == "get"
+ ? check_accessor (m, f, n, ma, true)
+ : check_modifier (m, f, n, ma, true));
+
+ if (r != found_none)
+ {
+ // Update the location of the access expression to point
+ // to this function.
+ //
+ ma.loc = location (real_source_location (f));
+ found = r;
+ }
+ }
+
+ if (t)
+ cerr << (found != found_none ? '+' : '-') << endl;
+ }
+ }
+
+ // If that didn't work then the generated code won't be able
+ // to access this member.
+ //
+ if (found == found_none)
+ {
+ location const& l (m.location ());
+
+ if (virt)
+ {
+ error (l) << "no suitable " << kind << " function could be "
+ << "automatically found for virtual data member '"
+ << m.name () << "'" << endl;
+
+ info (l) << "use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+ else
+ {
+ error (l) << "data member '" << m.name () << "' is "
+ << a.string () << " and no suitable " << kind
+ << " function could be automatically found" << endl;
+
+ info (l) << "consider making class 'odb::access' a friend of "
+ << "class '" << class_name (c) << "'" << endl;
+
+ info (l) << "or use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+
+ throw operation_failed ();
+ }
+ }
+
+ member_access& ma (m.get<member_access> (k));
+
+ if (ma.empty ())
+ return;
+
+ cxx_tokens& e (ma.expr);
+
+ // If it is just a name, resolve it and convert to an appropriate
+ // expression.
+ //
+ if (e.size () == 1 && e.back ().type == CPP_NAME)
+ {
+ string n (e.back ().literal);
+ e.clear ();
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node)
+ {
+ error (ma.loc) << "unable to resolve data member or function "
+ << "name '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case FIELD_DECL:
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ break;
+ }
+ case BASELINK:
+ {
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in non-static member functions.
+ //
+ if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f))
+ continue;
+
+ if ((k == "get"
+ ? check_accessor (m, f, n, ma, false)
+ : check_modifier (m, f, n, ma, false)) == found_best)
+ break;
+ }
+
+ if (e.empty ())
+ {
+ error (ma.loc) << "unable to find suitable " << kind
+ << " function '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+ break;
+ }
+ default:
+ {
+ error (ma.loc) << "name '" << n << "' does not refer to a data "
+ << "member or function" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ // If there is no 'this' keyword, then add it as a prefix.
+ //
+ {
+ bool t (false);
+ for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ if (i->type == CPP_KEYWORD && i->literal == "this")
+ {
+ t = true;
+ break;
+ }
+ }
+
+ if (!t)
+ {
+ e.insert (e.begin (), cxx_token (0, CPP_DOT));
+ e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this"));
+ }
+ }
+
+ // Check that there is no placeholder in the accessor expression.
+ //
+ if (k == "get" && ma.placeholder ())
+ {
+ error (ma.loc) << "(?) placeholder in the accessor expression"
+ << endl;
+ throw operation_failed ();
+ }
+
+ // Check that the member type is default-constructible if we
+ // have a by value modifier.
+ //
+ if (k == "set" && ma.placeholder ())
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&utype (m)));
+
+ // Assume all other types are default-constructible.
+ //
+ if (c != 0)
+ {
+ // If this type is a class template instantiation, then make
+ // sure it is instantiated. While types used in real members
+ // will be instantiated, this is not necessarily the case for
+ // virtual members. Without the instantiation we won't be able
+ // to detect whether the type has the default ctor.
+ //
+ // It would have been cleaner to do it in post_process_pragmas()
+ // but there we don't yet know whether we need the default ctor.
+ // And it is a good idea not to require instantiability unless
+ // we really need it.
+ //
+ tree type (c->tree_node ());
+
+ if (!COMPLETE_TYPE_P (type) &&
+ CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+ {
+ // Reset input location so that we get nice diagnostics in
+ // case of an error. Use the location of the virtual pragma.
+ //
+ location_t loc (m.get<location_t> ("virtual-location"));
+ input_location = loc;
+
+ if (instantiate_class_template (type) == error_mark_node ||
+ errorcount != 0 ||
+ !COMPLETE_TYPE_P (type))
+ {
+ error (loc) << "unable to instantiate virtual data member " <<
+ "type" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (!c->default_ctor ())
+ {
+ error (ma.loc) << "modifier expression requires member type " <<
+ "to be default-constructible" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+
+ //
+ // Process section.
+ //
+
+ user_section&
+ process_user_section (semantics::data_member& m, semantics::class_& c)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ user_section::load_type l (
+ m.get ("section-load", user_section::load_eager));
+
+ user_section::update_type u (
+ m.get ("section-update", user_section::update_always));
+
+ if (l == user_section::load_eager && u == user_section::update_always)
+ {
+ location const& l (m.location ());
+
+ error (l) << "eager-loaded, always-updated section is pointless"
+ << endl;
+
+ info (l) << "use '#pragma db load' and/or '#pragma db update' to "
+ "specify an alternative loading and/or updating strategy" << endl;
+
+ info (l) << "or remove the section altogether" << endl;
+
+ throw operation_failed ();
+ }
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version));
+ user_section us (m, c, n, l, u);
+
+ // We may already have seen this section (e.g., forward reference
+ // from a member of this section).
+ //
+ user_sections::iterator i (find (uss.begin (), uss.end (), us));
+
+ if (i != uss.end ())
+ return *i;
+
+ // If we are adding a new section to an optimistic class with
+ // version in a base, make sure the base is sectionable.
+ //
+ semantics::data_member* opt (optimistic (c));
+ if (opt != 0 && &opt->scope () != &c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ semantics::node* base (poly_root ? poly_root : &opt->scope ());
+
+ if (!base->count ("sectionable"))
+ {
+ error (m.location ()) << "adding new section to a derived class " <<
+ "in an optimistic hierarchy requires sectionable base class" <<
+ endl;
+
+ info (base->location ()) << "use '#pragma db object sectionable' " <<
+ "to make the base class of this hierarchy sectionable" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ uss.push_back (us);
+ return uss.back ();
+ }
+
+ void
+ process_section_member (semantics::data_member& m)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ string name (m.get<string> ("section-member"));
+ location_t loc (m.get<location_t> ("section-member-location"));
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ try
+ {
+ data_member& us (c.lookup<data_member> (name, class_::include_hidden));
+
+ // Make sure we are referencing a section.
+ //
+ if (utype (us).fq_name () != "::odb::section")
+ {
+ error (loc) << "data member '" << name << "' in '#pragma db " <<
+ "section' is not of the odb::section type" << endl;
+ throw operation_failed ();
+ }
+
+ // If the section is in the base, handle polymorphic inheritance.
+ //
+ class_& b (dynamic_cast<class_&> (us.scope ()));
+ user_section* s (0);
+
+ if (&c != &b && poly_derived)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // This is a section override. See if we have already handled
+ // this section.
+ //
+ for (user_sections::iterator i (uss.begin ());
+ s == 0 && i != uss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ s = &*i;
+ }
+
+ // Otherwise, find and copy the nearest override in the base.
+ // The result should be a chain of overrides leading all the
+ // way to the original section.
+ //
+ if (s == 0)
+ {
+ for (class_* b (&polymorphic_base (c));;
+ b = &polymorphic_base (*b))
+ {
+ user_sections& buss (b->get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (buss.begin ());
+ s == 0 && i != buss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ {
+ uss.push_back (*i);
+ uss.back ().object = &c;
+ uss.back ().base = &*i;
+ s = &uss.back ();
+ }
+ }
+
+ if (s != 0)
+ break;
+
+ assert (b != poly_root); // We should have found it by now.
+ }
+ }
+ }
+ else
+ s = &process_user_section (us, c);
+
+ // Mark the member as added/deleted if the section is added/deleted.
+ // Also check that the version ordering is correct.
+ //
+ if (unsigned long long sav = added (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("added-location"));
+
+ if (unsigned long long mav = added (m))
+ {
+ location_t ml (m.get<location_t> ("added-location"));
+
+ if (mav < sav)
+ {
+ error (ml) << "member addition version is less than the " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mav == sav)
+ {
+ error (ml) << "member addition version is the same as " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("added", sav);
+ m.set ("added-location", sl);
+ }
+ }
+
+ if (unsigned long long sdv = deleted (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("deleted-location"));
+
+ if (unsigned long long mdv = deleted (m))
+ {
+ location_t ml (m.get<location_t> ("deleted-location"));
+
+ if (mdv > sdv)
+ {
+ error (ml) << "member deletion version is greater than the " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mdv == sdv)
+ {
+ error (ml) << "member deletion version is the same as " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("deleted", sdv);
+ m.set ("deleted-location", sl);
+ }
+ }
+
+ // Insert as object_section.
+ //
+ m.set ("section", static_cast<object_section*> (s));
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (loc) << "name '" << name << "' in '#pragma db section' " <<
+ "does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << name << "' " <<
+ "specified with '#pragma db section'" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << name << "' specified " <<
+ "with '#pragma db section' is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to " <<
+ "this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ //
+ // Process wrapper.
+ //
+
+ bool
+ process_wrapper (semantics::type& t)
+ {
+ if (t.count ("wrapper"))
+ return t.get<bool> ("wrapper");
+
+ // Check this type with wrapper_traits.
+ //
+ tree inst (instantiate_template (wrapper_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Get the wrapped type.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("wrapped_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ // The wrapped_type alias is a typedef in an instantiation
+ // that we just instantiated dynamically. As a result there
+ // is no semantic graph edges corresponding to this typedef
+ // since we haven't parsed it yet (unless it was instantiated
+ // explicitly by the user; see below). So to get the tree node
+ // that can actually be resolved to the graph node, we use
+ // the source type of this typedef.
+ //
+ tree type (DECL_ORIGINAL_TYPE (decl));
+
+ bool qc (CP_TYPE_CONST_P (type));
+ bool qv (CP_TYPE_VOLATILE_P (type));
+ bool qr (CP_TYPE_RESTRICT_P (type));
+
+ type = TYPE_MAIN_VARIANT (type);
+ semantics::type* wt (
+ dynamic_cast<semantics::type*> (unit.find (type)));
+
+ // Object pointers and wrappers often use the same smart
+ // pointers so check if the wrapped type is an object.
+ //
+ if (object (*wt))
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ if (qc || qv || qr)
+ {
+ for (semantics::type::qualified_iterator i (wt->qualified_begin ());
+ i != wt->qualified_end (); ++i)
+ {
+ semantics::qualifier& q (i->qualifier ());
+
+ if (q.const_ () == qc &&
+ q.volatile_ () == qv &&
+ q.restrict_ () == qr)
+ {
+ wt = &q;
+ break;
+ }
+ }
+ }
+
+ // Find the hint.
+ //
+ // If we can't find any, then try to fallback to the wrapped_type
+ // alias inside wrapper_traits. This requires an explicit
+ // wrapper_traits instantiation (see above).
+ //
+ semantics::names* wh (find_hint (unit, decl));
+
+ if (wh == nullptr)
+ wh = unit.find_hint (TREE_TYPE (decl));
+
+ t.set ("wrapper-type", wt);
+ t.set ("wrapper-hint", wh);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "wrapped_type type" << endl;
+ throw;
+ }
+
+ // Get the null_handler flag.
+ //
+ bool null_handler (false);
+
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_handler"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ null_handler = static_cast<bool> (integer_value (init));
+ t.set ("wrapper-null-handler", null_handler);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_handler constant" << endl;
+ throw;
+ }
+
+ // Get the null_default flag.
+ //
+ if (null_handler)
+ {
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_default"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("wrapper-null-default",
+ static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_default constant" << endl;
+ throw;
+ }
+ }
+
+ // Check if the wrapper is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+
+ t.set ("wrapper", true);
+ return true;
+ }
+
+ //
+ // Process object pointer.
+ //
+
+ semantics::class_*
+ process_object_pointer (semantics::data_member& m,
+ semantics::type& t,
+ string const& kp = string ())
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* c (0);
+
+ // The overall idea is as follows: try to instantiate the pointer
+ // traits class template. If we are successeful, then get the
+ // element type and see if it is an object.
+ //
+ if (t.count ("element-type"))
+ c = t.get<class_*> ("element-type");
+ else
+ {
+ tree inst (instantiate_template (pointer_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return 0;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string fl (DECL_SOURCE_FILE (decl));
+ size_t ln (DECL_SOURCE_LINE (decl));
+ size_t cl (DECL_SOURCE_COLUMN (decl));
+
+ // Get the element type.
+ //
+ tree tn (0);
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("element_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ tn = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
+
+ // Check if the pointer is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'element_type' type"
+ << endl;
+ throw;
+ }
+
+ c = dynamic_cast<class_*> (unit.find (tn));
+
+ if (c == 0 || !object (*c))
+ return 0;
+
+ t.set ("element-type", c);
+
+ // Determine the pointer kind.
+ //
+ try
+ {
+ tree kind (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (kind == error_mark_node || TREE_CODE (kind) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (kind) &&
+ !DECL_TEMPLATE_INSTANTIATED (kind) &&
+ !DECL_EXPLICIT_INSTANTIATION (kind))
+ instantiate_decl (kind, false, false);
+
+ tree init (DECL_INITIAL (kind));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ pointer_kind_type pk = static_cast<pointer_kind_type> (
+ integer_value (init));
+ t.set ("pointer-kind", pk);
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+
+ // Get the lazy flag.
+ //
+ try
+ {
+ tree lazy (
+ lookup_qualified_name (
+ inst, get_identifier ("lazy"), false, false));
+
+ if (lazy == error_mark_node || TREE_CODE (lazy) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (lazy) &&
+ !DECL_TEMPLATE_INSTANTIATED (lazy) &&
+ !DECL_EXPLICIT_INSTANTIATION (lazy))
+ instantiate_decl (lazy, false, false);
+
+ tree init (DECL_INITIAL (lazy));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("pointer-lazy", static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+ }
+
+ // See if this is the inverse side of a bidirectional relationship.
+ // If so, then resolve the member path and cache it in the context.
+ //
+ if (m.count ("inverse"))
+ {
+ string name (m.get<string> ("inverse"));
+ location_t l (m.get<location_t> ("inverse-location"));
+ data_member_path mp (resolve_data_members (*c, name, l, lex_));
+
+ {
+ string tl;
+ data_member& m (*mp.back ());
+
+ if (container (m) && lex_.next (tl) != CPP_EOF)
+ {
+ error (l) << "unexpect name after container member " <<
+ m.name () << " in '#pragma db inverse'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // Validate each member.
+ //
+ for (data_member_path::iterator i (mp.begin ()); i != mp.end (); ++i)
+ {
+ data_member& im (**i);
+ const string& n (im.name ());
+
+ if (im.count ("transient"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is transient" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (im.count ("inverse") || im.count ("value-inverse"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is itself inverse" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // @@ Would be good to check that the other end is actually
+ // an object pointer/points_to and points to the correct
+ // object. But the other class may not have been processed
+ // yet. Need to do in validator, pass 2.
+ //
+ m.remove ("inverse");
+ m.set (kp + (kp.empty () ? "": "-") + "inverse", mp);
+ }
+
+ return c;
+ }
+
+ //
+ // Process points-to pragma.
+ //
+
+ void
+ process_points_to (semantics::data_member& m,
+ string const& /*kp*/ = string ())
+ {
+ if (!m.count ("points-to"))
+ return;
+
+ using semantics::class_;
+
+ tree t (m.get<tree> ("points-to"));
+ location_t l (m.get<location_t> ("points-to-location"));
+
+ class_* c (dynamic_cast<class_*> (unit.find (t)));
+
+ if (c == 0 || !object (*c))
+ {
+ error (l) << "name specified with '#pragma db points_to' does "
+ << "not refer to an object" << endl;
+ throw operation_failed ();
+ }
+
+ m.remove ("points-to");
+ m.set (/*kp + (kp.empty () ? "": "-") + */"points-to", c);
+ }
+
+ //
+ // Process container.
+ //
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ if (obj_ptr)
+ process_object_pointer (m, t, prefix);
+ }
+
+ bool
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ // The overall idea is as follows: try to instantiate the container
+ // traits class template. If we are successeful, then this is a
+ // container type and we can extract the various information from
+ // the instantiation. Otherwise, this is not a container.
+ //
+ location ml (m.location ());
+
+ container_kind_type ck;
+ bool smart;
+ semantics::type* vt (0);
+ semantics::type* it (0);
+ semantics::type* kt (0);
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ if (t.count ("container-kind"))
+ {
+ ck = t.get<container_kind_type> ("container-kind");
+ smart = t.get<bool> ("container-smart");
+
+ vt = &utype (m, vh, "value");
+
+ if (ck == ck_ordered)
+ it = &utype (m, ih, "index");
+
+ if (ck == ck_map || ck == ck_multimap)
+ kt = &utype (m, kh, "key");
+ }
+ else
+ {
+ tree inst (instantiate_template (container_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return false;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Determine the container kind.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ ck = static_cast<container_kind_type> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "container kind constant" << endl;
+
+ throw;
+ }
+
+ t.set ("container-kind", ck);
+
+ // See if it is a smart container.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("smart"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ smart = static_cast<bool> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "'smart' constant" << endl;
+ throw;
+ }
+
+ // For now we only support ordered smart containers.
+ //
+ if (smart && ck != ck_ordered)
+ {
+ os << f << ":" << l << ":" << c << ": error: only ordered smart " <<
+ "containers are currently supported" << endl;
+ throw operation_failed ();
+ }
+
+ t.set ("container-smart", smart);
+
+ // Mark id column as not null.
+ //
+ t.set ("id-not-null", true);
+
+ // Get the value type.
+ //
+ {
+ tree decl (lookup_qualified_name (
+ inst, get_identifier ("value_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "value_type type" << endl;
+
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ vt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container value type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ vh = find_hint (unit, decl);
+ }
+
+
+ t.set ("value-tree-type", vt);
+ t.set ("value-tree-hint", vh);
+ vt = &utype (m, vh, "value"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("value-null") && vt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container value declared null while its type "
+ << "is declared not null" << endl;
+ }
+
+ // Get the index type for ordered containers.
+ //
+ if (ck == ck_ordered)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("index_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "index_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ it = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container index type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ ih = find_hint (unit, decl);
+
+ t.set ("index-not-null", true);
+ t.set ("index-tree-type", it);
+ t.set ("index-tree-hint", ih);
+ it = &utype (m, ih, "index"); // Map.
+ }
+
+ // Get the key type for maps.
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("key_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "key_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ kt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container key type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ kh = find_hint (unit, decl);
+
+ t.set ("key-tree-type", kt);
+ t.set ("key-tree-hint", kh);
+ kt = &utype (m, kh, "key"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("key-null") && kt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container key declared null while its type "
+ << "is declared not null" << endl;
+ }
+ }
+
+ // Determine if container value/index/key types are wrappers.
+ //
+ process_wrapper (*vt);
+
+ if (it != 0)
+ process_wrapper (*it);
+
+ if (kt != 0)
+ process_wrapper (*kt);
+
+ // Check if we are versioned. For now we are not allowing for
+ // soft-add/delete in container keys (might be used in WHERE,
+ // primary key).
+ //
+ {
+ semantics::class_* comp (0);
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ comp = composite_wrapper (*kt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+
+ error (ml) << "map key type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (kt->location ()) << "key type is defined here" << endl;
+ throw operation_failed ();
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ comp = composite_wrapper (*vt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = 0;
+ break;
+ }
+
+ error (ml) << "set value type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (vt->location ()) << "value type is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (force_versioned || (comp != 0 && column_count (*comp).soft != 0))
+ t.set ("versioned", true);
+ }
+ }
+
+ // Process member data.
+ //
+ m.set ("id-tree-type", &id_tree_type);
+
+ // Has to be first to handle inverse.
+ //
+ process_container_value (*vt, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, m, "key", true);
+
+ // A map cannot be an inverse container.
+ //
+ if (m.count ("value-inverse") && (ck == ck_map || ck == ck_multimap))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse container cannot be a map" << endl;
+ throw operation_failed ();
+ }
+
+ // If this is an inverse side of a bidirectional object relationship
+ // and it is an ordered container, mark it as unordred since there is
+ // no concept of order in this construct.
+ //
+ if (ck == ck_ordered && m.count ("value-inverse"))
+ m.set ("unordered", true);
+
+ // Issue an error if we have a non-inverse smart unordered container.
+ //
+ if (smart && ck == ck_ordered && unordered (m) &&
+ !m.count ("value-inverse"))
+ {
+ error (ml) << "smart ordered container cannot be unordered" << endl;
+ throw operation_failed ();
+ }
+
+ // Issue a warning if we are relaxing null-ness in the member.
+ //
+ if (m.count ("value-null") &&
+ (t.count ("value-not-null") || vt->count ("not-null")))
+ {
+ warn (ml) << "container value declared null while the container "
+ << "type or value type declares it as not null" << endl;
+ }
+
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ if (m.count ("key-null") &&
+ (t.count ("key-not-null") || kt->count ("not-null")))
+ {
+ warn (ml) << "container key declared null while the container "
+ << "type or key type declares it as not null" << endl;
+ }
+ }
+
+ return true;
+ }
+
+ //
+ // Implementation details (c-tor, helpers).
+ //
+
+ data_member ()
+ {
+ // Find the odb namespace.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find wrapper traits.
+ //
+ wrapper_traits_ = lookup_qualified_name (
+ odb, get_identifier ("wrapper_traits"), true, false);
+
+ if (wrapper_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (wrapper_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve wrapper_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find pointer traits.
+ //
+ pointer_traits_ = lookup_qualified_name (
+ odb, get_identifier ("pointer_traits"), true, false);
+
+ if (pointer_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (pointer_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve pointer_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find the access class.
+ //
+ tree access = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ if (access == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve access class"
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ access = TREE_TYPE (access);
+
+ // Find container_traits.
+ //
+ container_traits_ = lookup_qualified_name (
+ access, get_identifier ("container_traits"), true, false);
+
+ if (container_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (container_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve container_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ static tree
+ instantiate_template (tree t, tree arg)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = arg;
+
+ // This step should succeed regardles of whether there is a
+ // specialization for this type.
+ //
+ tree inst (
+ lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error));
+
+ if (inst == error_mark_node)
+ {
+ // Diagnostics has already been issued by lookup_template_class.
+ //
+ throw operation_failed ();
+ }
+
+ inst = TYPE_MAIN_VARIANT (inst);
+
+ // The instantiation may already be complete if it matches a
+ // (complete) specialization or was used before.
+ //
+ if (!COMPLETE_TYPE_P (inst))
+ inst = instantiate_class_template (inst);
+
+ // If we cannot instantiate this type, assume there is no suitable
+ // specialization for it.
+ //
+ if (inst == error_mark_node || !COMPLETE_TYPE_P (inst))
+ return 0;
+
+ return inst;
+ }
+
+ private:
+ tree wrapper_traits_;
+ tree pointer_traits_;
+ tree container_traits_;
+
+ cxx_string_lexer lex_;
+ };
+
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map")) {}
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ location const& l (m.location ());
+
+ if (lazy_pointer (t))
+ {
+ error (l) << "lazy object pointer in view" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the corresponding associated object. First see if this
+ // data member name matches any aliases.
+ //
+ view_alias_map::iterator i (amap_.find (m.name ()));
+
+ if (i == amap_.end ())
+ i = amap_.find (public_name (m, false));
+
+ view_object* vo (0);
+
+ if (i != amap_.end ())
+ {
+ vo = i->second;
+
+ if (vo->obj != c) // @@ Poly base/derived difference?
+ {
+ error (l) << "different pointed-to and associated objects" << endl;
+ info (vo->loc) << "associated object is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ // If there is no alias match, try the object type.
+ //
+ view_object_map::iterator i (omap_.find (c));
+
+ if (i == omap_.end ())
+ {
+ error (l) << "unable to find associated object for object "
+ << "pointer" << endl;
+ info (l) << "use associated object alias as this data member "
+ << "name" << endl;
+ throw operation_failed ();
+ }
+
+ vo = i->second;
+ }
+
+ if (vo->ptr != 0)
+ {
+ location const& l2 (vo->ptr->location ());
+
+ error (l) << "associated object is already loaded via another "
+ << "object pointer" << endl;
+ info (l2) << "the other data member is defined here" << endl;
+ info (l2) << "use associated object alias as this data member "
+ << "name to load a different object" << endl;
+
+ throw operation_failed ();
+ }
+
+ vo->ptr = &m;
+ m.set ("view-object", vo);
+ }
+ }
+
+ private:
+ semantics::class_& view_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ };
+
+ // Figure out the "summary" added/deleted version for a composite
+ // value type.
+ //
+ struct summary_version: object_members_base
+ {
+ summary_version (): av (0), dv (0), a_ (true), d_ (true) {}
+
+ virtual void
+ traverse_simple (semantics::data_member&)
+ {
+ if (a_)
+ {
+ if (unsigned long long v = added (member_path_))
+ {
+ if (av == 0 || av < v)
+ av = v;
+ }
+ else
+ {
+ av = 0;
+ a_ = false;
+ }
+ }
+
+ if (d_)
+ {
+ if (unsigned long long v = deleted (member_path_))
+ {
+ if (dv == 0 || dv > v)
+ dv = v;
+ }
+ else
+ {
+ dv = 0;
+ d_ = false;
+ }
+ }
+ }
+
+ public:
+ unsigned long long av;
+ unsigned long long dv;
+
+ bool a_;
+ bool d_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true),
+ std_string_ (0),
+ std_string_hint_ (0),
+ access_ (0)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+
+ // Resolve the std::string type node.
+ //
+ using semantics::scope;
+
+ for (scope::names_iterator_pair ip (unit.find ("std"));
+ ip.first != ip.second; ++ip.first)
+ {
+ if (scope* ns = dynamic_cast<scope*> (&ip.first->named ()))
+ {
+ scope::names_iterator_pair jp (ns->find ("string"));
+
+ if (jp.first != jp.second)
+ {
+ std_string_ = dynamic_cast<semantics::type*> (
+ &jp.first->named ());
+ std_string_hint_ = &*jp.first;
+ break;
+ }
+ }
+ }
+
+ assert (std_string_ != 0); // No std::string?
+
+ // Resolve odb::access, if any.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb != error_mark_node)
+ {
+ access_ = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+
+ // Check if odb::access is a friend of this class.
+ //
+ c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_));
+
+ // Assign pointer.
+ //
+ if (k == class_object || k == class_view)
+ assign_pointer (c);
+
+ if (k == class_object)
+ traverse_object_pre (c);
+ else if (k == class_view)
+ traverse_view_pre (c);
+
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object_post (c);
+ else if (k == class_view)
+ traverse_view_post (c);
+ else if (k == class_composite)
+ traverse_composite_post (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object_pre (type& c)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* poly_root (polymorphic (c));
+
+ // Sections.
+ //
+ user_sections& uss (c.set ("user-sections", user_sections (c)));
+
+ // Copy sections from reuse bases. For polymorphic classes, sections
+ // are overridden.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ user_sections& buss (b.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator j (buss.begin ());
+ j != buss.end ();
+ ++j)
+ {
+ // Don't copy the special version update section.
+ //
+ if (j->special == user_section::special_version)
+ continue;
+
+ uss.push_back (*j);
+ uss.back ().object = &c;
+ uss.back ().base = &*j;
+ }
+ }
+ }
+ }
+
+ // Determine whether it is a session object.
+ //
+ if (!c.count ("session"))
+ {
+ // If this is a derived class in a polymorphic hierarchy,
+ // then it should have the same session value as the root.
+ //
+ if (poly_root != 0 && poly_root != &c)
+ c.set ("session", session (*poly_root));
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // the session value.
+ //
+ bool found (false);
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (ns->count ("session"))
+ {
+ c.set ("session", ns->get<bool> ("session"));
+ found = true;
+ break;
+ }
+
+ if (ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // If still not found, then use the default value.
+ //
+ if (!found)
+ c.set ("session", options.generate_session ());
+ }
+ }
+
+ if (session (c))
+ features.session_object = true;
+
+ if (poly_root != 0)
+ {
+ using namespace semantics;
+ using semantics::data_member;
+
+ data_member_path& id (*id_member (*poly_root));
+ data_member* idm (id.front ());
+
+ if (poly_root != &c)
+ {
+ // If we are a derived class in the polymorphic persistent
+ // class hierarchy, then add a synthesized virtual pointer
+ // member that points back to the root.
+ //
+ semantics::class_& base (polymorphic_base (c));
+
+ if (&base != poly_root)
+ idm = &dynamic_cast<data_member&> (base.names_begin ()->named ());
+
+ path const& f (idm->file ());
+ size_t l (idm->line ()), col (idm->column ());
+
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (f, l, col, tree (0)));
+ m.set ("virtual", true);
+
+ // Make it the first member in the class. This is important:
+ // we rely on the corrensponding foreign key to be first.
+ //
+ node_position<type, scope::names_iterator> np (c, c.names_end ());
+ unit.new_edge<semantics::names> (
+ np, m, idm->name (), access::public_);
+
+ // Use the raw pointer as this member's type.
+ //
+ if (!base.pointed_p ())
+ {
+ // Create the pointer type in the graph. The pointer node
+ // in GCC seems to always be present, even if not explicitly
+ // used in the translation unit.
+ //
+ tree t (base.tree_node ());
+ tree ptr (TYPE_POINTER_TO (t));
+ assert (ptr != 0);
+ ptr = TYPE_MAIN_VARIANT (ptr);
+ pointer& p (unit.new_node<pointer> (f, l, col, ptr));
+ unit.insert (ptr, p);
+ unit.new_edge<points> (p, base);
+ assert (base.pointed_p ());
+ }
+
+ unit.new_edge<belongs> (m, base.pointed ().pointer ());
+
+ // Mark it as a special kind of id.
+ //
+ m.set ("id", string ());
+ m.set ("polymorphic-ref", true);
+
+ // Make sure we also use the same column name as the root.
+ //
+ if (composite_wrapper (utype (id)))
+ m.set ("column", table_column (column_prefix (id, true).prefix));
+ else
+ m.set ("column", table_column (column_name (id)));
+ }
+ else
+ {
+ // If we are a root of the polymorphic persistent class hierarchy,
+ // then add a synthesized virtual member for the discriminator.
+ // Use the location of the polymorphic pragma as the location of
+ // this member.
+ //
+ location_t loc (c.get<location_t> ("polymorphic-location"));
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (
+ path (LOCATION_FILE (loc)),
+ LOCATION_LINE (loc),
+ LOCATION_COLUMN (loc),
+ tree (0)));
+ m.set ("virtual", true);
+
+ // Insert it after the id member (or first if this id comes
+ // from reuse-base).
+ //
+ node_position<type, scope::names_iterator> np (
+ c, c.find (idm->named ()));
+ unit.new_edge<semantics::names> (
+ np, m, "typeid_", access::public_);
+
+ belongs& edge (unit.new_edge<belongs> (m, *std_string_));
+ edge.hint (*std_string_hint_);
+
+ m.set ("readonly", true);
+ m.set ("discriminator", true);
+
+ c.set ("discriminator", &m);
+ }
+ }
+ }
+
+ virtual void
+ traverse_object_post (type& c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ semantics::data_member* opt (optimistic (c));
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned ||
+ column_count (c).soft != 0 ||
+ (poly_derived && polymorphic_base (c).count ("versioned")))
+ c.set ("versioned", true);
+
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // See if we need to add a special fake section for version update.
+ //
+ if (c.count ("sectionable"))
+ {
+ uss.push_back (
+ user_section (*opt,
+ c,
+ uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version),
+ user_section::load_lazy,
+ user_section::update_manual,
+ user_section::special_version));
+
+ // If we are a root of a polymorphic hierarchy and the version is in
+ // a reuse-base, then we need to make sure that base is sectionable
+ // and derive from its special version update section.
+ //
+ semantics::node& opt_base (opt->scope ());
+ if (poly_root == &c && &opt_base != &c)
+ {
+ if (!opt_base.count ("sectionable"))
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+
+ error (l) << "reuse base class of a sectionable polymorphic " <<
+ "root class must be sectionable" << endl;
+
+ info (opt_base.location ()) << "use '#pragma db object " <<
+ "sectionable' to make the base class of this hierarchy " <<
+ "sectionable" << endl;
+
+ throw operation_failed ();
+ }
+
+ uss.back ().base =
+ &opt_base.get<user_sections> ("user-sections").back ();
+ }
+ }
+
+ // Calculate column counts for sections.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ column_count_type cc (column_count (c, &*i));
+ i->total = cc.total;
+ i->inverse = cc.inverse;
+ i->readonly = cc.readonly;
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned || cc.soft != 0 ||
+ (poly_derived && i->base != 0 && i->base->versioned))
+ i->versioned = true;
+
+ if (size_t n = has_a (c, test_container, &*i))
+ {
+ i->containers = true;
+ i->versioned_containers =
+ n != has_a (c,
+ test_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+
+ if ((n = has_a (c, test_readwrite_container, &*i)))
+ {
+ i->readwrite_containers = true;
+ i->readwrite_versioned_containers =
+ n != has_a (c,
+ test_readwrite_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+ }
+ }
+ }
+ }
+
+ //
+ // View.
+ //
+
+ virtual void
+ traverse_view_pre (type& c)
+ {
+ // Resolve referenced objects from tree nodes to semantic graph
+ // nodes. Also populate maps and compute counts.
+ //
+ view_alias_map& amap (c.set ("alias-map", view_alias_map ()));
+ view_object_map& omap (c.set ("object-map", view_object_map ()));
+
+ size_t& obj_count (c.set ("object-count", size_t (0)));
+ size_t& tbl_count (c.set ("table-count", size_t (0)));
+
+ if (c.count ("objects"))
+ {
+ using semantics::class_;
+
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ {
+ tbl_count++;
+ continue;
+ }
+ else
+ obj_count++;
+
+ tree n (TYPE_MAIN_VARIANT (i->obj_node));
+
+ if (TREE_CODE (n) != RECORD_TYPE)
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a class" << endl;
+
+ throw operation_failed ();
+ }
+
+ class_& o (dynamic_cast<class_&> (*unit.find (n)));
+
+ if (!object (o))
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a persistent class" << endl;
+
+ info (o.location ()) << "class '" << i->obj_name << "' is " <<
+ "defined here" << endl;
+
+ throw operation_failed ();
+ }
+
+ i->obj = &o;
+ i->ptr = 0; // Nothing yet.
+
+ if (i->alias.empty ())
+ {
+ if (!omap.insert (view_object_map::value_type (&o, &*i)).second)
+ {
+ error (i->loc) << "persistent class '" << i->obj_name <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[&o]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Also add the bases of a polymorphic object.
+ //
+ class_* poly_root (polymorphic (o));
+
+ if (poly_root != 0 && poly_root != &o)
+ {
+ for (class_* b (&polymorphic_base (o));;
+ b = &polymorphic_base (*b))
+ {
+ if (!omap.insert (view_object_map::value_type (b, &*i)).second)
+ {
+ error (i->loc) << "base class '" << class_name (*b) <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[b]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ if (b == poly_root)
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!amap.insert (
+ view_alias_map::value_type (i->alias, &*i)).second)
+ {
+ error (i->loc) << "alias '" << i->alias << "' is used in " <<
+ "the view more than once" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_view_post (type& c)
+ {
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+
+ // Figure out if we are versioned. Forced versioning is handled
+ // in relational/processing.
+ //
+ if (column_count (c).soft != 0)
+ c.set ("versioned", true);
+ }
+
+ //
+ // Composite.
+ //
+
+ virtual void
+ traverse_composite_post (type& c)
+ {
+ // Figure out if we are versioned.
+ //
+ if (force_versioned || column_count (c).soft != 0)
+ {
+ c.set ("versioned", true);
+
+ // See if we are "summarily" added/deleted, that is, all the
+ // columns are added/deleted. Note: this does not include
+ // containers.
+ //
+ summary_version sv;
+ sv.traverse (c);
+
+ // Note: there are no locations.
+ //
+ if (sv.av != 0)
+ c.set ("added", sv.av);
+
+ if (sv.dv != 0)
+ c.set ("deleted", sv.dv);
+ }
+ }
+
+ //
+ // Assign object/view pointer.
+ //
+
+ void
+ assign_pointer (type& c)
+ {
+ location_t loc (0); // Pragma location, or 0 if not used.
+
+ try
+ {
+ bool raw;
+ string ptr;
+ string const& type (class_fq_name (c));
+
+ tree decl (0); // Resolved template node.
+ string decl_name; // User-provided template name.
+ tree resolve_scope (0); // Scope in which we resolve names.
+
+ class_pointer const* cp (0);
+ bool cp_template (false);
+
+ if (c.count ("pointer"))
+ {
+ cp = &c.get<class_pointer> ("pointer");
+ }
+ // If we are a derived type in polymorphic hierarchy, then use
+ // our root's pointer type by default.
+ //
+ else if (semantics::class_* r = polymorphic (c))
+ {
+ if (&c != r && r->count ("pointer-template"))
+ cp = r->get<class_pointer const*> ("pointer-template");
+ }
+
+ if (cp != 0)
+ {
+ string const& p (cp->name);
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ cp_template = true;
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ raw = true;
+ ptr = p;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ // Template-id.
+ //
+ raw = false; // Fair to assume not raw, though technically can be.
+ ptr = p;
+ decl_name.assign (p, 0, p.find ('<'));
+ }
+ else
+ {
+ // This is not a template-id. Resolve it and see if it is a
+ // template or a type.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TYPE_DECL)
+ {
+ raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE);
+ ptr = p;
+
+ // This can be a typedef'ed alias for a TR1 template-id.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl)))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+ }
+ else
+ decl = 0; // Not a template.
+ }
+ else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ cp_template = true;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a type or a template" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ }
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // a pointer.
+ //
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (!ns->count ("pointer"))
+ {
+ if (ns->global_scope ()) // Note: namespace always named.
+ break;
+ else
+ continue;
+ }
+
+ cp = &ns->get<class_pointer> ("pointer");
+ string const& p (cp->name);
+
+ // Namespace-specified pointer can only be '*' or are template.
+ //
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a raw pointer" << endl;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a template-id" << endl;
+ }
+ else
+ {
+ // Resolve this name and make sure it is a template.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a template" << endl;
+ }
+ }
+
+ if (ptr.empty ())
+ throw operation_failed ();
+
+ cp_template = true;
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ break;
+ }
+
+ // Use the default pointer.
+ //
+ if (ptr.empty ())
+ {
+ string const& p (options.default_pointer ());
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+
+ // Resolve scope is the scope of the class.
+ //
+ resolve_scope = class_scope (c).tree_node ();
+ }
+ }
+
+ // If this class is a root of a polymorphic hierarchy, then cache
+ // the pointer template so that we can use it for derived classes.
+ //
+ if (cp != 0 && cp_template && polymorphic (c) == &c)
+ c.set ("pointer-template", cp);
+
+ // Check if we are using TR1.
+ //
+ if (decl != 0 || !decl_name.empty ())
+ {
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ // First check the user-supplied name.
+ //
+ tr1 = tr1
+ || decl_name.compare (0, 8, "std::tr1") == 0
+ || decl_name.compare (0, 10, "::std::tr1") == 0;
+
+ // If there was no match, also resolve the name since it can be
+ // a using-declaration for a TR1 template.
+ //
+ if (!tr1)
+ {
+ if (decl == 0)
+ decl = resolve_name (decl_name, resolve_scope, false);
+
+ if (TREE_CODE (decl) != TEMPLATE_DECL || !
+ DECL_CLASS_TEMPLATE_P (decl))
+ {
+ // This is only checked for the --default-pointer option.
+ //
+ error (c.file (), c.line (), c.column ())
+ << "name '" << decl_name << "' specified with the "
+ << "--default-pointer option does not name a class "
+ << "template" << endl;
+
+ throw operation_failed ();
+ }
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+
+ // Fully-qualify all the unqualified components of the name.
+ //
+ try
+ {
+ lex_.start (ptr);
+ ptr.clear ();
+
+ string t;
+ bool punc (false);
+ bool scoped (false);
+
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ ptr += ' ';
+
+ punc = false;
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ ptr += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ ptr += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ ptr += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // If the name was not preceeded with '::', look it
+ // up in the pragmas's scope and add the qualifer.
+ //
+ if (!scoped)
+ {
+ tree decl (resolve_name (t, resolve_scope, false));
+ tree scope (CP_DECL_CONTEXT (decl));
+
+ // If this is an inline namespace, skip it until we get
+ // to the non-inline one.
+ //
+ while (scope != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (scope));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, scope, true))
+#else
+ if (!is_associated_namespace (prev, scope))
+#endif
+ break;
+
+ scope = prev;
+ }
+
+ if (scope != global_namespace)
+ {
+ ptr += "::";
+ ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER);
+ }
+
+ ptr += "::";
+ }
+
+ ptr += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ ptr += t;
+ punc = true;
+ break;
+ }
+ default:
+ {
+ ptr += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ }
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw operation_failed ();
+ }
+
+ c.set ("object-pointer", ptr);
+ c.set ("object-pointer-raw", raw);
+ }
+ catch (invalid_name const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "name '" << ex.name () << "' specified with db pragma "
+ << "pointer is invalid" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "name '" << ex.name () << "' specified with the "
+ << "--default-pointer option is invalid" << endl;
+
+
+ throw operation_failed ();
+ }
+ catch (unable_to_resolve const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with db pragma pointer" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with the --default-pointer option" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ struct invalid_name
+ {
+ invalid_name (string const& n): name_ (n) {}
+
+ string const&
+ name () const {return name_;}
+
+ private:
+ string name_;
+ };
+
+ typedef lookup::unable_to_resolve unable_to_resolve;
+
+ tree
+ resolve_name (string const& qn, tree scope, bool is_type)
+ {
+ try
+ {
+ string tl;
+ tree tn;
+ cpp_ttype tt, ptt;
+
+ nlex_.start (qn);
+ tt = nlex_.next (tl, &tn);
+
+ string name;
+ return lookup::resolve_scoped_name (
+ nlex_, tt, tl, tn, ptt, scope, name, is_type);
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw invalid_name (qn);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ throw invalid_name (qn);
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+
+ cxx_string_lexer lex_;
+ cxx_string_lexer nlex_; // Nested lexer.
+
+ semantics::type* std_string_;
+ semantics::names* std_string_hint_;
+
+ tree access_; // odb::access node.
+ };
+
+ static bool
+ check_to_from (const cxx_tokens& ex, const char* c, location_t l)
+ {
+ // Make sure we have one and only one placeholder (?).
+ //
+ bool r (false), m (true);
+
+ for (cxx_tokens::const_iterator i (ex.begin ()), e (ex.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ {
+ if (r)
+ m = false; // Multiple (?), can't move.
+ else
+ r = true;
+ }
+ }
+ }
+ else
+ ++i;
+ }
+
+ if (!r)
+ {
+ error (l) << "no '(?)' expression in the '" << c << "' clause "
+ << "of db pragma map" << endl;
+
+ throw operation_failed ();
+ }
+
+ return m;
+ }
+}
+
+static void
+process1 (semantics::unit& u)
+{
+ // Process custom C++ type mapping.
+ //
+
+ // Create an empty list if we don't have one. This makes the
+ // rest of the code simpler.
+ //
+ if (!u.count ("custom-cxx-types"))
+ u.set ("custom-cxx-types", custom_cxx_types ());
+
+ custom_cxx_types & cts (u.get<custom_cxx_types> ("custom-cxx-types"));
+
+ for (custom_cxx_types::iterator i (cts.begin ()); i != cts.end (); ++i)
+ {
+ custom_cxx_type& ct (*i);
+
+ // type
+ //
+ if (ct.type_node == 0)
+ {
+ error (ct.loc) << "'type' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.type = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.type_node)));
+ ct.type_hint = u.find_hint (ct.type_node);
+
+ // as
+ //
+ if (ct.as_node == 0)
+ {
+ error (ct.loc) << "'as' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.as = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.as_node)));
+ ct.as_hint = u.find_hint (ct.as_node);
+
+ // to
+ //
+ {
+ cxx_tokens& e (ct.to);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.to_move = true;
+ }
+ else
+ ct.to_move = check_to_from (e, "to", ct.loc);
+ }
+
+ // from
+ //
+ {
+ cxx_tokens& e (ct.from);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.from_move = true;
+ }
+ else
+ ct.from_move = check_to_from (e, "from", ct.loc);
+ }
+
+ // Resolve mapping scope.
+ //
+ semantics::scope* s (dynamic_cast<semantics::scope*> (u.find (ct.scope)));
+ if (s == 0)
+ {
+ error (ct.loc) << "unable to resolve db pragma map scope" << endl;
+ throw operation_failed ();
+ }
+
+ if (semantics::namespace_* ns = dynamic_cast<semantics::namespace_*> (s))
+ {
+ if (ns->extension ())
+ s = &ns->original ();
+ }
+
+ // Enter into the map.
+ //
+ if (!s->count ("custom-cxx-type-map"))
+ s->set ("custom-cxx-type-map", custom_cxx_type_map ());
+
+ s->get<custom_cxx_type_map> ("custom-cxx-type-map")[ct.type] = &ct;
+ }
+}
+
+static void
+process2 (options const& ops, features& f, semantics::unit& u)
+{
+ unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ // Common processing.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx->unit);
+ }
+
+ // Database-specific processing.
+ //
+ switch (ops.database ()[0])
+ {
+ case database::common:
+ {
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::process ();
+ break;
+ }
+ }
+}
+
+void
+process (options const& ops,
+ features& f,
+ semantics::unit& u,
+ semantics::path const&,
+ unsigned short pass)
+{
+ try
+ {
+ if (pass == 1)
+ process1 (u);
+ else if (pass == 2)
+ process2 (ops, f, u);
+ }
+ catch (operation_failed const&)
+ {
+ // Processing failed. Diagnostics has already been issued.
+ //
+ throw processor_failed ();
+ }
+}
diff --git a/odb/odb/processor.hxx b/odb/odb/processor.hxx
new file mode 100644
index 0000000..1e70cab
--- /dev/null
+++ b/odb/odb/processor.hxx
@@ -0,0 +1,23 @@
+// file : odb/processor.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PROCESSOR_HXX
+#define ODB_PROCESSOR_HXX
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+class processor_failed {};
+
+// Pass one is very early processing before any validation has been
+// done.
+//
+void
+process (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&,
+ unsigned short pass);
+
+#endif // ODB_PROCESSOR_HXX
diff --git a/odb/odb/profile.cxx b/odb/odb/profile.cxx
new file mode 100644
index 0000000..1a10bfb
--- /dev/null
+++ b/odb/odb/profile.cxx
@@ -0,0 +1,86 @@
+// file : odb/profile.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <unistd.h> // stat
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+#include <iostream>
+
+#include <odb/profile.hxx>
+
+using namespace std;
+
+static bool
+exist (profile_data::path const& p)
+{
+ struct stat info;
+
+ // Just check that the file exist without checking for permissions, etc.
+ //
+ return stat (p.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode);
+}
+
+string
+profile_search (char const* prof, void* arg)
+{
+ typedef profile_data::path path;
+ typedef profile_data::paths paths;
+
+ profile_data* pd (static_cast<profile_data*> (arg));
+ paths const& ps (pd->search_paths);
+
+ path p (prof), odb ("odb"), r;
+ p.normalize (); // Convert '/' to the canonical path separator form.
+ path p_db (p);
+ p_db += "-";
+ p_db += pd->db.string ();
+ p += ".options";
+ p_db += ".options";
+
+ paths::const_iterator i (ps.begin ()), end (ps.end ());
+ for (; i != end; ++i)
+ {
+ // First check for the database-specific version in the search directory
+ // itself and then try the odb/ subdirectory.
+ //
+ if (exist (r = *i / p_db))
+ break;
+
+ if (exist (r = *i / odb / p_db))
+ break;
+
+ // Then try the same with the database-independent version.
+ //
+ if (exist (r = *i / p))
+ break;
+
+ if (exist (r = *i / odb / p))
+ break;
+ }
+
+ if (i == end)
+ {
+ // Ignore the case where we didn't find the profile and this is the
+ // common database.
+ //
+ if (pd->db == database::common)
+ return string ();
+
+ cerr << pd->name << ": error: unable to locate options file for profile '"
+ << prof << "'" << endl;
+ throw profile_failure ();
+ }
+
+ if (pd->loaded.find (r) != pd->loaded.end ())
+ return string ();
+
+ pd->loaded.insert (r);
+ return r.string ();
+}
+
+string
+profile_search_ignore (char const*, void*)
+{
+ return string ();
+}
diff --git a/odb/odb/profile.hxx b/odb/odb/profile.hxx
new file mode 100644
index 0000000..b6e8e53
--- /dev/null
+++ b/odb/odb/profile.hxx
@@ -0,0 +1,39 @@
+// file : odb/profile.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PROFILE_HXX
+#define ODB_PROFILE_HXX
+
+#include <set>
+#include <vector>
+#include <string>
+
+#include <libcutl/fs/path.hxx>
+
+#include <odb/option-types.hxx>
+
+struct profile_data
+{
+ typedef cutl::fs::path path;
+ typedef std::vector<path> paths;
+
+ profile_data (paths const& p, database d, char const* n)
+ : search_paths (p), db (d), name (n)
+ {
+ }
+
+ paths const& search_paths;
+ database db;
+ char const* name;
+ std::set<path> loaded;
+};
+
+struct profile_failure {};
+
+std::string
+profile_search (char const* profile, void* arg);
+
+std::string
+profile_search_ignore (char const* profile, void* arg);
+
+#endif // ODB_PROFILE_HXX
diff --git a/odb/odb/relational/changelog.cxx b/odb/odb/relational/changelog.cxx
new file mode 100644
index 0000000..99f72da
--- /dev/null
+++ b/odb/odb/relational/changelog.cxx
@@ -0,0 +1,1239 @@
+// file : odb/relational/changelog.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/semantics/relational.hxx>
+#include <odb/traversal/relational.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace changelog
+ {
+ using namespace sema_rel;
+ using sema_rel::model;
+ using sema_rel::changelog;
+
+ typedef map<qname, semantics::node*> deleted_table_map;
+ typedef map<uname, semantics::data_member*> deleted_column_map;
+
+ namespace
+ {
+ //
+ // diff
+ //
+
+ struct diff_table: trav_rel::column,
+ trav_rel::primary_key,
+ trav_rel::foreign_key,
+ trav_rel::index
+ {
+ enum mode_type {mode_add, mode_drop};
+
+ diff_table (table& o,
+ mode_type m,
+ alter_table& a,
+ graph& gr,
+ options const& op,
+ model_version const* v)
+ : other (o), mode (m), at (a), g (gr), ops (op), version (v) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ using sema_rel::column;
+
+ if (mode == mode_add)
+ {
+ if (column* oc = other.find<column> (c.name ()))
+ {
+ if (c.type () != oc->type ())
+ diagnose_column (c, "type", oc->type (), c.type ());
+
+ if (c.null () != oc->null ())
+ {
+ alter_column& ac (g.new_node<alter_column> (c.id ()));
+
+ // Set the alters edge.
+ //
+ column* b (at.lookup<column, drop_column> (c.name ()));
+ assert (b != 0);
+ g.new_edge<alters> (ac, *b);
+
+ ac.null (c.null ());
+ g.new_edge<unames> (at, ac, c.name ());
+ }
+
+ if (c.default_ () != oc->default_ ())
+ diagnose_column (
+ c, "default value", oc->default_ (), c.default_ ());
+
+ if (c.options () != oc->options ())
+ diagnose_column (c, "options", oc->options (), c.options ());
+ }
+ else
+ {
+ if (version != 0)
+ {
+ data_member_path const& mp (
+ c.get<data_member_path> ("member-path"));
+
+ semantics::data_member* m (context::added_member (mp));
+ if (m != 0)
+ {
+ // Make sure the addition version is the current version.
+ //
+ if (context::added (*m) != version->current)
+ {
+ location l (m->get<location_t> ("added-location"));
+ error (l) << "member addition version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard additions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ else if (ops.warn_hard_add () && version->open)
+ {
+ // Issue nicer diagnostics for the direct data member case.
+ //
+ if (mp.size () == 1)
+ {
+ location l (mp.back ()->location ());
+ warn (l) << "data member is hard-added" << endl;
+ }
+ else
+ {
+ semantics::class_& s (
+ dynamic_cast<semantics::class_&> (
+ mp.front ()->scope ()));
+
+ warn (s.location ()) << "column '" << c.name () << "' " <<
+ "in class '" << s.name () << "' is hard-added" << endl;
+
+ for (data_member_path::const_iterator i (mp.begin ());
+ i != mp.end (); ++i)
+ {
+ info ((*i)->location ()) << "corresponding hard-" <<
+ "added data member could be '" << (*i)->name () <<
+ "'" << endl;
+ }
+ }
+ }
+ }
+
+ add_column& ac (g.new_node<add_column> (c, at, g));
+ g.new_edge<unames> (at, ac, c.name ());
+ }
+ }
+ else
+ {
+ if (other.find<column> (c.name ()) == 0)
+ {
+ if (version != 0)
+ {
+ // See if we have an entry for this column in the soft-
+ // deleted map.
+ //
+ deleted_column_map const& dm (
+ other.get<deleted_column_map> ("deleted-map"));
+ deleted_column_map::const_iterator i (dm.find (c.name ()));
+
+ if (i != dm.end ())
+ {
+ if (context::deleted (*i->second) != version->current)
+ {
+ location l (i->second->get<location_t> ("deleted-location"));
+ error (l) << "member deletion version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard deletions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ else if (ops.warn_hard_delete () && version->open)
+ {
+ // Here all we have is a column name and a class (object)
+ // or data member (container) corresponding to the table.
+ //
+ if (semantics::class_* s =
+ other.get<semantics::class_*> ("class", 0))
+ {
+ warn (s->location ()) << "column '" << c.name () << "' " <<
+ "in class '" << s->name () << "' is hard-deleted" << endl;
+ }
+ else
+ {
+ data_member_path const& mp (
+ other.get<data_member_path> ("member-path"));
+
+ warn (mp.back ()->location ()) << "column '" <<
+ c.name () << "' in container '" <<
+ mp.back ()->name () << "' is hard-deleted" << endl;
+ }
+ }
+ }
+
+ drop_column& dc (g.new_node<drop_column> (c.id ()));
+ g.new_edge<unames> (at, dc, c.name ());
+ }
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::primary_key& pk)
+ {
+ using sema_rel::primary_key;
+
+ if (mode == mode_add)
+ {
+ if (primary_key* opk = other.find<primary_key> (pk.name ()))
+ {
+ if (pk.auto_ () != opk->auto_ ())
+ diagnose_primary_key (pk, "auto kind");
+
+ // Database-specific information.
+ //
+ for (primary_key::extra_map::const_iterator i (
+ pk.extra ().begin ()); i != pk.extra ().end (); ++i)
+ {
+ if (opk->extra ().count (i->first) == 0 ||
+ opk->extra ()[i->first] != i->second)
+ diagnose_primary_key (pk, i->first.c_str ());
+ }
+
+ for (primary_key::extra_map::const_iterator i (
+ opk->extra ().begin ()); i != opk->extra ().end (); ++i)
+ {
+ if (pk.extra ().count (i->first) == 0 ||
+ pk.extra ()[i->first] != i->second)
+ diagnose_primary_key (pk, i->first.c_str ());
+ }
+
+ if (pk.contains_size () != opk->contains_size ())
+ diagnose_primary_key (pk, "member set");
+
+ for (primary_key::contains_size_type i (0);
+ i != pk.contains_size (); ++i)
+ {
+ sema_rel::contains& c (pk.contains_at (i));
+ sema_rel::contains& oc (opk->contains_at (i));
+
+ if (c.column ().name () != oc.column ().name ())
+ diagnose_primary_key (pk, "member set");
+ }
+ }
+ else
+ {
+ location const& l (pk.get<location> ("cxx-location"));
+ error (l) << "adding object id to an existing class is " <<
+ "not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new class with the object id, migrating the data, and " <<
+ "deleteing the old class" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ if (other.find<primary_key> (pk.name ()) == 0)
+ {
+ location const& l (other.get<location> ("cxx-location"));
+ error (l) << "deleting object id from an existing class is " <<
+ "not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new class without the object id, migrating the data, " <<
+ "and deleteing the old class" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ using sema_rel::foreign_key;
+
+ if (mode == mode_add)
+ {
+ if (foreign_key* ofk = other.find<foreign_key> (fk.name ()))
+ {
+ if (fk.deferrable () != ofk->deferrable ())
+ diagnose_foreign_key (fk, "deferrable mode");
+
+ if (fk.on_delete () != ofk->on_delete ())
+ diagnose_foreign_key (fk, "on delete action");
+
+ if (fk.referenced_table () != ofk->referenced_table ())
+ // See diagnose_foreign_key() if changing this name.
+ //
+ diagnose_foreign_key (fk, "pointed-to class");
+
+ if (fk.referenced_columns () != ofk->referenced_columns ())
+ diagnose_foreign_key (fk, "id member set");
+
+ if (fk.contains_size () != ofk->contains_size ())
+ diagnose_foreign_key (fk, "id member set");
+
+ for (foreign_key::contains_size_type i (0);
+ i != fk.contains_size (); ++i)
+ {
+ sema_rel::contains& c (fk.contains_at (i));
+ sema_rel::contains& oc (ofk->contains_at (i));
+
+ if (c.column ().name () != oc.column ().name ())
+ diagnose_foreign_key (fk, "id member set");
+ }
+ }
+ else
+ {
+ add_foreign_key& afk (g.new_node<add_foreign_key> (fk, at, g));
+ g.new_edge<unames> (at, afk, fk.name ());
+ }
+ }
+ else
+ {
+ if (other.find<foreign_key> (fk.name ()) == 0)
+ {
+ drop_foreign_key& dfk (g.new_node<drop_foreign_key> (fk.id ()));
+ g.new_edge<unames> (at, dfk, fk.name ());
+ }
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::index& i)
+ {
+ using sema_rel::index;
+
+ if (mode == mode_add)
+ {
+ if (index* oi = other.find<index> (i.name ()))
+ {
+ if (i.type () != oi->type ())
+ diagnose_index (i, "type", oi->type (), i.type ());
+
+ if (i.method () != oi->method ())
+ diagnose_index (i, "method", oi->method (), i.method ());
+
+ if (i.options () != oi->options ())
+ diagnose_index (i, "options", oi->options (), i.options ());
+
+ if (i.contains_size () != oi->contains_size ())
+ diagnose_index (i, "member set", "", "");
+
+ for (index::contains_size_type j (0);
+ j != i.contains_size (); ++j)
+ {
+ sema_rel::contains& c (i.contains_at (j));
+ sema_rel::contains& oc (oi->contains_at (j));
+
+ if (c.column ().name () != oc.column ().name ())
+ diagnose_index (i, "member set", "", "");
+
+ if (c.options () != oc.options ())
+ diagnose_index (
+ i, "member options", oc.options (), c.options ());
+ }
+ }
+ else
+ {
+ add_index& ai (g.new_node<add_index> (i, at, g));
+ g.new_edge<unames> (at, ai, i.name ());
+ }
+ }
+ else
+ {
+ if (other.find<index> (i.name ()) == 0)
+ {
+ drop_index& di (g.new_node<drop_index> (i.id ()));
+ g.new_edge<unames> (at, di, i.name ());
+ }
+ }
+ }
+
+ void
+ diagnose_column (sema_rel::column& c,
+ char const* name,
+ string const& ov,
+ string const& nv)
+ {
+ table& t (c.table ());
+ location const& tl (t.get<location> ("cxx-location"));
+ location const& cl (c.get<location> ("cxx-location"));
+
+ error (cl) << "change to data member results in the change of " <<
+ "the corresponding column " << name;
+
+ if (!ov.empty () || !nv.empty ())
+ cerr << " (old: '" << ov << "', new: '" << nv << "')";
+
+ cerr << endl;
+
+ error (cl) << "this change is not yet handled automatically" << endl;
+ info (cl) << "corresponding column '" << c.name () << "' " <<
+ "originates here" << endl;
+ info (tl) << "corresponding table '" << t.name () << "' " <<
+ "originates here" << endl;
+ info (cl) << "consider re-implementing this change by adding " <<
+ "a new data member with the desired " << name << ", migrating " <<
+ "the data, and deleting the old data member" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ diagnose_primary_key (sema_rel::primary_key& pk, char const* name)
+ {
+ location const& l (pk.get<location> ("cxx-location"));
+
+ error (l) << "changing object id " << name << " in an existing " <<
+ "class is not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new class with the desired object id " << name << ", " <<
+ "migrating the data, and deleteing the old class" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ diagnose_foreign_key (sema_rel::foreign_key& fk, char const* name)
+ {
+ // This can be an object pointer or a polymorphic base link.
+ // The latter will trigger this call if we change one base
+ // to another.
+ //
+ using sema_rel::table;
+ using sema_rel::foreign_key;
+
+ // Polymorphic base link is the first foreign key.
+ //
+ table& t (fk.table ());
+ table::names_iterator p (t.find (fk.name ()));
+
+ if (t.extra ()["kind"] == "polymorphic derived object" &&
+ (p == t.names_begin () || !(--p)->is_a<foreign_key> ()))
+ {
+ location const& l (t.get<location> ("cxx-location"));
+
+ if (name == string ("pointed-to class"))
+ {
+ error (l) << "changing polymorphic base is not " <<
+ "supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new derived class with the desired base, migrating the " <<
+ "data, and deleteing the old class" << endl;
+ }
+ else
+ {
+ error (l) << "changing polymorphic base " << name <<
+ " is not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new derived class with the desired " << name << ", " <<
+ "migrating the data, and deleteing the old class" << endl;
+ }
+ }
+ else
+ {
+ location const& l (fk.get<location> ("cxx-location"));
+
+ error (l) << "changing object pointer " << name << " is not " <<
+ "supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new object pointer with the desired " << name << ", " <<
+ "migrating the data, and deleteing the old pointer" << endl;
+ }
+
+ throw operation_failed ();
+ }
+
+ void
+ diagnose_index (sema_rel::index& i,
+ char const* name,
+ string const& ov,
+ string const& nv)
+ {
+ table& t (i.table ());
+ location const& tl (t.get<location> ("cxx-location"));
+ location const& il (i.get<location> ("cxx-location"));
+
+ error (il) << "change to index " << name;
+
+ if (!ov.empty () || !nv.empty ())
+ cerr << " (old: '" << ov << "', new: '" << nv << "')";
+
+ cerr << " is not yet handled automatically" << endl;
+
+ info (il) << "corresponding index '" << i.name () << "' " <<
+ "originates here" << endl;
+ info (tl) << "corresponding table '" << t.name () << "' " <<
+ "originates here" << endl;
+ info (il) << "consider re-implementing this change by adding " <<
+ "a new index with the desired " << name << " and deleting the " <<
+ "old one" << endl;
+
+ throw operation_failed ();
+ }
+
+ protected:
+ table& other;
+ mode_type mode;
+ alter_table& at;
+ graph& g;
+ options const& ops;
+ model_version const* version;
+ };
+
+ struct diff_model: trav_rel::table
+ {
+ enum mode_type {mode_add, mode_drop};
+
+ diff_model (model& o,
+ mode_type m,
+ changeset& s,
+ graph& gr,
+ string const& in,
+ options const& op,
+ model_version const* v)
+ : other (o),
+ mode (m),
+ cs (s),
+ g (gr),
+ in_name (in),
+ ops (op),
+ version (v) {}
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ using sema_rel::table;
+
+ if (mode == mode_add)
+ {
+ if (table* ot = other.find<table> (t.name ()))
+ {
+ // See if there are any changes to the table.
+ //
+ alter_table& at (g.new_node<alter_table> (t.id ()));
+
+ // Set the alters edge for lookup.
+ //
+ table* bt (cs.lookup<table, drop_table> (t.name ()));
+ assert (bt != 0);
+ alters& ae (g.new_edge<alters> (at, *bt));
+
+ if (t.options () != ot->options ())
+ diagnose_table (t, "options", ot->options (), t.options ());
+
+ if (ot->extra ()["kind"] != t.extra ()["kind"])
+ diagnose_table (t, "kind",
+ ot->extra ()["kind"], t.extra ()["kind"]);
+
+ {
+ trav_rel::table table;
+ trav_rel::unames names;
+ diff_table dtable (
+ *ot, diff_table::mode_add, at, g, ops, version);
+ table >> names >> dtable;
+ table.traverse (t);
+ }
+
+ {
+ trav_rel::table table;
+ trav_rel::unames names;
+ diff_table dtable (
+ t, diff_table::mode_drop, at, g, ops, version);
+ table >> names >> dtable;
+ table.traverse (*ot);
+ }
+
+ if (!at.names_empty ())
+ g.new_edge<qnames> (cs, at, t.name ());
+ else
+ {
+ g.delete_edge (at, *bt, ae);
+ g.delete_node (at);
+ }
+ }
+ else
+ {
+ // Soft-add is only applicable to containers.
+ //
+ if (version != 0 && t.count ("member-path"))
+ {
+ data_member_path const& mp (
+ t.get<data_member_path> ("member-path"));
+
+ // Make sure the addition version is the current version.
+ //
+ semantics::data_member* m (context::added_member (mp));
+ if (m != 0)
+ {
+ if (context::added (*m) != version->current)
+ {
+ location l (m->get<location_t> ("added-location"));
+ error (l) << "member addition version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard additions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ else if (ops.warn_hard_add () && version->open)
+ {
+ // Issue nicer diagnostics for the direct data member case.
+ //
+ if (mp.size () == 1)
+ {
+ location l (mp.back ()->location ());
+ warn (l) << "data member is hard-added" << endl;
+ }
+ else
+ {
+ semantics::class_& s (
+ dynamic_cast<semantics::class_&> (
+ mp.front ()->scope ()));
+
+ warn (s.location ()) << "container table '" <<
+ t.name () << "' in class '" << s.name () << "' is " <<
+ "hard-added" << endl;
+
+ for (data_member_path::const_iterator i (mp.begin ());
+ i != mp.end (); ++i)
+ {
+ info ((*i)->location ()) << "corresponding hard-" <<
+ "added data member could be '" << (*i)->name () <<
+ "'" << endl;
+ }
+ }
+ }
+ }
+
+ add_table& at (g.new_node<add_table> (t, cs, g));
+ g.new_edge<qnames> (cs, at, t.name ());
+ }
+ }
+ else
+ {
+ if (other.find<table> (t.name ()) == 0)
+ {
+ if (version != 0)
+ {
+ // See if we have an entry for this table in the soft-
+ // deleted map.
+ //
+ deleted_table_map const& dm (
+ other.get<deleted_table_map> ("deleted-map"));
+ deleted_table_map::const_iterator i (dm.find (t.name ()));
+
+ if (i != dm.end ())
+ {
+ // This table could be derived either from a class (object)
+ // or data member (container).
+ //
+ semantics::class_* c (
+ dynamic_cast<semantics::class_*> (i->second));
+ if (c != 0 && context::deleted (*c) != version->current)
+ {
+ location l (c->get<location_t> ("deleted-location"));
+ error (l) << "class deletion version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+
+ semantics::data_member* m (
+ dynamic_cast<semantics::data_member*> (i->second));
+ if (m != 0 && context::deleted (*m) != version->current)
+ {
+ location l (m->get<location_t> ("deleted-location"));
+ error (l) << "member deletion version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard deletions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ // At first, it may seem like a good idea not to warn about
+ // class deletions since it is not possible to do anything
+ // useful with a class without referencing it from the code
+ // (in C++ sense). However, things get tricky once we consider
+ // polymorphism since the migration code could be "working"
+ // with the hard-deleted derived class via its base. So we
+ // are going to warn about polymorphic derived tables.
+ //
+ else if (ops.warn_hard_delete () && version->open)
+ {
+ string k (t.extra ()["kind"]);
+
+ // We don't have any sensible location.
+ //
+ if (k == "container")
+ {
+ // We will issue a useless warning if the object table
+ // that this container references is also deleted.
+ // While we could detect this, it is going to require
+ // some effort since we would have to delay the warning
+ // and check later once we know all the deleted tables.
+ //
+ cerr << in_name << ": warning: container table '" <<
+ t.name () << "' is hard-deleted" << endl;
+ }
+ else if (k == "polymorphic derived object")
+ {
+ // The same as above: the warning will be useless if
+ // we are dropping the whole hierarchy.
+ //
+ cerr << in_name << ": warning: polymorphic derived " <<
+ "object table '" << t.name () << "' is hard-deleted" <<
+ endl;
+ }
+ }
+ }
+
+ drop_table& dt (g.new_node<drop_table> (t.id ()));
+ g.new_edge<qnames> (cs, dt, t.name ());
+ }
+ }
+ }
+
+ void
+ diagnose_table (sema_rel::table& t,
+ char const* name,
+ string const& ov,
+ string const& nv)
+ {
+ location const& tl (t.get<location> ("cxx-location"));
+
+ error (tl) << "change to object or container member results in "
+ "the change of the corresponding table " << name;
+
+ if (!ov.empty () || !nv.empty ())
+ cerr << " (old: '" << ov << "', new: '" << nv << "')";
+
+ cerr << endl;
+
+ error (tl) << "this change is not yet handled automatically" << endl;
+ info (tl) << "consider re-implementing this change by adding a " <<
+ "new object or container member with the desired " << name <<
+ ", migrating the data, and deleting the old object or member" <<
+ endl;
+
+ throw operation_failed ();
+ }
+
+ protected:
+ model& other;
+ mode_type mode;
+ changeset& cs;
+ graph& g;
+ string in_name;
+ options const& ops;
+ model_version const* version;
+ };
+
+ // Assumes the new model has cxx-location set. If version is not 0,
+ // then assume it is the current model version and the new model is
+ // the current model which has member paths and deleted maps set.
+ //
+ changeset&
+ diff (model& o,
+ model& n,
+ changelog& l,
+ string const& in_name,
+ options const& ops,
+ model_version const* version)
+ {
+ changeset& r (l.new_node<changeset> (n.version ()));
+
+ // Set the alters edge for lookup. If we are diff'ing two models of
+ // the same version, then use the old model as a base. Otherwise use
+ // the tip of changelog (it should correspond to the old model).
+ //
+ if (o.version () == n.version ())
+ l.new_edge<alters> (r, o);
+ else
+ {
+ if (l.contains_changeset_empty ())
+ {
+ model& m (l.model ());
+
+ // The changelog model version may also be equal to the new model
+ // version if the new base model version is greater than the
+ // latest changeset.
+ //
+ assert (m.version () == o.version () ||
+ m.version () == n.version ());
+
+ l.new_edge<alters> (r, m);
+ }
+ else
+ {
+ changeset& c (l.contains_changeset_back ().changeset ());
+ assert (o.version () == c.version ());
+ l.new_edge<alters> (r, c);
+ }
+ }
+
+ {
+ trav_rel::model model;
+ trav_rel::qnames names;
+ diff_model dmodel (
+ o, diff_model::mode_add, r, l, in_name, ops, version);
+ model >> names >> dmodel;
+ model.traverse (n);
+ }
+
+ {
+ trav_rel::model model;
+ trav_rel::qnames names;
+ diff_model dmodel (
+ n, diff_model::mode_drop, r, l, in_name, ops, version);
+ model >> names >> dmodel;
+ model.traverse (o);
+ }
+
+ return r;
+ }
+
+ //
+ // patch
+ //
+
+ struct patch_table: trav_rel::add_column,
+ trav_rel::drop_column,
+ trav_rel::alter_column,
+ trav_rel::add_index,
+ trav_rel::drop_index,
+ trav_rel::add_foreign_key,
+ trav_rel::drop_foreign_key
+ {
+ patch_table (table& tl, graph& gr): t (tl), g (gr) {}
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ try
+ {
+ column& c (g.new_node<column> (ac, t, g));
+ g.new_edge<unames> (t, c, ac.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: column '" << ac.name () <<
+ "' already exists in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ table::names_iterator i (t.find (dc.name ()));
+
+ if (i == t.names_end () || !i->nameable ().is_a<column> ())
+ {
+ cerr << "error: invalid changelog: column '" << dc.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (t, i->nameable (), *i);
+ }
+
+ virtual void
+ traverse (sema_rel::alter_column& ac)
+ {
+ if (column* c = t.find<column> (ac.name ()))
+ {
+ if (ac.null_altered ())
+ c->null (ac.null ());
+ }
+ else
+ {
+ cerr << "error: invalid changelog: column '" << ac.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::add_index& ai)
+ {
+ using sema_rel::index;
+
+ try
+ {
+ index& i (g.new_node<index> (ai, t, g));
+ g.new_edge<unames> (t, i, ai.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: index '" << ai.name () <<
+ "' already exists in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_index& di)
+ {
+ using sema_rel::index;
+ table::names_iterator i (t.find (di.name ()));
+
+ if (i == t.names_end () || !i->nameable ().is_a<index> ())
+ {
+ cerr << "error: invalid changelog: index '" << di.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (t, i->nameable (), *i);
+ }
+
+ virtual void
+ traverse (sema_rel::add_foreign_key& afk)
+ {
+ using sema_rel::foreign_key;
+
+ try
+ {
+ foreign_key& fk (g.new_node<foreign_key> (afk, t, g));
+ g.new_edge<unames> (t, fk, afk.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: foreign key '" << afk.name () <<
+ "' already exists in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ using sema_rel::foreign_key;
+ table::names_iterator i (t.find (dfk.name ()));
+
+ if (i == t.names_end () || !i->nameable ().is_a<foreign_key> ())
+ {
+ cerr << "error: invalid changelog: foreign key '" << dfk.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (t, i->nameable (), *i);
+ }
+
+ protected:
+ table& t;
+ graph& g;
+ };
+
+ struct patch_model: trav_rel::add_table,
+ trav_rel::drop_table,
+ trav_rel::alter_table
+ {
+ patch_model (model& ml, graph& gr): m (ml), g (gr) {}
+
+ virtual void
+ traverse (sema_rel::add_table& at)
+ {
+ try
+ {
+ table& t (g.new_node<table> (at, m, g));
+ g.new_edge<qnames> (m, t, at.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: table '" << at.name () <<
+ "' already exists in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_table& dt)
+ {
+ model::names_iterator i (m.find (dt.name ()));
+
+ if (i == m.names_end () || !i->nameable ().is_a<table> ())
+ {
+ cerr << "error: invalid changelog: table '" << dt.name () <<
+ "' does not exist in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (m, i->nameable (), *i);
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (table* t = m.find<table> (at.name ()))
+ {
+ trav_rel::alter_table atable;
+ trav_rel::unames names;
+ patch_table ptable (*t, g);
+ atable >> names >> ptable;
+ atable.traverse (at);
+ }
+ else
+ {
+ cerr << "error: invalid changelog: table '" << at.name () <<
+ "' does not exist in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+ }
+
+ protected:
+ model& m;
+ graph& g;
+ };
+
+ model&
+ patch (model& m, changeset& c, graph& g)
+ {
+ model& r (g.new_node<model> (m, g));
+
+ trav_rel::changeset changeset;
+ trav_rel::qnames names;
+ patch_model pmodel (r, g);
+ changeset >> names >> pmodel;
+ changeset.traverse (c);
+
+ r.version (c.version ());
+ return r;
+ }
+ }
+
+ cutl::shared_ptr<changelog>
+ generate (model& m,
+ model_version const& mv,
+ changelog* old,
+ string const& in_name,
+ string const& out_name,
+ options const& ops)
+ {
+ database db (ops.database ()[0]);
+ cutl::shared_ptr<changelog> cl (
+ new (shared) changelog (db.string (), ops.schema_name ()[db]));
+ graph& g (*cl);
+
+ if (old == 0)
+ {
+ // Don't allow changelog initialization if the version is closed.
+ // This will prevent adding new files to an existing object model
+ // with a closed version.
+ //
+ if (!mv.open)
+ {
+ cerr << out_name << ": error: unable to initialize changelog " <<
+ "because current version is closed" << endl;
+ throw operation_failed ();
+ }
+
+ if (!ops.init_changelog ())
+ cerr << out_name << ": info: initializing changelog with base " <<
+ "version " << mv.base << endl;
+
+ if (mv.base == mv.current)
+ g.new_edge<contains_model> (*cl, g.new_node<model> (m, g));
+ else
+ {
+ // In this case we have to create an empty model at the base
+ // version and a changeset. We do it this way instead of putting
+ // everything into the base model in order to support adding new
+ // header files to the project.
+ //
+ cerr << out_name << ": warning: base and current versions " <<
+ "differ; assuming base model is empty" << endl;
+
+ model& nm (g.new_node<model> (mv.base));
+ g.new_edge<contains_model> (*cl, nm);
+ changeset& c (diff (nm, m, *cl, in_name, ops, &mv));
+
+ if (!c.names_empty ())
+ {
+ g.new_edge<alters_model> (c, nm);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+ }
+
+ return cl;
+ }
+
+ // Get the changelog base and current versions and do some sanity
+ // checks.
+ //
+ version bver (old->model ().version ());
+ version cver (
+ old->contains_changeset_empty ()
+ ? bver
+ : old->contains_changeset_back ().changeset ().version ());
+
+ if (mv.base < bver)
+ {
+ cerr << in_name << ": error: latest changelog base version is " <<
+ "greater than model base version" << endl;
+ throw operation_failed ();
+ }
+
+ if (mv.current < cver)
+ {
+ cerr << in_name << ": error: latest changelog current version is " <<
+ "greater than model current version" << endl;
+ throw operation_failed ();
+ }
+
+ // Build the new changelog.
+ //
+ model& oldm (old->model ());
+
+ // Now we have a case with a "real" old model (i.e., non-empty
+ // and with version older than current) as well as zero or more
+ // changeset.
+ //
+ //
+ model* last (&g.new_node<model> (oldm, g));
+ model* base (bver == mv.base && mv.base != mv.current ? last : 0);
+ if (base != 0)
+ g.new_edge<contains_model> (*cl, *base);
+
+ for (changelog::contains_changeset_iterator i (
+ old->contains_changeset_begin ());
+ i != old->contains_changeset_end (); ++i)
+ {
+ changeset& cs (i->changeset ());
+
+ // Don't copy the changeset for the current version. Instead, we
+ // will re-create it from scratch.
+ //
+ if (cs.version () == mv.current)
+ break;
+
+ model& prev (*last);
+ last = &patch (prev, cs, g);
+
+ if (base == 0)
+ {
+ if (last->version () == mv.base)
+ {
+ base = last;
+ g.new_edge<contains_model> (*cl, *base);
+ }
+ else if (last->version () > mv.base)
+ {
+ // We have a gap. Plug it with an empty base model. We will
+ // also need to create a new changeset for this step.
+ //
+ base = &g.new_node<model> (mv.base);
+ g.new_edge<contains_model> (*cl, *base);
+
+ changeset& c (diff (*base, *last, *cl, in_name, ops, 0));
+ if (!c.names_empty ())
+ {
+ g.new_edge<alters_model> (c, *base);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+
+ continue;
+ }
+ }
+
+ // Copy the changeset unless it is below or at our base version.
+ //
+ if (last->version () <= mv.base)
+ continue;
+
+ changeset& c (
+ g.new_node<changeset> (
+ cs,
+ cl->contains_changeset_empty ()
+ ? static_cast<qscope&> (*base) // Cannot be NULL.
+ : cl->contains_changeset_back ().changeset (),
+ g));
+
+ g.new_edge<alters_model> (c, prev);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+
+ // If we still haven't found the new base model, then it means it
+ // has version greater than any changeset we have seen.
+ //
+ if (base == 0)
+ {
+ if (mv.base == mv.current)
+ base = &g.new_node<model> (m, g);
+ else
+ {
+ // Fast-forward the latest model to the new base.
+ //
+ base = last;
+ base->version (mv.base);
+ }
+
+ g.new_edge<contains_model> (*cl, *base);
+ }
+
+ // If the current version is closed, make sure the model hasn't
+ // changed.
+ //
+ if (!mv.open)
+ {
+ // If the last changeset has the current version, then apply it.
+ //
+ model* om (last);
+ if (!old->contains_changeset_empty ())
+ {
+ changeset& c (old->contains_changeset_back ().changeset ());
+ if (c.version () == mv.current)
+ om = &patch (*last, c, g);
+ }
+
+ changeset& c (diff (*om, m, *cl, in_name, ops, &mv));
+
+ if (!c.names_empty ())
+ {
+ qnames& n (*c.names_begin ());
+
+ cerr << out_name << ": error: current version is closed" << endl;
+ cerr << out_name << ": info: first new change is " <<
+ n.nameable ().kind () << " '" << n.name () << "'" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Add a changeset for the current version unless it is the same
+ // as the base version.
+ //
+ if (mv.base != mv.current)
+ {
+ // Add it even if it is empty. This can be useful, for example,
+ // for data-only migrations were the user relies on the database
+ // version being updated in the version table.
+ //
+ changeset& c (diff (*last, m, *cl, in_name, ops, &mv));
+ g.new_edge<alters_model> (c, *last);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+
+ return cl;
+ }
+ }
+}
diff --git a/odb/odb/relational/common-query.cxx b/odb/odb/relational/common-query.cxx
new file mode 100644
index 0000000..53321ce
--- /dev/null
+++ b/odb/odb/relational/common-query.cxx
@@ -0,0 +1,169 @@
+// file : odb/relational/common-query.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/common-query.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ // query_alias_traits
+ //
+
+ void query_alias_traits::
+ generate_decl_body ()
+ {
+ os << "static const char table_name[];";
+ }
+
+ void query_alias_traits::
+ generate_def (semantics::data_member& m, semantics::class_& c)
+ {
+ // Come up with a table alias. Generally, we want it to be based
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
+ //
+ string alias;
+ {
+ string n;
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+ }
+
+ generate_def (public_name (m), c, alias);
+ }
+
+ void query_alias_traits::
+ generate_def (string const& tag, semantics::class_& c, string const& alias)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+ semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+
+ if (poly_derived)
+ generate_def (tag, *poly_base, alias);
+
+ os << "const char alias_traits<"
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>::" << endl
+ << "table_name[] = ";
+
+ if (poly_root != 0)
+ os << strlit (quote_id (alias + "_" + table_name (c).uname ()));
+ else
+ os << strlit (quote_id (alias));
+
+ os << ";"
+ << endl;
+ }
+
+ entry<query_alias_traits> query_alias_traits_;
+
+
+ // query_columns_base
+ //
+
+ entry<query_columns_base> query_columns_base_;
+
+ // query_columns
+ //
+
+ void query_columns::
+ column_ctor (string const& type, string const& name, string const& base)
+ {
+ os << name << " (";
+
+ if (multi_dynamic)
+ os << "odb::query_column< " << type << " >& qc," << endl;
+
+ os << "const char* t, const char* c, const char* conv)" << endl
+ << " : " << base << " (" << (multi_dynamic ? "qc, " : "") <<
+ "t, c, conv)"
+ << "{"
+ << "}";
+ }
+
+ void query_columns::
+ column_ctor_args_extra (semantics::data_member&)
+ {
+ }
+
+ void query_columns::
+ column_common (semantics::data_member& m,
+ string const& type,
+ string const& column,
+ string const& suffix)
+ {
+ string name (public_name (m));
+
+ if (decl_)
+ {
+ string type_id (database_type_id (m));
+
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef" << endl
+ << db << "::query_column<" << endl
+ << " " << db << "::value_traits<" << endl
+ << " " << type << "," << endl
+ << " " << type_id << " >::query_type," << endl
+ << " " << type_id << " >" << endl
+ << name << suffix << ";"
+ << endl;
+ }
+ else
+ {
+ // Note that here we don't use suffix.
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << "const typename " << tmpl << "::" << name << "_type_" << endl
+ << tmpl << "::" << endl
+ << name << " (";
+
+ // Pass common query column for registration.
+ //
+ if (multi_dynamic)
+ {
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_common, typename A::common_traits >" +
+ scope_;
+
+ os << tmpl << "::" << name << "," << endl;
+ }
+
+ os << "A::" << "table_name, " << strlit (quote_id (column));
+
+ string const& conv (convert_to_expr (column_type (), m));
+ os << ", " << (conv.empty () ? "0" : strlit (conv));
+
+ column_ctor_args_extra (m);
+
+ os << ");"
+ << endl;
+ }
+ }
+}
diff --git a/odb/odb/relational/common-query.hxx b/odb/odb/relational/common-query.hxx
new file mode 100644
index 0000000..c29df6b
--- /dev/null
+++ b/odb/odb/relational/common-query.hxx
@@ -0,0 +1,63 @@
+// file : odb/relational/common-query.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_COMMON_QUERY_HXX
+#define ODB_RELATIONAL_COMMON_QUERY_HXX
+
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ //
+ //
+ struct query_alias_traits: ::query_alias_traits, virtual context
+ {
+ typedef query_alias_traits base_impl;
+
+ query_alias_traits (base const& x): base (x) {}
+
+ virtual void
+ generate_decl_body ();
+
+ virtual void
+ generate_def (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_def (string const& tag, semantics::class_&, string const& alias);
+ };
+
+ //
+ //
+ struct query_columns_base: ::query_columns_base, virtual context
+ {
+ typedef query_columns_base base_impl;
+
+ query_columns_base (base const& x): base (x) {const_ = "const ";}
+ };
+
+ //
+ //
+ struct query_columns: ::query_columns, virtual context
+ {
+ typedef query_columns base_impl;
+
+ query_columns (base const& x): base (x) {const_ = "const ";}
+
+ virtual string
+ database_type_id (semantics::data_member&) = 0;
+
+ virtual void
+ column_ctor (string const& type, string const& name, string const& base);
+
+ virtual void
+ column_ctor_args_extra (semantics::data_member&);
+
+ virtual void
+ column_common (semantics::data_member&,
+ string const& type,
+ string const& column,
+ string const& suffix);
+ };
+}
+
+#endif // ODB_RELATIONAL_COMMON_QUERY_HXX
diff --git a/odb/odb/relational/common.cxx b/odb/odb/relational/common.cxx
new file mode 100644
index 0000000..5c9126c
--- /dev/null
+++ b/odb/odb/relational/common.cxx
@@ -0,0 +1,27 @@
+// file : odb/relational/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ // member_image_type
+ //
+ string member_image_type::
+ image_type (semantics::data_member&)
+ {
+ assert (false);
+ return string ();
+ }
+
+ // member_database_type_id
+ //
+ string member_database_type_id::
+ database_type_id (semantics::data_member&)
+ {
+ assert (false);
+ return string ();
+ }
+}
diff --git a/odb/odb/relational/common.hxx b/odb/odb/relational/common.hxx
new file mode 100644
index 0000000..01266a0
--- /dev/null
+++ b/odb/odb/relational/common.hxx
@@ -0,0 +1,273 @@
+// file : odb/relational/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_COMMON_HXX
+#define ODB_RELATIONAL_COMMON_HXX
+
+#include <set>
+#include <cassert>
+
+#include <odb/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ struct member_base: traversal::data_member, virtual context
+ {
+ typedef member_base base;
+
+ member_base (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix,
+ object_section* section = 0)
+ : type_override_ (type),
+ custom_override_ (ct),
+ fq_type_override_ (fq_type),
+ key_prefix_ (key_prefix),
+ section_ (section),
+ top_level_ (false)
+ {
+ }
+
+ member_base (string const& var,
+ semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix,
+ object_section* section = 0)
+ : var_override_ (var),
+ type_override_ (type),
+ custom_override_ (ct),
+ fq_type_override_ (fq_type),
+ key_prefix_ (key_prefix),
+ section_ (section),
+ top_level_ (false)
+ {
+ }
+
+ protected:
+ // For virtual inheritance only. Should not be actually called.
+ //
+ member_base ();
+
+ protected:
+ string var_override_;
+ semantics::type* type_override_;
+ const custom_cxx_type* custom_override_;
+ string fq_type_override_;
+ string key_prefix_;
+ object_section* section_;
+
+ // True during the top-level call of pre() and post() below. Note that
+ // call via another tarverser (e.g., for a class) is not considered top-
+ // level.
+ //
+ bool top_level_;
+ };
+
+ // Template argument is the database SQL type (sql_type).
+ //
+ template <typename T>
+ struct member_base_impl: virtual member_base
+ {
+ typedef member_base_impl base_impl;
+
+ member_base_impl (base const& x): base (x) {}
+
+ protected:
+ member_base_impl () {}
+
+ public:
+ virtual T const&
+ member_sql_type (semantics::data_member&) = 0;
+
+ void
+ traverse (semantics::data_member& m, bool top_level)
+ {
+ top_level_ = top_level;
+ traverse (m);
+ top_level_ = false;
+ }
+
+ struct member_info
+ {
+ semantics::data_member& m; // Member.
+ semantics::type& t; // Cvr-unqualified member C++ type, note
+ // that m.type () may not be the same as t.
+ const custom_cxx_type* ct; // Translation used for t, if any.
+ semantics::class_* ptr; // Pointed-to object if m is an object
+ // pointer. In this case t is the id type
+ // while fq_type_ is the pointer fq-type.
+ semantics::type* wrapper; // Wrapper type if member is a composite or
+ // container wrapper, also cvr-unqualified.
+ // In this case t is the wrapped type.
+ bool cq; // True if the original (wrapper) type
+ // is const-qualified.
+ T const* st; // Member SQL type (only simple values).
+ string& var; // Member variable name with trailing '_'.
+
+ // C++ type fq-name.
+ //
+ string
+ fq_type (bool unwrap = true) const
+ {
+ semantics::names* hint;
+
+ if (wrapper != 0 && unwrap)
+ {
+ // Use the hint from the wrapper unless the wrapped type
+ // is qualified.
+ //
+ hint = wrapper->get<semantics::names*> ("wrapper-hint");
+ utype (*context::wrapper (*wrapper), hint);
+ return t.fq_name (hint);
+ }
+
+ // Use the original type from 'm' instead of 't' since the hint may
+ // be invalid for a different type. Plus, if a type is overriden,
+ // then the fq_type must be as well.
+ //
+ if (ptr != 0)
+ {
+ semantics::type& t (utype (*id_member (*ptr), hint));
+ return t.fq_name (hint);
+ }
+ else if (fq_type_.empty ())
+ {
+ semantics::type& t (utype (m, hint));
+ return t.fq_name (hint);
+ }
+ else
+ // If we are translated, then fq_type_ contains the original type.
+ //
+ return ct == 0 ? fq_type_ : t.fq_name (ct->as_hint);
+ }
+
+ string
+ ptr_fq_type () const
+ {
+ assert (ptr != 0);
+
+ if (fq_type_.empty ())
+ {
+ // If type is overridden so should fq_type so it is safe to
+ // get the type from the member.
+ //
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+ return t.fq_name (hint);
+ }
+ else
+ return fq_type_;
+ }
+
+ string const& fq_type_;
+
+ member_info (semantics::data_member& m_,
+ semantics::type& t_,
+ const custom_cxx_type* ct_,
+ semantics::type* wrapper_,
+ bool cq_,
+ string& var_,
+ string const& fq_type)
+ : m (m_),
+ t (t_),
+ ct (ct_),
+ ptr (0),
+ wrapper (wrapper_),
+ cq (cq_),
+ st (0),
+ var (var_),
+ fq_type_ (fq_type)
+ {
+ }
+ };
+
+ bool
+ container (member_info& mi)
+ {
+ // This cannot be a container if we have a type override.
+ //
+ return type_override_ == 0 && context::container (mi.m);
+ }
+
+ // The false return value indicates that no further callbacks should be
+ // called for this member.
+ //
+ virtual bool
+ pre (member_info&) {return true;}
+
+ virtual void
+ post (member_info&) {}
+
+ // Note: calling these directly will mess up the top_level logic.
+ //
+ protected:
+ virtual void
+ traverse_composite (member_info&) {}
+
+ virtual void
+ traverse_container (member_info&) {}
+
+ // Note that by default traverse_pointer() will traverse the
+ // pointed-to object id type.
+ //
+ virtual void
+ traverse_pointer (member_info&);
+
+ virtual void
+ traverse_simple (member_info&) {}
+
+ private:
+ virtual void
+ traverse (semantics::data_member&);
+ };
+
+ //
+ //
+ struct member_image_type: virtual member_base
+ {
+ typedef member_image_type base;
+
+ member_image_type (): member_base (0, 0, string (), string ()) {}
+
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type = string (),
+ string const& key_prefix = string ())
+ : member_base (type, ct, fq_type, key_prefix) {}
+
+ // Has to be overriden.
+ //
+ virtual string
+ image_type (semantics::data_member&);
+ };
+
+ //
+ //
+ struct member_database_type_id: virtual member_base
+ {
+ typedef member_database_type_id base;
+
+ member_database_type_id (): member_base (0, 0, string (), string ()) {}
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type = string (),
+ string const& key_prefix = string ())
+ : member_base (type, ct, fq_type, key_prefix) {}
+
+ // Has to be overriden.
+ //
+ virtual string
+ database_type_id (semantics::data_member&);
+ };
+}
+
+#include <odb/relational/common.txx>
+
+// Other common parts.
+//
+#include <odb/relational/common-query.hxx>
+
+#endif // ODB_RELATIONAL_COMMON_HXX
diff --git a/odb/odb/relational/common.txx b/odb/odb/relational/common.txx
new file mode 100644
index 0000000..82a4a4a
--- /dev/null
+++ b/odb/odb/relational/common.txx
@@ -0,0 +1,125 @@
+// file : odb/relational/common.txx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace relational
+{
+ //
+ // member_base_impl
+ //
+
+ template <typename T>
+ void member_base_impl<T>::
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ string var;
+
+ if (!var_override_.empty ())
+ var = var_override_;
+ else
+ {
+ string const& name (m.name ());
+ var = name + (name[name.size () - 1] == '_' ? "" : "_");
+ }
+
+ bool cq (type_override_ != 0 ? false : const_member (m));
+ const custom_cxx_type* ct (type_override_ != 0 ? custom_override_ : 0);
+ semantics::type& t (type_override_ != 0
+ ? *type_override_
+ : utype (m, &ct));
+
+ semantics::type* cont;
+ if (semantics::class_* c = object_pointer (t))
+ {
+ // A pointer in view might point to an object without id.
+ //
+ data_member_path* id (id_member (*c));
+ semantics::type& t (id != 0 ? utype (*id, &ct) : utype (m, &ct));
+ semantics::class_* comp (id != 0 ? composite_wrapper (t) : 0);
+
+ member_info mi (m,
+ (comp != 0 ? *comp : t),
+ ct,
+ (comp != 0 && wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_); // Pointer type.
+
+ mi.ptr = c;
+
+ // Pointer in views aren't really a "column".
+ //
+ if (!view_member (m) && comp == 0)
+ mi.st = &member_sql_type (m);
+
+ if (pre (mi))
+ {
+ traverse_pointer (mi);
+ post (mi);
+ }
+ }
+ else if (semantics::class_* c = composite_wrapper (t))
+ {
+ // If t is a wrapper, pass the wrapped type. Also pass the
+ // original, wrapper type.
+ //
+ member_info mi (m,
+ *c,
+ ct,
+ (wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_composite (mi);
+ post (mi);
+ }
+ }
+ // This cannot be a container if we have a type override.
+ //
+ else if (type_override_ == 0 && (cont = context::container (m)))
+ {
+ // The same unwrapping logic as for composite values.
+ //
+ member_info mi (m,
+ *cont,
+ 0, // Cannot be mapped.
+ (wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_container (mi);
+ post (mi);
+ }
+ }
+ else
+ {
+ member_info mi (m, t, ct, 0, cq, var, fq_type_override_);
+ mi.st = &member_sql_type (m);
+
+ if (pre (mi))
+ {
+ traverse_simple (mi);
+ post (mi);
+ }
+ }
+ }
+
+ template <typename T>
+ void member_base_impl<T>::
+ traverse_pointer (member_info& mi)
+ {
+ if (!view_member (mi.m)) // Not really "as if" pointed-to id member.
+ {
+ if (composite (mi.t)) // Already unwrapped.
+ traverse_composite (mi);
+ else
+ traverse_simple (mi);
+ }
+ }
+}
diff --git a/odb/odb/relational/context.cxx b/odb/odb/relational/context.cxx
new file mode 100644
index 0000000..3fba69b
--- /dev/null
+++ b/odb/odb/relational/context.cxx
@@ -0,0 +1,169 @@
+// file : odb/relational/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_),
+ model (current ().model),
+ generate_grow (current ().generate_grow),
+ need_alias_as (current ().need_alias_as),
+ insert_send_auto_id (current ().insert_send_auto_id),
+ delay_freeing_statement_result (current ().delay_freeing_statement_result),
+ need_image_clone (current ().need_image_clone),
+ generate_bulk (current ().generate_bulk),
+ global_index (current ().global_index),
+ global_fkey (current ().global_fkey),
+ bind_vector (data_->bind_vector_),
+ truncated_vector (data_->truncated_vector_)
+ {
+ }
+
+ context::
+ context (data* d, sema_rel::model* m)
+ : data_ (d),
+ model (m),
+ bind_vector (data_->bind_vector_),
+ truncated_vector (data_->truncated_vector_)
+ {
+ assert (current_ == 0);
+ current_ = this;
+ }
+
+ string context::
+ index_name (qname const& table, string const& base)
+ {
+ string n;
+
+ if (options.index_suffix ().count (db) != 0)
+ n = base + options.index_suffix ()[db];
+ else
+ n = compose_name (base, "i");
+
+ // If this database has global index names, then add the table
+ // name as a prefix (the schema, if needed, will be added by
+ // database-specific create_index overrides).
+ //
+ if (global_index)
+ n = compose_name (table.uname (), n);
+
+ return transform_name (n, sql_name_index);
+ }
+
+ string context::
+ fkey_name (qname const& table, string const& base)
+ {
+ string n;
+
+ if (options.fkey_suffix ().count (db) != 0)
+ n = base + options.fkey_suffix ()[db];
+ else
+ n = compose_name (base, "fk");
+
+ // If this database has global index names, then add the table
+ // name as a prefix (the schema, if needed, will be added by
+ // database-specific create_foreign_key overrides).
+ //
+ if (global_fkey)
+ n = compose_name (table.uname (), n);
+
+ return transform_name (n, sql_name_fkey);
+ }
+
+ string context::
+ convert (string const& e, string const& c)
+ {
+ size_t p (c.find ("(?)"));
+ string r (c, 0, p);
+ r += e;
+ r.append (c, p + 3, string::npos);
+ return r;
+ }
+
+ string const& context::
+ convert_expr (string const&, semantics::data_member&, bool)
+ {
+ assert (false);
+ throw operation_failed ();
+ }
+
+ bool context::
+ grow_impl (semantics::class_&, user_section*)
+ {
+ return false;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member&)
+ {
+ return false;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&)
+ {
+ return false;
+ }
+
+ string context::
+ quote_string_impl (string const& s) const
+ {
+ string r;
+ r.reserve (s.size ());
+ r += '\'';
+
+ for (string::size_type i (0), n (s.size ()); i < n; ++i)
+ {
+ if (s[i] == '\'')
+ r += "''";
+ else
+ r += s[i];
+ }
+
+ r += '\'';
+ return r;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r += *i;
+ r += '"';
+ }
+
+ return r;
+ }
+}
diff --git a/odb/odb/relational/context.hxx b/odb/odb/relational/context.hxx
new file mode 100644
index 0000000..db9b5be
--- /dev/null
+++ b/odb/odb/relational/context.hxx
@@ -0,0 +1,289 @@
+// file : odb/relational/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_CONTEXT_HXX
+#define ODB_RELATIONAL_CONTEXT_HXX
+
+#include <odb/context.hxx>
+
+#include <odb/semantics/relational.hxx>
+#include <odb/traversal/relational.hxx>
+
+namespace relational
+{
+ namespace sema_rel = semantics::relational;
+ namespace trav_rel = traversal::relational;
+
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete,
+ statement_where // WHERE clause.
+ };
+
+ // Index.
+ //
+ struct index
+ {
+ location_t loc; // Location of this index definition.
+ std::string name; // If empty, then derive from the member name.
+ std::string type; // E.g., "UNIQUE", etc.
+ std::string method; // E.g., "BTREE", etc.
+ std::string options; // Database-specific index options.
+
+ struct member
+ {
+ location_t loc; // Location of this member specifier.
+ std::string name; // Member name, e.g., foo_, foo_.bar_.
+ data_member_path path; // Member path.
+ std::string options; // Member options, e.g., "ASC", etc.
+ };
+ typedef std::vector<member> members_type;
+
+ members_type members;
+ };
+
+ typedef std::vector<index> indexes;
+
+ // Indexes in the above vector are in location order.
+ //
+ struct index_comparator
+ {
+ bool
+ operator() (index const& x, index const& y) const
+ {
+ return x.loc < y.loc;
+ }
+ };
+
+ // Custom database type mapping.
+ //
+ struct custom_db_type
+ {
+ regex type;
+ std::string as;
+ std::string to;
+ std::string from;
+ location_t loc;
+ };
+
+ typedef std::vector<custom_db_type> custom_db_types;
+
+ class context: public virtual ::context
+ {
+ public:
+ // Return true if an object or value type has members for which
+ // the image can grow. If section is not specified, then ignore
+ // separately loaded members. Otherwise ignore members that do
+ // not belong to the section.
+ //
+ bool
+ grow (semantics::class_&, user_section* = 0);
+
+ // The same for a member's value type.
+ //
+ bool
+ grow (semantics::data_member&);
+
+ bool
+ grow (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const& key_prefix);
+
+ public:
+ // Quote SQL string.
+ //
+ string
+ quote_string (string const&) const;
+
+ // Quote SQL identifier.
+ //
+ string
+ quote_id (string const&) const;
+
+ string
+ quote_id (qname const&) const;
+
+ // Quoted column and table names.
+ //
+ string
+ column_qname (semantics::data_member& m, column_prefix const& cp) const
+ {
+ return quote_id (column_name (m, cp));
+ }
+
+ string
+ column_qname (data_member_path const& mp) const
+ {
+ return quote_id (column_name (mp));
+ }
+
+ string
+ column_qname (semantics::data_member& m,
+ string const& key_prefix,
+ string const& default_name,
+ column_prefix const& cp) const
+ {
+ return quote_id (column_name (m, key_prefix, default_name, cp));
+ }
+
+ string
+ table_qname (semantics::class_& c) const
+ {
+ return quote_id (table_name (c));
+ }
+
+ string
+ table_qname (semantics::class_& obj, data_member_path const& mp) const
+ {
+ return quote_id (table_name (obj, mp));
+ }
+
+ string
+ table_qname (semantics::data_member& m, table_prefix const& p) const
+ {
+ return quote_id (table_name (m, p));
+ }
+
+ public:
+ string
+ index_name (qname const& table, string const& base);
+
+ string
+ fkey_name (qname const& table, string const& base);
+
+ // Custom database type conversion.
+ //
+ public:
+ string
+ convert_to (string const& expr,
+ string const& sqlt,
+ semantics::data_member& m)
+ {
+ string const& conv (current ().convert_expr (sqlt, m, true));
+ return conv.empty () ? expr : convert (expr, conv);
+ }
+
+ string
+ convert_from (string const& expr,
+ string const& sqlt,
+ semantics::data_member& m)
+ {
+ string const& conv (current ().convert_expr (sqlt, m, false));
+ return conv.empty () ? expr : convert (expr, conv);
+ }
+
+ // These shortcut versions should only be used on special members
+ // (e.g., auto id, version, etc) since they may not determine the
+ // proper SQL type in other cases (prefixes, composite ids, etc).
+ //
+ string
+ convert_to (string const& expr, semantics::data_member& m)
+ {
+ return convert_to (expr, column_type (m), m);
+ }
+
+ string
+ convert_from (string const& expr, semantics::data_member& m)
+ {
+ return convert_from (expr, column_type (m), m);
+ }
+
+ // Return the conversion expression itself.
+ //
+ string const&
+ convert_to_expr (string const& sqlt, semantics::data_member& m)
+ {
+ return current ().convert_expr (sqlt, m, true);
+ }
+
+ protected:
+ virtual string const&
+ convert_expr (string const& sqlt, semantics::data_member&, bool to);
+
+ string
+ convert (string const& expr, string const& conv);
+
+ protected:
+ // The default implementation returns false.
+ //
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ // The default implementation uses the ISO quoting ('') and
+ // escapes singe quotes inside the string by double-quoting
+ // (' -> ''). Some (most?) database systems support escape
+ // sequences. We may want to provide utilize that to support
+ // things like \n, \t, etc.
+ //
+ virtual string
+ quote_string_impl (string const&) const;
+
+ // The default implementation uses the ISO quoting ("").
+ //
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ public:
+ virtual
+ ~context ();
+ context ();
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ protected:
+ struct data;
+ typedef context base_context;
+
+ context (data*, sema_rel::model*);
+
+ private:
+ static context* current_;
+
+ protected:
+ struct data: root_context::data
+ {
+ data (std::ostream& os): root_context::data (os) {}
+
+ string bind_vector_;
+ string truncated_vector_;
+ };
+ data* data_;
+
+ public:
+ sema_rel::model* model;
+
+ bool generate_grow;
+ bool need_alias_as;
+ bool insert_send_auto_id;
+ bool delay_freeing_statement_result;
+ bool need_image_clone;
+ bool generate_bulk;
+
+ bool global_index;
+ bool global_fkey;
+
+ string const& bind_vector;
+ string const& truncated_vector;
+ };
+}
+
+#include <odb/relational/context.ixx>
+
+#endif // ODB_RELATIONAL_CONTEXT_HXX
diff --git a/odb/odb/relational/context.ixx b/odb/odb/relational/context.ixx
new file mode 100644
index 0000000..abf1fb5
--- /dev/null
+++ b/odb/odb/relational/context.ixx
@@ -0,0 +1,44 @@
+// file : odb/relational/context.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace relational
+{
+ inline bool context::
+ grow (semantics::class_& c, user_section* s)
+ {
+ return current ().grow_impl (c, s);
+ }
+
+ inline bool context::
+ grow (semantics::data_member& m)
+ {
+ return current ().grow_impl (m);
+ }
+
+ inline bool context::
+ grow (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ return current ().grow_impl (m, t, ct, kp);
+ }
+
+ inline context::string context::
+ quote_string (string const& str) const
+ {
+ return current ().quote_string_impl (str);
+ }
+
+ inline context::string context::
+ quote_id (string const& id) const
+ {
+ return current ().quote_id_impl (qname (id));
+ }
+
+ inline context::string context::
+ quote_id (qname const& id) const
+ {
+ return current ().quote_id_impl (id);
+ }
+}
diff --git a/odb/odb/relational/generate.hxx b/odb/odb/relational/generate.hxx
new file mode 100644
index 0000000..e597fb8
--- /dev/null
+++ b/odb/odb/relational/generate.hxx
@@ -0,0 +1,81 @@
+// file : odb/relational/generate.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_GENERATE_HXX
+#define ODB_RELATIONAL_GENERATE_HXX
+
+#include <string>
+#include <libcutl/shared-ptr.hxx>
+
+#include <odb/context.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
+namespace relational
+{
+ namespace header
+ {
+ void
+ generate ();
+ }
+
+ namespace inline_
+ {
+ void
+ generate ();
+ }
+
+ namespace source
+ {
+ void
+ generate ();
+ }
+
+ namespace model
+ {
+ cutl::shared_ptr<semantics::relational::model>
+ generate ();
+ }
+
+ namespace changelog
+ {
+ // Returns NULL if the changelog is unchanged.
+ //
+ cutl::shared_ptr<semantics::relational::changelog>
+ generate (semantics::relational::model&,
+ model_version const&,
+ semantics::relational::changelog* old, // Can be NULL.
+ std::string const& in_name,
+ std::string const& out_name,
+ options const&);
+ }
+
+ namespace schema
+ {
+ void
+ generate_prologue ();
+
+ void
+ generate_epilogue ();
+
+ void
+ generate_drop ();
+
+ void
+ generate_create ();
+
+ void
+ generate_migrate_pre (semantics::relational::changeset&);
+
+ void
+ generate_migrate_post (semantics::relational::changeset&);
+
+ // Generate embedded schema.
+ //
+ void
+ generate_source (semantics::relational::changelog*);
+ }
+}
+
+#endif // ODB_RELATIONAL_GENERATE_HXX
diff --git a/odb/odb/relational/header.cxx b/odb/odb/relational/header.cxx
new file mode 100644
index 0000000..364d48e
--- /dev/null
+++ b/odb/odb/relational/header.cxx
@@ -0,0 +1,1146 @@
+// file : odb/relational/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+void relational::header::class1::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ data_member* idb (id ? id->back () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (context::optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // pointer_query_columns & query_columns
+ //
+ if (options.generate_query ())
+ {
+ // If we don't have object pointers, then also generate
+ // query_columns (in this case pointer_query_columns and
+ // query_columns are the same and the former inherits from
+ // the latter). Otherwise we have to postpone query_columns
+ // generation until the second pass to deal with forward-
+ // declared objects.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ pointer_query_columns_type_->traverse (c);
+ }
+
+ // object_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits_impl< " << type << ", " <<
+ "id_" << db << " >:" << endl
+ << " public access::object_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ object_public_extra_pre (c);
+
+ // For dynamic multi-database support also generate common traits
+ // alias (used in query aliasing).
+ //
+ if (options.generate_query () && multi_dynamic)
+ {
+ os << "typedef access::object_traits_impl< " << type << ", " <<
+ "id_common > common_traits;"
+ << endl;
+ }
+
+ // Polymorphic root_traits, base_traits, and discriminator_image_type.
+ //
+ if (poly)
+ {
+ if (!abst)
+ os << "typedef polymorphic_entry<object_type, id_" << db <<
+ "> entry_type;";
+
+ os << "typedef object_traits_impl<root_type, id_" << db << "> " <<
+ "root_traits;";
+
+ if (poly_derived)
+ {
+ os << "typedef object_traits_impl<base_type, id_" << db << "> " <<
+ "base_traits;"
+ << endl;
+ }
+ else
+ {
+ os << endl
+ << "struct discriminator_image_type"
+ << "{";
+
+ discriminator_image_member_->traverse (*discriminator);
+
+ if (opt != 0)
+ version_image_member_->traverse (*opt);
+
+ os << "std::size_t version;"
+ << "};";
+ }
+ }
+
+ // id_image_type
+ //
+ if (id != 0)
+ {
+ if (base_id)
+ {
+ if (poly_derived)
+ os << "typedef root_traits::id_image_type id_image_type;"
+ << endl;
+ else
+ {
+ semantics::class_& b (
+ dynamic_cast<semantics::class_&> (idf->scope ()));
+
+ os << "typedef object_traits_impl< " << class_fq_name (b) << ", " <<
+ "id_" << db << " >::id_image_type id_image_type;"
+ << endl;
+ }
+ }
+ else
+ {
+ os << "struct id_image_type"
+ << "{";
+
+ id_image_member_->traverse (*idb);
+
+ if (opt != 0)
+ version_image_member_->traverse (*opt);
+
+ os << "std::size_t version;"
+ << "};";
+ }
+ }
+
+ // Polymorphic map.
+ //
+ if (poly)
+ {
+ if (!poly_derived)
+ os << "static map_type* map;";
+
+ os << "static const " << (abst ? "abstract_" : "") << "info_type info;"
+ << endl;
+ }
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ // Extra (container, section) statement cache (forward declaration).
+ //
+ if (!reuse_abst && id != 0)
+ os << "struct extra_statement_cache_type;"
+ << endl;
+
+ //
+ // Containers (abstract and concrete).
+ //
+
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ //
+ // Sections (abstract and concrete).
+ //
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ instance<section_traits> t (c);
+ t->traverse (*i);
+ }
+
+ //
+ // Query (abstract and concrete).
+ //
+
+ if (options.generate_query ())
+ {
+ // Generate object pointer tags here unless we are generating dynamic
+ // multi-database support, in which case they generated in object_traits.
+ //
+ if (!multi_dynamic && has_a (c, test_pointer | exclude_base))
+ {
+ query_tags t; // Not customizable.
+ t.traverse (c);
+ }
+ }
+
+ //
+ // Functions (abstract and concrete).
+ //
+
+ if (id != 0 || !reuse_abst)
+ os << "using object_traits<object_type>::id;"
+ << endl;
+
+ if (opt != 0)
+ os << "using object_traits<object_type>::version;"
+ << endl;
+
+ if (!poly_derived && id != 0)
+ {
+ if (auto_id)
+ os << "static id_type" << endl
+ << "id (const id_image_type&);"
+ << endl;
+
+ if (options.generate_query ())
+ os << "static id_type" << endl
+ << "id (const image_type&);"
+ << endl;
+
+ if (opt != 0)
+ os << "static version_type" << endl
+ << "version (const image_type&);"
+ << endl;
+ }
+
+ // discriminator()
+ //
+ if (poly && !poly_derived)
+ os << "static discriminator_type" << endl
+ << "discriminator (const image_type&);"
+ << endl;
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ // For derived classes in a polymorphic hierarchy, grow() will
+ // check bases up to the specified depth. If one of the base
+ // images has grown, then it will increment its version. But
+ // the return value only indicates the state of this image,
+ // excluding polymorphic bases (in other words, it is possible
+ // that one of the bases has grown but this function returns
+ // false).
+ //
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+ }
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl;
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // we get the the external id binding.
+ //
+ if (poly_derived)
+ os << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl;
+
+ os << "image_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // bind (id_image_type)
+ //
+ if (id != 0)
+ {
+ os << "static void" << endl
+ << "bind (" << bind_vector << ", id_image_type&" <<
+ (opt != 0 ? ", bool bind_version = true" : "") << ");"
+ << endl;
+ }
+
+ // init (image, object)
+ //
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&," << endl
+ << "const object_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // init (object, image)
+ //
+ os << "static void" << endl
+ << "init (object_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+
+ // init (id_image, id)
+ //
+ if (id != 0)
+ {
+ os << "static void" << endl
+ << "init (id_image_type&, const id_type&" <<
+ (opt != 0 ? ", const version_type* = 0" : "") << ");"
+ << endl;
+ }
+
+ if (poly_derived)
+ {
+ // check_version
+ //
+ os << "static bool" << endl
+ << "check_version (const std::size_t*, const image_type&);"
+ << endl;
+
+ // update_version
+ //
+ os << "static void" << endl
+ << "update_version (std::size_t*, const image_type&, " <<
+ db << "::binding*);"
+ << endl;
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ {
+ object_public_extra_post (c);
+ os << "};";
+ return;
+ }
+
+ column_count_type const& cc (column_count (c));
+
+ // Statements typedefs.
+ //
+ if (poly)
+ {
+ if (poly_derived)
+ os << "typedef" << endl
+ << db << "::polymorphic_derived_object_statements" <<
+ "<object_type>" << endl
+ << "statements_type;"
+ << endl
+ << "typedef" << endl
+ << db << "::polymorphic_root_object_statements<root_type>" << endl
+ << "root_statements_type;"
+ << endl;
+ else
+ os << "typedef" << endl
+ << db << "::polymorphic_root_object_statements<object_type>" << endl
+ << "statements_type;"
+ << endl
+ << "typedef statements_type root_statements_type;"
+ << endl;
+ }
+ else
+ {
+ if (id != 0)
+ os << "typedef " << db << "::object_statements<object_type> " <<
+ "statements_type;"
+ << endl;
+ else
+ os << "typedef " << db << "::no_id_object_statements<object_type> " <<
+ "statements_type;"
+ << endl;
+ }
+
+ //
+ // Query (concrete).
+ //
+
+ if (options.generate_query ())
+ {
+ // query_base_type
+ //
+ os << "typedef " << db << "::query_base query_base_type;"
+ << endl;
+ }
+
+ //
+ // Containers (concrete).
+ //
+
+ //
+ // Sections (concrete).
+ //
+
+ // column_count
+ //
+ os << "static const std::size_t column_count = " << cc.total << "UL;"
+ << "static const std::size_t id_column_count = " << cc.id << "UL;"
+ << "static const std::size_t inverse_column_count = " <<
+ cc.inverse << "UL;"
+ << "static const std::size_t readonly_column_count = " <<
+ cc.readonly << "UL;"
+ << "static const std::size_t managed_optimistic_column_count = " <<
+ cc.optimistic_managed << "UL;";
+
+ if (poly && !poly_derived)
+ os << "static const std::size_t discriminator_column_count = " <<
+ cc.discriminator << "UL;";
+
+ os << endl
+ << "static const std::size_t separate_load_column_count = " <<
+ cc.separate_load << "UL;"
+ << "static const std::size_t separate_update_column_count = " <<
+ cc.separate_update << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ os << "static const char persist_statement[];";
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ {
+ char const* n (abst ? "1" : "depth");
+
+ os << "static const char* const find_statements[" << n << "];"
+ << "static const std::size_t find_column_counts[" << n << "];";
+ }
+ else
+ {
+ os << "static const char find_statement[];";
+
+ if (poly)
+ os << "static const char find_discriminator_statement[];";
+ }
+
+ if (cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update)
+ os << "static const char update_statement[];";
+
+ os << "static const char erase_statement[];";
+
+ if (opt != 0 && !poly_derived)
+ os << "static const char optimistic_erase_statement[];";
+ }
+
+ if (options.generate_query ())
+ {
+ os << "static const char query_statement[];"
+ << "static const char erase_query_statement[];"
+ << endl
+ << "static const char table_name[];";
+ }
+
+ os << endl;
+
+ //
+ // Functions (concrete).
+ //
+
+ // persist ()
+ //
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ if (c.count ("bulk-persist"))
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") <<
+ "object_type**, std::size_t, multiple_exceptions&);"
+ << endl;
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "static pointer_type" << endl
+ << "find (database&, const id_type&);"
+ << endl;
+
+ // find (id, obj)
+ //
+ os << "static bool" << endl
+ << "find (database&, const id_type&, object_type&";
+
+ if (poly)
+ os << ", bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ // reload ()
+ //
+ os << "static bool" << endl
+ << "reload (database&, object_type&";
+
+ if (poly)
+ os << ", bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ // update ()
+ //
+ // In case of a polymorphic object, we generate update() even if it is
+ // readonly since the potentially-readwrite base will rely on it to
+ // initialize the id image.
+ //
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "static void" << endl
+ << "update (database&, const object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ if (c.count ("bulk-update"))
+ os << "static void" << endl
+ << "update (database&, const object_type**, std::size_t, " <<
+ "multiple_exceptions&);"
+ << endl;
+ }
+
+ // erase ()
+ //
+ os << "static void" << endl
+ << "erase (database&, const id_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ if (c.count ("bulk-erase"))
+ {
+ os << "static std::size_t" << endl
+ << "erase (database&, const id_type**, std::size_t, " <<
+ "multiple_exceptions&);"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type**, std::size_t, " <<
+ "multiple_exceptions&);"
+ << endl;
+ }
+
+ // Sections.
+ //
+ // We treat all polymorphic sections as (potentially) having something
+ // to load or to update since we cannot predict what will be added to
+ // them in overrides.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "load (connection&, object_type&, section&" <<
+ (poly ? ", const info_type* = 0" : "") << ");"
+ << endl;
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "update (connection&, const object_type&, const section&" <<
+ (poly ? ", const info_type* = 0" : "") << ");"
+ << endl;
+ }
+
+ // query ()
+ //
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ {
+ os << "static result<object_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static result<object_type>" << endl
+ << "query (database&, const odb::query_base&);"
+ << endl;
+ }
+
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const odb::query_base&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, " <<
+ "const odb::query_base&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+ }
+
+ object_public_extra_post (c);
+
+ // Implementation details.
+ //
+ os << "public:" << endl;
+
+ if (id != 0)
+ {
+ // Load the object image.
+ //
+ os << "static bool" << endl
+ << "find_ (statements_type&," << endl
+ << "const id_type*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived && !abst)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+
+ // Load the rest of the object (containers, etc). Expects the id
+ // image in the object statements to be initialized to the object
+ // id.
+ //
+ os << "static void" << endl
+ << "load_ (statements_type&," << endl
+ << "object_type&," << endl
+ << "bool reload";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+ }
+
+ // discriminator_ ()
+ //
+ if (poly && !poly_derived)
+ {
+ os << "static void" << endl
+ << "discriminator_ (statements_type&," << endl
+ << "const id_type&," << endl
+ << "discriminator_type*";
+
+ if (opt != 0)
+ os << "," << endl
+ << "version_type* = 0";
+
+ os << ");"
+ << endl;
+ }
+
+ // Load the dynamic part of the object. Depth inidicates where
+ // the dynamic part starts. Expects the id image in the object
+ // statements to be initialized to the object id. We don't need
+ // it if we are poly-abstract.
+ //
+ if (poly_derived && !abst)
+ os << "static void" << endl
+ << "load_ (database&, root_type&, std::size_t);"
+ << endl;
+
+ // Image chain manipulation.
+ //
+ if (poly && need_image_clone && options.generate_query ())
+ {
+ os << "static root_traits::image_type&" << endl
+ << "root_image (image_type&);"
+ << endl;
+
+ // Note that the original image is non-const since for some databases
+ // the copy "steals" stuff from the original (e.g., LOB descriptors in
+ // Oracle).
+ //
+ os << "static image_type*" << endl
+ << "clone_image (image_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "copy_image (image_type&, image_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "free_image (image_type*);"
+ << endl;
+ }
+
+ os << "};";
+
+
+ // object_traits_impl< , id_common>
+ //
+ // Note that it is not generated for reuse-abstract classes.
+ //
+ if (options.default_database_specified () &&
+ options.default_database () == db)
+ {
+ os << "template <>" << endl
+ << "class access::object_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::object_traits_impl< " << type << ", " <<
+ "id_" << db << " >"
+ << "{"
+ << "};";
+ }
+}
+
+void relational::header::class1::
+traverse_view (type& c)
+{
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ size_t columns (column_count (c).total);
+ size_t obj_count (c.get<size_t> ("object-count"));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // view_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_" << db << " >:" << endl
+ << " public access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ view_public_extra_pre (c);
+
+ // For dynamic multi-database support also generate common traits
+ // alias (used in query aliasing).
+ //
+ if (multi_dynamic)
+ {
+ os << "typedef access::view_traits_impl< " << type << ", " <<
+ "id_common > common_traits;"
+ << endl;
+ }
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ os << "typedef " << db << "::view_statements<view_type> statements_type;"
+ << endl;
+
+ //
+ // Query.
+ //
+
+ // Generate associated object tags here unless we are generating dynamic
+ // multi-database support, in which case they generated in object_traits.
+ //
+ if (!multi_dynamic)
+ {
+ query_tags t; // Not customizable.
+ t.traverse (c);
+ }
+
+ // query_base_type and query_columns (definition generated by class2).
+ //
+ os << "typedef " << db << "::query_base query_base_type;"
+ << "struct query_columns";
+
+ if (obj_count == 0)
+ os << "{"
+ << "};";
+ else
+ os << ";"
+ << endl;
+
+ os << "static const bool versioned = " << versioned << ";"
+ << endl;
+
+ //
+ // Functions.
+ //
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ")" << (columns != 0 ? ";\n" : "{}");
+ }
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ")" << (columns != 0 ? ";\n" : "{}");
+
+ // init (view, image)
+ //
+ os << "static void" << endl
+ << "init (view_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ")" << (columns != 0 ? ";\n" : "{}");
+
+ // column_count
+ //
+ os << "static const std::size_t column_count = " << columns << "UL;"
+ << endl;
+
+ // Statements.
+ //
+ view_query& vq (c.get<view_query> ("query"));
+
+ if (vq.kind != view_query::runtime)
+ {
+ os << "static query_base_type" << endl
+ << "query_statement (const query_base_type&);"
+ << endl;
+ }
+
+ //
+ // Functions.
+ //
+
+ // query ()
+ //
+ if (!options.omit_unprepared ())
+ {
+ os << "static result<view_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static result<view_type>" << endl
+ << "query (database&, const odb::query_base&);"
+ << endl;
+ }
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, " <<
+ "const odb::query_base&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+
+ view_public_extra_post (c);
+
+ os << "};";
+
+ // view_traits_impl< , id_common>
+ //
+ if (options.default_database_specified () &&
+ options.default_database () == db)
+ {
+ os << "template <>" << endl
+ << "class access::view_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::view_traits_impl< " << type << ", " <<
+ "id_" << db << " >"
+ << "{"
+ << "};";
+ }
+}
+
+void relational::header::class1::
+traverse_composite (type& c)
+{
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // While composite_value_traits is not used directly by user code, we
+ // still need to export it if the generated code for the same database
+ // is split into several DLLs.
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::composite_value_traits< " << type <<
+ ", id_" << db << " >"
+ << "{"
+ << "public:" << endl;
+
+ // value_type
+ //
+ os << "typedef " << type << " value_type;"
+ << endl;
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ // Containers.
+ //
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "image_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // init (image, value)
+ //
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&," << endl
+ << "const value_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // init (value, image)
+ //
+ os << "static void" << endl
+ << "init (value_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ if (!has_a (c, test_container))
+ {
+ // get_null (image)
+ //
+ os << "static bool" << endl
+ << "get_null (const image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // set_null (image)
+ //
+ os << "static void" << endl
+ << "set_null (image_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ column_count_type const& cc (column_count (c));
+ os << "static const std::size_t column_count = " << cc.total << "UL;";
+
+ os << "};";
+}
+
+void relational::header::
+generate ()
+{
+ context ctx;
+ ostream& os (ctx.os);
+
+ instance<include> i;
+ i->generate ();
+
+ os << "namespace odb"
+ << "{";
+
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class1> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class2> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ os << "}";
+}
diff --git a/odb/odb/relational/header.hxx b/odb/odb/relational/header.hxx
new file mode 100644
index 0000000..964aff2
--- /dev/null
+++ b/odb/odb/relational/header.hxx
@@ -0,0 +1,1466 @@
+// file : odb/relational/header.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_HEADER_HXX
+#define ODB_RELATIONAL_HEADER_HXX
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace header
+ {
+ //
+ // image_type
+ //
+
+ struct image_member: virtual member_base
+ {
+ typedef image_member base;
+
+ image_member (string const& var = string ())
+ : member_base (var, 0, 0, string (), string ()) {}
+
+ image_member (string const& var,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix) {}
+ };
+
+ template <typename T>
+ struct image_member_impl: image_member, virtual member_base_impl<T>
+ {
+ typedef image_member_impl base_impl;
+
+ image_member_impl (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_image_type_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ image_type = member_image_type_->image_type (mi.m);
+
+ if (var_override_.empty ())
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ return true;
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ // Use a helper to create a complete chain of images all
+ // the way to the root (see libodb/odb/view-image.hxx).
+ //
+ os << "view_object_image<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " " << class_fq_name (*poly_root) << "," << endl
+ << " id_" << db << " >";
+ else
+ os << "object_traits_impl< " << class_fq_name (c) << ", " <<
+ "id_" << db << " >::image_type";
+
+ os << " " << mi.var << "value;"
+ << endl;
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << endl;
+ }
+
+ protected:
+ string image_type;
+ instance<member_image_type> member_image_type_;
+ };
+
+ struct image_base: traversal::class_, virtual context
+ {
+ typedef image_base base;
+
+ image_base (): first_ (true) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ if (first_)
+ {
+ os << ": ";
+ first_ = false;
+ }
+ else
+ {
+ os << "," << endl
+ << " ";
+ }
+
+ string const& type (class_fq_name (c));
+
+ if (obj)
+ os << "object_traits_impl< " << type << ", id_" << db <<
+ " >::image_type";
+ else
+ os << "composite_value_traits< " << type << ", id_" << db <<
+ " >::image_type";
+ }
+
+ private:
+ bool first_;
+ };
+
+ struct image_type: traversal::class_, virtual context
+ {
+ typedef image_type base;
+
+ image_type ()
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ image_type (image_type const&)
+ : root_context (), context () //@@ -Wextra
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ virtual void
+ image_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ os << "struct image_type";
+
+ if (!view (c))
+ {
+ // Don't go into the base if we are a derived type in a
+ // polymorphic hierarchy.
+ //
+ if (!poly_derived)
+ {
+ instance<image_base> b;
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+ }
+
+ os << "{";
+
+ if (poly_derived)
+ os << "base_traits::image_type* base;"
+ << endl;
+
+ names (c);
+
+ // We don't need a version if this is a composite value type
+ // or reuse-abstract object.
+ //
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
+ os << "std::size_t version;"
+ << endl;
+
+ image_extra (c);
+
+ os << "};";
+ }
+
+ private:
+ instance<image_member> member_;
+ traversal::names names_member_;
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& c)
+ : object_members_base (true, false, false), c_ (c)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (object (c_))
+ object_members_base::traverse_composite (m, c);
+ else
+ {
+ // If we are generating traits for a composite value type, then
+ // we don't want to go into its bases or it composite members.
+ //
+ if (m == 0 && &c == &c_)
+ names (c);
+ }
+ }
+
+ virtual void
+ container_public_extra_pre (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ container_public_extra_post (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ using semantics::type;
+ using semantics::class_;
+
+ // Figure out if this member is from a base object or composite
+ // value and if it's from an object, whether it is reuse-abstract.
+ //
+ bool base, reuse_abst;
+
+ if (object (c_))
+ {
+ base = cur_object != &c_ ||
+ !object (dynamic_cast<type&> (m.scope ()));
+ reuse_abst = abstract (c_) && !polymorphic (c_);
+ }
+ else
+ {
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
+ }
+
+ container_kind_type ck (container_kind (c));
+
+ const custom_cxx_type* vct (0);
+ const custom_cxx_type* ict (0);
+ const custom_cxx_type* kct (0);
+
+ type& vt (container_vt (m, &vct));
+ type* it (0);
+ type* kt (0);
+
+ bool ordered (false);
+ bool inverse (context::inverse (m, "value"));
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (m, &ict);
+ ordered = true;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (m, &kct);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ bool smart (!inverse &&
+ (ck != ck_ordered || ordered) &&
+ container_smart (c));
+
+ string name (flat_prefix_ + public_name (m) + "_traits");
+
+ // Figure out column counts.
+ //
+ size_t id_columns, value_columns, data_columns, cond_columns;
+ bool versioned (context::versioned (m));
+
+ if (!reuse_abst)
+ {
+ type& idt (container_idt (m));
+
+ if (class_* idc = composite_wrapper (idt))
+ id_columns = column_count (*idc).total;
+ else
+ id_columns = 1;
+
+ data_columns = cond_columns = id_columns;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ // Add one for the index.
+ //
+ if (ordered)
+ {
+ data_columns++;
+
+ if (smart)
+ cond_columns++;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // Add some for the key.
+ //
+ size_t n;
+
+ class_* ptr (object_pointer (*kt));
+ semantics::type& t (ptr == 0 ? *kt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ n = column_count (*comp).total;
+ else
+ n = 1;
+
+ data_columns += n;
+
+ // Key is not currently used (see also bind()).
+ //
+ // cond_columns += n;
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // Not currently used (see also bind())
+ //
+ // Value is also a key.
+ //
+ // class_* ptr (object_pointer (vt));
+ // semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+ //
+ // if (class_* comp = composite_wrapper (t))
+ // cond_columns += column_count (*comp).total;
+ // else
+ // cond_columns++;
+ //
+ break;
+ }
+ }
+
+ {
+ class_* ptr (object_pointer (vt));
+ semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ value_columns = column_count (*comp).total;
+ else
+ value_columns = 1;
+
+ data_columns += value_columns;
+ }
+
+ // Store column counts for the source generator.
+ //
+ m.set ("id-column-count", id_columns);
+ m.set ("value-column-count", value_columns);
+ m.set ("cond-column-count", cond_columns);
+ m.set ("data-column-count", data_columns);
+ }
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << "struct " << exp << name;
+
+ if (base)
+ {
+ semantics::class_& b (dynamic_cast<semantics::class_&> (m.scope ()));
+ string const& type (class_fq_name (b));
+
+ if (object (b))
+ os << ": access::object_traits_impl< " << type << ", id_" <<
+ db << " >::" << name;
+ else
+ os << ": access::composite_value_traits< " << type << ", id_" <<
+ db << " >::" << public_name (m) << "_traits"; // No prefix_.
+ }
+
+ os << "{";
+
+ container_public_extra_pre (m, c);
+
+ if (!reuse_abst)
+ {
+ // column_count
+ //
+ os << "static const std::size_t id_column_count = " <<
+ id_columns << "UL;";
+
+ if (smart)
+ os << "static const std::size_t value_column_count = " <<
+ value_columns << "UL;"
+ << "static const std::size_t cond_column_count = " <<
+ cond_columns << "UL;";
+
+ os << "static const std::size_t data_column_count = " <<
+ data_columns << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ os << "static const char insert_statement[];"
+ << "static const char select_statement[];";
+
+ if (smart)
+ os << "static const char update_statement[];";
+
+ os << "static const char delete_statement[];"
+ << endl;
+ }
+
+ if (base)
+ {
+ container_public_extra_post (m, c);
+ os << "};";
+
+ return;
+ }
+
+ // container_type
+ // container_traits
+ // index_type
+ // key_type
+ // value_type
+ //
+ os << "typedef ";
+
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (semantics::type* wt = wrapper (t))
+ {
+ // Use the hint from the wrapper unless the wrapped type is
+ // qualified. In this case use the hint for the unqualified
+ // type.
+ //
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ utype (*wt, hint);
+
+ os << c.fq_name (hint);
+ }
+ else
+ // t and c are the same.
+ //
+ os << t.fq_name (hint);
+ }
+
+ os << " container_type;";
+
+ os << "typedef" << endl
+ << "odb::access::container_traits<container_type>" << endl
+ << "container_traits_type;";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef container_traits_type::index_type index_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef container_traits_type::key_type key_type;";
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "typedef container_traits_type::value_type value_type;"
+ << endl;
+
+ // functions_type
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef " << (smart ? "smart_" : "") <<
+ "ordered_functions<index_type, value_type> functions_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef map_functions<key_type, value_type> " <<
+ "functions_type;";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "typedef set_functions<value_type> functions_type;";
+ break;
+ }
+ }
+
+ os << "typedef " << db << "::" << (smart ? "smart_" : "")
+ << "container_statements< " << name << " > statements_type;"
+ << endl;
+
+ // cond_image_type (object id is taken from the object image).
+ //
+ // For dumb containers we use the id binding directly.
+ //
+ if (smart)
+ {
+ os << "struct cond_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "index_", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, kct, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "value_", vt, vct, "value_type", "value");
+ im->traverse (m);
+ break;
+ }
+ }
+
+ os << "std::size_t version;"
+ << "};";
+ }
+
+ // data_image_type (object id is taken from the object image)
+ //
+ os << "struct data_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "index_", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, kct, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im ("value_", vt, vct, "value_type", "value");
+ im->traverse (m);
+
+ os << "std::size_t version;"
+ << "};";
+
+ // bind (cond_image)
+ //
+ if (smart)
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type&);"
+ << endl;
+
+ // bind (data_image)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "data_image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // bind (cond_image, data_image) (update)
+ //
+ if (smart)
+ {
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type&," << endl
+ << "data_image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "static void" << endl
+ << "grow (data_image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "init (data_image_type&," << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type*," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "const key_type*," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "const value_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (cond_image)
+ //
+ if (smart)
+ {
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (cond_image_type&, index_type);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // os << "init (data_image_type&, const key_type&);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // init (data)
+ //
+ os << "static void" << endl
+ << "init (";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type&," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "key_type&," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "value_type&," << endl;
+ os << "const data_image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // insert
+ //
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert (index_type, const value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert (const key_type&, const value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert (const value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // select
+ //
+ os << "static bool" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "select (index_type&, value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "select (key_type&, value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "select (value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // update
+ //
+ if (smart)
+ {
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "update (index_type, const value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //os << "update (const key_type&, const value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //os << "update (const value_type&, const value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // delete_
+ //
+ os << "static void" << endl
+ << "delete_ (";
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "index_type, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ os << "void*);"
+ << endl;
+
+ // persist
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "persist (const container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // load
+ //
+ os << "static void" << endl
+ << "load (container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // update
+ //
+ if (!(inverse || readonly (member_path_, member_scope_)))
+ {
+ os << "static void" << endl
+ << "update (const container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // erase
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "erase (";
+
+ if (smart)
+ os << "const container_type*, ";
+
+ os << "statements_type&);"
+ << endl;
+ }
+
+ container_public_extra_post (m, c);
+
+ os << "};";
+ }
+
+ protected:
+ semantics::class_& c_;
+ };
+
+ //
+ //
+ struct section_traits: virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c): c_ (c) {}
+
+ virtual void
+ section_public_extra_pre (user_section&)
+ {
+ }
+
+ virtual void
+ section_public_extra_post (user_section&)
+ {
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+
+ semantics::data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // hierarchy, then pretend it inherits from the special version update
+ // section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (*s.member) + "_traits");
+
+ os << "// " << s.member->name () << endl
+ << "//" << endl
+ << "struct " << exp << name
+ << "{";
+
+ os << "typedef object_traits_impl<object_type, id_" << db <<
+ ">::image_type image_type;"
+ << "typedef object_traits_impl<object_type, id_" << db <<
+ ">::id_image_type id_image_type;"
+ << endl;
+
+ section_public_extra_pre (s);
+
+ // bind (id, image_type)
+ //
+ // If id is NULL, then id is ignored (select). Otherwise, it is
+ // copied at the end (update).
+ //
+ if (load || load_opt || update || update_opt)
+ {
+ os << "static std::size_t" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "image_type&," << endl
+ << db << "::statement_kind";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // grow ()
+ //
+ // We have to have out own version because the truncated vector
+ // will have different number of elements.
+ //
+ if (generate_grow && (load || load_opt))
+ {
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (object, image)
+ //
+ if (load)
+ {
+ os << "static void" << endl
+ << "init (object_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (image, object)
+ //
+ if (update)
+ {
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&," << endl
+ << "const object_type&";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_public_extra_post (s);
+ os << "};";
+ return;
+ }
+
+ // column_count
+ //
+ column_count_type const& cc (column_count (poly ? *poly_root : c_));
+
+ // Generate load and update column counts even when they are zero so
+ // that we can instantiate section_statements.
+ //
+ os << "static const std::size_t id_column_count = " << cc.id << "UL;";
+
+ os << "static const std::size_t managed_optimistic_load_column_count" <<
+ " = " << cc.optimistic_managed << "UL;"
+ << "static const std::size_t load_column_count = " <<
+ (load ? s.total_total () : 0) << "UL;";
+
+ os << "static const std::size_t managed_optimistic_update_column_count" <<
+ " = " << (poly_derived ? 0 : cc.optimistic_managed) << "UL;"
+ << "static const std::size_t update_column_count = " <<
+ (update ? s.total - s.inverse - s.readonly : 0) << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << s.versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ if (load || load_opt)
+ os << "static const char select_statement[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_statement[];"
+ << endl;
+
+ // Section statements.
+ //
+ if (load || load_opt || update || update_opt)
+ os << "typedef " << db << "::section_statements< object_type, " <<
+ name << " > statements_type;"
+ << endl;
+
+ // We pass statement cache instead of just statements because
+ // we may also need statements for containers.
+ //
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ os << "static void" << endl
+ << "load (extra_statement_cache_type&, object_type&" <<
+ (poly ? ", bool top = true" : "") << ");"
+ << endl;
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ os << "static void" << endl
+ << "update (extra_statement_cache_type&, const object_type&" <<
+ (poly_derived && s.base != 0 ? ", bool base = true" : "") << ");"
+ << endl;
+
+ section_public_extra_post (s);
+
+ os << "};";
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ protected:
+ semantics::class_& c_;
+ };
+
+ // First pass over objects, views, and composites. Some code must be
+ // split into two parts to deal with yet undefined types.
+ //
+ struct class1: traversal::class_, virtual context
+ {
+ typedef class1 base;
+
+ class1 ()
+ : typedefs_ (false),
+ id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ class1 (class1 const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+ }
+
+ virtual void
+ object_public_extra_pre (type&)
+ {
+ }
+
+ virtual void
+ object_public_extra_post (type&)
+ {
+ }
+
+ virtual void
+ traverse_object (type&);
+
+ virtual void
+ view_public_extra_pre (type&)
+ {
+ }
+
+ virtual void
+ view_public_extra_post (type&)
+ {
+ }
+
+ virtual void
+ traverse_view (type&);
+
+ virtual void
+ traverse_composite (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<image_type> image_type_;
+ instance<image_member> id_image_member_;
+ instance<image_member> version_image_member_;
+ instance<image_member> discriminator_image_member_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> pointer_query_columns_type_;
+ };
+
+ // Second pass over objects, views, and composites.
+ //
+ struct class2: traversal::class_, virtual context
+ {
+ typedef class2 base;
+
+ class2 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ class2 (class2 const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ if (options.generate_query ())
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // query_columns
+ //
+ // If we don't have any pointers, then query_columns is generated
+ // in pass 1 (see the comment in class1 for details).
+ //
+ if (has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ // Generate extern template declarations.
+ //
+ if (multi_dynamic)
+ query_columns_type_inst_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ view_query_columns_type_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+ }
+
+ virtual void
+ traverse_composite (type&)
+ {
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> query_columns_type_inst_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ os << "#include <odb/details/buffer.hxx>" << endl
+ << endl;
+
+ os << "#include <odb/" << db << "/version.hxx>" << endl
+ << "#include <odb/" << db << "/forward.hxx>" << endl
+ << "#include <odb/" << db << "/binding.hxx>" << endl
+ << "#include <odb/" << db << "/" << db << "-types.hxx>" << endl;
+
+ if (options.generate_query ())
+ {
+ os << "#include <odb/" << db << "/query.hxx>" << endl;
+
+ if (multi_dynamic)
+ os << "#include <odb/" << db << "/query-dynamic.hxx>" << endl;
+ }
+
+ os << endl;
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_HEADER_HXX
diff --git a/odb/odb/relational/inline.cxx b/odb/odb/relational/inline.cxx
new file mode 100644
index 0000000..5e60705
--- /dev/null
+++ b/odb/odb/relational/inline.cxx
@@ -0,0 +1,47 @@
+// file : odb/relational/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace inline_
+ {
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ instance<include> i;
+ i->generate ();
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/odb/relational/inline.hxx b/odb/odb/relational/inline.hxx
new file mode 100644
index 0000000..a609cc1
--- /dev/null
+++ b/odb/odb/relational/inline.hxx
@@ -0,0 +1,693 @@
+// file : odb/relational/inline.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_INLINE_HXX
+#define ODB_RELATIONAL_INLINE_HXX
+
+#include <odb/diagnostics.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace inline_
+ {
+ //
+ // get/set null (composite value only)
+ //
+
+ struct null_member: virtual member_base
+ {
+ typedef null_member base;
+
+ null_member (bool get)
+ : member_base (0, 0, string (), string ()), get_ (get) {}
+
+ protected:
+ bool get_;
+ };
+
+ template <typename T>
+ struct null_member_impl: null_member, virtual member_base_impl<T>
+ {
+ typedef null_member_impl base_impl;
+
+ null_member_impl (base const& x): base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // If the whole value type is readonly, then set will never be
+ // called with sk == statement_update.
+ //
+ if (!get_ && !readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c)))
+ os << "if (sk == statement_insert)" << endl;
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (added (mi.m) || deleted (mi.m))
+ os << "}";
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ string traits ("composite_value_traits< " + mi.fq_type () + ", id_" +
+ db.string () + " >");
+
+ if (get_)
+ os << "r = r && " << traits << "::get_null (" <<
+ "i." << mi.var << "value";
+ else
+ os << traits << "::set_null (i." << mi.var << "value, sk";
+
+ if (versioned (*composite (mi.t)))
+ os << ", svm";
+
+ os << ");";
+ }
+ };
+
+ struct null_base: traversal::class_, virtual context
+ {
+ typedef null_base base;
+
+ null_base (bool get): get_ (get) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!composite (c))
+ return;
+
+ string traits ("composite_value_traits< " + class_fq_name (c) +
+ ", id_" + db.string () + " >");
+
+ // If the derived value type is readonly, then set will never be
+ // called with sk == statement_update.
+ //
+ if (!get_ && readonly (c) && !readonly (*context::top_object))
+ os << "if (sk == statement_insert)" << endl;
+
+ if (get_)
+ os << "r = r && " << traits << "::get_null (i";
+ else
+ os << traits << "::set_null (i, sk";
+
+ if (versioned (c))
+ os << ", svm";
+
+ os << ");";
+ }
+
+ protected:
+ bool get_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ ()
+ : typedefs_ (false),
+ get_null_base_ (true),
+ get_null_member_ (true),
+ set_null_base_ (false),
+ set_null_member_ (false)
+ {
+ init ();
+ }
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ get_null_base_ (true),
+ get_null_member_ (true),
+ set_null_base_ (false),
+ set_null_member_ (false)
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ get_null_base_inherits_ >> get_null_base_;
+ get_null_member_names_ >> get_null_member_;
+
+ set_null_base_inherits_ >> set_null_base_;
+ set_null_member_names_ >> set_null_member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ context::top_object = context::cur_object = &c;
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+
+ context::top_object = context::cur_object = 0;
+ }
+
+ virtual void
+ object_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+ data_member* optimistic (context::optimistic (c));
+
+ // Base class that contains the object id and version for optimistic
+ // concurrency.
+ //
+ type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
+
+ type* poly_root (context::polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ object_extra (c);
+
+ if (id != 0 && base_id)
+ {
+ if (!poly_derived)
+ {
+ // id (id_image_type)
+ //
+ if (auto_id)
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const id_image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::id (i);"
+ << "}";
+ }
+
+ // id (image_type)
+ //
+ if (options.generate_query ())
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::id (i);"
+ << "}";
+ }
+
+ // version (image_type)
+ //
+ if (optimistic != 0)
+ {
+ os << "inline" << endl
+ << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::version (i);"
+ << "}";
+ }
+ }
+
+ // bind (id_image_type)
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (optimistic != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
+ db << " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");"
+ << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (optimistic != 0 ? ", const version_type* v" : "") << ")"
+ << "{"
+ << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
+ db << " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
+ << "}";
+ }
+
+ if (poly_derived)
+ {
+ size_t depth (polymorphic_depth (c));
+
+ // check_version
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "check_version (const std::size_t* v, const image_type& i)"
+ << "{"
+ << "return ";
+
+ string image ("i.");
+ for (size_t i (0); i < depth; ++i)
+ {
+ os << (i == 0 ? "" : " ||") << endl
+ << " v[" << i << "UL] != " << image << "version";
+
+ image += "base->";
+ }
+
+ os << ";"
+ << "}";
+
+ // update_version
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "update_version (std::size_t* v, const image_type& i, " <<
+ db << "::binding* b)"
+ << "{";
+
+ image = "i.";
+ for (size_t i (0); i < depth; ++i)
+ {
+ os << "v[" << i << "UL] = " << image << "version;";
+ image += "base->";
+ }
+
+ // A poly-abstract class always has only one entry in the
+ // bindings array.
+ //
+ if (abst)
+ os << "b[0].version++;";
+ else
+ for (size_t i (0); i < depth; ++i)
+ os << "b[" << i << "UL].version++;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ // erase (object_type)
+ //
+ if (id != 0 && !poly && optimistic == 0 &&
+ !has_a (c, test_smart_container))
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj)"
+ << "{"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "erase (db, id (obj));";
+
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
+ os << "callback (db, obj, callback_event::post_erase);"
+ << "}";
+ }
+
+ // load (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s, " <<
+ "const info_type* pi)"
+ << "{"
+ << "return base_traits::load (conn, obj, s, pi);"
+ << "}";
+ }
+
+ // update (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s, const info_type* pi)"
+ << "{"
+ << "return base_traits::update (conn, obj, s, pi);"
+ << "}";
+ }
+
+ // load_()
+ //
+ if (id != 0 &&
+ !(poly_derived ||
+ has_a (c, test_container | include_eager_load, &main_section) ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0))
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "load_ (statements_type& sts," << endl
+ << "object_type& obj," << endl
+ << "bool";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sts);"
+ << "ODB_POTENTIALLY_UNUSED (obj);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ // Mark eager sections as loaded.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ // We can only end up here if the object itself is versioned
+ // (simple value section).
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ os << "}";
+ }
+
+ if (poly && need_image_clone && options.generate_query ())
+ {
+ // root_image ()
+ //
+ os << "inline" << endl
+ << traits << "::root_traits::image_type&" << endl
+ << traits << "::" << endl
+ << "root_image (image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "return base_traits::root_image (*i.base);";
+ else
+ os << "return i;";
+
+ os << "}";
+
+ // clone_image ()
+ //
+ os << "inline" << endl
+ << traits << "::image_type*" << endl
+ << traits << "::" << endl
+ << "clone_image (image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "details::unique_ptr<base_traits::image_type> p (" << endl
+ << "base_traits::clone_image (*i.base));"
+ << "image_type* c (new image_type (i));"
+ << "c->base = p.release ();"
+ << "return c;";
+ else
+ os << "return new image_type (i);";
+
+ os << "}";
+
+ // copy_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "copy_image (image_type& d, image_type& s)"
+ << "{";
+
+ if (poly_derived)
+ os << "base_traits::image_type* b (d.base);"
+ << "base_traits::copy_image (*b, *s.base);"
+ << "d = s;" // Overwrites the base pointer.
+ << "d.base = b;";
+ else
+ os << "d = s;";
+
+ os << "}";
+
+ // free_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "free_image (image_type* i)"
+ << "{";
+
+ if (poly_derived)
+ os << "base_traits::free_image (i->base);";
+
+ os << "delete i;"
+ << "}";
+ }
+ }
+
+ virtual void
+ view_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ view_extra (c);
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::composite_value_traits< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ if (!has_a (c, test_container))
+ {
+ // get_null (image)
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "get_null (const image_type& i";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "bool r (true);";
+
+ inherits (c, get_null_base_inherits_);
+ names (c, get_null_member_names_);
+
+ os << "return r;"
+ << "}";
+
+ // set_null (image)
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "set_null (image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ inherits (c, set_null_base_inherits_);
+ names (c, set_null_member_names_);
+
+ os << "}";
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<null_base> get_null_base_;
+ traversal::inherits get_null_base_inherits_;
+ instance<null_member> get_null_member_;
+ traversal::names get_null_member_names_;
+
+ instance<null_base> set_null_base_;
+ traversal::inherits set_null_base_inherits_;
+ instance<null_member> set_null_member_;
+ traversal::names set_null_member_names_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ if (versioned ())
+ os << "#include <odb/schema-version.hxx>" << endl
+ << endl;
+
+ if (features.polymorphic_object && options.generate_query ())
+ os << "#include <odb/details/unique-ptr.hxx>" << endl
+ << endl;
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_INLINE_HXX
diff --git a/odb/odb/relational/model.cxx b/odb/odb/relational/model.cxx
new file mode 100644
index 0000000..45d555a
--- /dev/null
+++ b/odb/odb/relational/model.cxx
@@ -0,0 +1,121 @@
+// file : odb/relational/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/model.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace model
+ {
+ // object_columns
+ //
+ string object_columns::
+ default_ (semantics::data_member& m)
+ {
+ default_value* dv (0);
+
+ semantics::type& t (utype (m));
+
+ if (m.count ("default"))
+ dv = &m.get<default_value> ("default");
+ else if (t.count ("default"))
+ dv = &t.get<default_value> ("default");
+ else
+ return ""; // No default value for this column.
+
+ switch (dv->kind)
+ {
+ case default_value::reset:
+ {
+ return ""; // No default value.
+ }
+ case default_value::null:
+ {
+ return default_null (m);
+ }
+ case default_value::boolean:
+ {
+ return default_bool (m, dv->literal == "true");
+ }
+ case default_value::integer:
+ {
+ return default_integer (m, dv->int_value, dv->literal == "-");
+ }
+ case default_value::floating:
+ {
+ return default_float (m, dv->float_value);
+ }
+ case default_value::string:
+ {
+ return default_string (m, dv->literal);
+ }
+ case default_value::enumerator:
+ {
+ return default_enum (m, dv->enum_value, dv->literal);
+ }
+ }
+
+ return "";
+ }
+
+ cutl::shared_ptr<sema_rel::model>
+ generate ()
+ {
+ context ctx;
+ cutl::shared_ptr<sema_rel::model> m (
+ new (shared) sema_rel::model (
+ ctx.versioned () ? ctx.version ().current : 0));
+ m->set ("deleted-map", deleted_table_map ());
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class_> c (*m);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ try
+ {
+ unit.dispatch (ctx.unit);
+ }
+ catch (sema_rel::duplicate_name const& e)
+ {
+ location const& o (e.orig.get<location> ("cxx-location"));
+ location const& d (e.dup.get<location> ("cxx-location"));
+
+ error (d) << e.dup.kind () << " name '" << e.name << "' conflicts "
+ << "with an already defined " << e.orig.kind () << " name"
+ << endl;
+
+ info (o) << "conflicting " << e.orig.kind () << " is defined here"
+ << endl;
+
+ if (e.dup.kind () == "index")
+ info (d) << "use #pragma db index to change its name" << endl;
+ else if (e.dup.kind () == "table")
+ info (d) << "use #pragma db table to change its name" << endl;
+ else
+ info (d) << "use #pragma db column to change its name" << endl;
+
+ throw operation_failed ();
+ }
+
+ return m;
+ }
+ }
+}
diff --git a/odb/odb/relational/model.hxx b/odb/odb/relational/model.hxx
new file mode 100644
index 0000000..fdfa8fd
--- /dev/null
+++ b/odb/odb/relational/model.hxx
@@ -0,0 +1,868 @@
+// file : odb/relational/model.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MODEL_HXX
+#define ODB_RELATIONAL_MODEL_HXX
+
+#include <map>
+#include <set>
+#include <cassert>
+#include <sstream>
+
+#include <odb/semantics/relational.hxx>
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace model
+ {
+ typedef std::set<qname> tables;
+ typedef std::map<qname, semantics::node*> deleted_table_map;
+ typedef std::map<uname, semantics::data_member*> deleted_column_map;
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ object_columns (sema_rel::model& model,
+ sema_rel::table& table,
+ bool object)
+ : model_ (model),
+ table_ (table),
+ object_ (object),
+ pkey_ (0),
+ id_override_ (false)
+ {
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if (context::top_object != &c)
+ {
+ // We are in one of the bases. Set the id_prefix to its
+ // (unqualified) name.
+ //
+ string t (id_prefix_);
+ id_prefix_ = class_name (c) + "::";
+ object_columns_base::traverse_object (c);
+ id_prefix_ = t;
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ string t (id_prefix_);
+
+ if (m != 0)
+ // Member of a composite type. Add the data member to id_prefix.
+ //
+ if (!id_override_)
+ id_prefix_ += m->name () + ".";
+ else
+ id_override_ = false;
+ else
+ // Composite base. Add its unqualified name to id_prefix.
+ //
+ id_prefix_ += class_name (c) + "::";
+
+ object_columns_base::traverse_composite (m, c);
+
+ id_prefix_ = t;
+ }
+
+ virtual void
+ traverse (semantics::data_member& m,
+ semantics::type& t,
+ string const& kp,
+ string const& dn,
+ semantics::class_* to = 0)
+ {
+ // This overrides the member name for a composite container value
+ // or key.
+ //
+ if (!kp.empty ())
+ {
+ semantics::class_* c (object_pointer (t));
+ if (composite_wrapper (c == 0 ? t : utype (*id_member (*c))))
+ {
+ id_prefix_ = kp + ".";
+ id_override_ = true;
+ }
+ }
+
+ object_columns_base::traverse (m, t, kp, dn, to);
+ }
+
+ using object_columns_base::traverse;
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ if (semantics::data_member* m = deleted_member (member_path_))
+ {
+ table_.get<deleted_column_map> ("deleted-map")[name] = m;
+ return false;
+ }
+
+ string col_id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
+
+ sema_rel::column& c (
+ model_.new_node<sema_rel::column> (col_id, type (m), null (m)));
+ c.set ("cxx-location", m.location ());
+ c.set ("member-path", member_path_);
+ model_.new_edge<sema_rel::unames> (table_, c, name);
+
+ // An id member cannot have a default value.
+ //
+ if (!object_columns_base::id ())
+ {
+ string const& d (default_ (m));
+
+ if (!d.empty ())
+ c.default_ (d);
+ }
+
+ // If we have options, add them.
+ //
+ string const& o (column_options (m, key_prefix_));
+
+ if (!o.empty ())
+ c.options (o);
+
+ constraints (m, name, col_id, c);
+ return true;
+ }
+
+ virtual string
+ type (semantics::data_member&)
+ {
+ return object_columns_base::column_type ();
+ }
+
+ virtual bool
+ null (semantics::data_member&)
+ {
+ return !object_columns_base::id () && object_columns_base::null ();
+ }
+
+ virtual string
+ default_null (semantics::data_member&)
+ {
+ return "NULL";
+ }
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ // Most databases do not support boolean literals. Those that
+ // do should override this.
+ //
+ return (v ? "1" : "0");
+ }
+
+ virtual string
+ default_integer (semantics::data_member&, unsigned long long v, bool neg)
+ {
+ std::ostringstream ostr;
+ ostr << (neg ? "-" : "") << v;
+ return ostr.str ();
+ }
+
+ virtual string
+ default_float (semantics::data_member&, double v)
+ {
+ std::ostringstream ostr;
+ ostr << v;
+ return ostr.str ();
+ }
+
+ virtual string
+ default_string (semantics::data_member&, string const& v)
+ {
+ return quote_string (v);
+ }
+
+ virtual string
+ default_enum (semantics::data_member&,
+ tree /*enumerator*/,
+ string const& /*name*/)
+ {
+ // Has to be implemented by the database-specific override.
+ //
+ assert (false);
+ return string ();
+ }
+
+ virtual void
+ primary_key (sema_rel::primary_key&)
+ {
+ }
+
+ virtual void
+ constraints (semantics::data_member& m,
+ string const& /* name */,
+ string const& /* id */,
+ sema_rel::column& c)
+ {
+ if (object_)
+ {
+ if (semantics::data_member* idm = id ())
+ {
+ if (pkey_ == 0)
+ {
+ pkey_ = &model_.new_node<sema_rel::primary_key> (
+ m.count ("auto"));
+ pkey_->set ("cxx-location", idm->location ());
+
+ // In most databases the primary key constraint can be
+ // manipulated without an explicit name. So we use the special
+ // empty name for primary keys in order not to clash with
+ // columns and other constraints. If the target database does
+ // not support unnamed primary key manipulation, then the
+ // database-specific code will have to come up with a suitable
+ // name.
+ //
+ model_.new_edge<sema_rel::unames> (table_, *pkey_, "");
+ primary_key (*pkey_);
+ }
+
+ model_.new_edge<sema_rel::contains> (*pkey_, c);
+ }
+ }
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore inverse object pointers.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ if (deleted (member_path_))
+ {
+ // Still traverse it as columns so that we can populate the
+ // deleted map.
+ //
+ object_columns_base::traverse_pointer (m, c);
+ return;
+ }
+
+ // Get the position of the last column.
+ //
+ sema_rel::table::names_iterator i (table_.names_end ());
+
+ while (i != table_.names_begin ())
+ {
+ --i;
+ if (i->nameable ().is_a<sema_rel::column> ())
+ break;
+ }
+
+ // Traverse the object pointer as columns.
+ //
+ object_columns_base::traverse_pointer (m, c);
+
+ // Get to the first column that we have added.
+ //
+ if (i != table_.names_end ())
+ ++i; // Next column.
+ else
+ i = table_.names_begin ();
+
+ foreign_key (m, c, i);
+ }
+
+ virtual void
+ traverse_points_to (semantics::data_member& m, semantics::class_& c)
+ {
+ if (deleted (member_path_))
+ {
+ // Still traverse it as columns so that we can populate the
+ // deleted map.
+ //
+ object_columns_base::traverse_points_to (m, c);
+ return;
+ }
+
+ // Get the position of the last column.
+ //
+ sema_rel::table::names_iterator i (table_.names_end ());
+
+ while (i != table_.names_begin ())
+ {
+ --i;
+ if (i->nameable ().is_a<sema_rel::column> ())
+ break;
+ }
+
+ // Traverse the data member as columns.
+ //
+ object_columns_base::traverse_points_to (m, c);
+
+ // Get to the first column that we have added.
+ //
+ if (i != table_.names_end ())
+ ++i; // Next column.
+ else
+ i = table_.names_begin ();
+
+ foreign_key (m, c, i);
+ }
+
+ virtual void
+ foreign_key (semantics::data_member& m,
+ semantics::class_& c,
+ sema_rel::table::names_iterator i)
+ {
+ using sema_rel::column;
+ using sema_rel::foreign_key;
+
+ string id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
+
+ deferrable def (
+ m.get<deferrable> ("deferrable",
+ options.fkeys_deferrable_mode ()[db]));
+
+ foreign_key::action_type on_delete (
+ m.get<foreign_key::action_type> (
+ "on-delete", foreign_key::no_action));
+
+ foreign_key& fk (
+ model_.new_node<foreign_key> (id, table_name (c), def, on_delete));
+
+ fk.set ("cxx-location", m.location ());
+
+ bool simple;
+
+ // Get referenced columns.
+ //
+ {
+ data_member_path& id (*id_member (c));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (id);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+
+ simple = (fk.referenced_columns ().size () == 1);
+ }
+
+ // Get referencing columns.
+ //
+ for (; i != table_.names_end (); ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
+ else
+ break;
+ }
+
+ // Derive the constraint name. Generally, we want it to be based
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
+ //
+ string name;
+
+ if (simple)
+ name = fk.contains_begin ()->column ().name ();
+ else
+ {
+ string p (column_prefix (m, key_prefix_, default_name_).prefix);
+
+ if (p.empty ())
+ p = public_name_db (m);
+ else if (p[p.size () - 1] == '_')
+ p.resize (p.size () - 1); // Remove trailing underscore.
+
+ name = column_prefix_.prefix + p;
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ table_, fk, fkey_name (table_.name (), name));
+ }
+
+ protected:
+ string
+ default_ (semantics::data_member&);
+
+ protected:
+ sema_rel::model& model_;
+ sema_rel::table& table_;
+ bool object_;
+ sema_rel::primary_key* pkey_;
+ string id_prefix_;
+ bool id_override_;
+ };
+
+ struct object_indexes: traversal::class_, virtual context
+ {
+ typedef object_indexes base;
+
+ object_indexes (sema_rel::model& model, sema_rel::table& table)
+ : model_ (model), table_ (table)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ object_indexes (object_indexes const& x)
+ : root_context (), context (), //@@ -Wextra
+ model_ (x.model_), table_ (x.table_)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c)) // Ignore transient bases.
+ return;
+
+ // Polymorphic bases get their own tables.
+ //
+ if (!polymorphic (c))
+ inherits (c);
+
+ indexes& ins (c.get<indexes> ("index"));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ // Using index name as its id.
+ //
+ sema_rel::index& in (
+ model_.new_node<sema_rel::index> (
+ i->name, i->type, i->method, i->options));
+ in.set ("cxx-location", location (i->loc));
+ model_.new_edge<sema_rel::unames> (table_, in, i->name);
+
+ for (index::members_type::iterator j (i->members.begin ());
+ j != i->members.end (); ++j)
+ {
+ using sema_rel::column;
+
+ index::member& im (*j);
+
+ semantics::type* t (&utype (*im.path.back ()));
+
+ if (semantics::class_* ptr = object_pointer (*t))
+ t = &utype (*id_member (*ptr));
+
+ if (type* comp = composite_wrapper (*t))
+ {
+ // Composite value. Get the list of the columns. Note that
+ // the column prefix needs to contain all the components.
+ //
+ instance<object_columns_list> ocl (
+ column_prefix (im.path, true));
+ ocl->traverse (*comp);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ {
+ column* c (table_.find<column> (i->name));
+ assert (c != 0);
+ model_.new_edge<sema_rel::contains> (in, *c, im.options);
+ }
+ }
+ else
+ {
+ // Simple value. Get the column name and look it up in the
+ // table.
+ //
+ column* c (table_.find<column> (column_name (im.path)));
+ assert (c != 0);
+ model_.new_edge<sema_rel::contains> (in, *c, im.options);
+ }
+ }
+ }
+ }
+
+ private:
+ sema_rel::model& model_;
+ sema_rel::table& table_;
+
+ traversal::inherits inherits_;
+ };
+
+ struct member_create: object_members_base, virtual context
+ {
+ typedef member_create base;
+
+ member_create (sema_rel::model& model)
+ : object_members_base (false, true, false), model_ (model)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if (context::top_object != &c)
+ {
+ // We are in one of the bases. Set the id_prefix to its
+ // (unqualified) name.
+ //
+ string t (id_prefix_);
+ id_prefix_ = class_name (c) + "::";
+ object_members_base::traverse_object (c);
+ id_prefix_ = t;
+ }
+ else
+ {
+ // Top-level object. Set its id as a prefix.
+ //
+ id_prefix_ = string (class_fq_name (c), 2) + "::";
+ object_members_base::traverse_object (c);
+ }
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ string t (id_prefix_);
+
+ if (m != 0)
+ // Member of a composite type. Add the data member to id_prefix.
+ //
+ id_prefix_ += m->name () + ".";
+ else
+ // Composite base. Add its unqualified name to id_prefix.
+ //
+ id_prefix_ += class_name (c) + "::";
+
+ object_members_base::traverse_composite (m, c);
+
+ id_prefix_ = t;
+ }
+
+ virtual string
+ table_options (semantics::data_member& m, semantics::type& ct)
+ {
+ return context::table_options (m, ct);
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& ct)
+ {
+ using semantics::type;
+ using semantics::data_member;
+
+ using sema_rel::column;
+
+ // Ignore inverse containers of object pointers.
+ //
+ if (inverse (m, "value"))
+ return;
+
+ container_kind_type ck (container_kind (ct));
+ qname const& name (table_name (m, table_prefix_));
+
+ // Ignore deleted container members.
+ //
+ if (semantics::data_member* m = deleted_member (member_path_))
+ {
+ model_.get<deleted_table_map> ("deleted-map")[name] = m;
+ return;
+ }
+
+ // Add the [] decorator to distinguish this id from non-container
+ // ids (we don't want to ever end up comparing, for example, an
+ // object table to a container table).
+ //
+ string id (id_prefix_ + m.name () + "[]");
+
+ sema_rel::table& t (model_.new_node<sema_rel::table> (id));
+ t.set ("cxx-location", m.location ());
+ t.set ("member-path", member_path_);
+ t.set ("deleted-map", deleted_column_map ());
+ model_.new_edge<sema_rel::qnames> (model_, t, name);
+
+ t.options (table_options (m, ct));
+ t.extra ()["kind"] = "container";
+
+ // object_id
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_idt (m), "id", "object_id");
+ }
+
+ // Foreign key and index for the object id. Keep this foreign
+ // key first since we reply on this information to lookup the
+ // corresponding object table.
+ //
+ {
+ // Derive the name prefix. See the comment for the other foreign
+ // key code above.
+ //
+ // Note also that id_name can be a column prefix (if id is
+ // composite), in which case it can be empty. In this case
+ // we just fallback on the default name.
+ //
+ // Finally, this is a top-level column, so there is no column
+ // prefix.
+ //
+ string id_name (
+ column_name (m, "id", "object_id", column_prefix ()));
+
+ if (id_name.empty ())
+ id_name = "object_id";
+
+ // Foreign key.
+ //
+ sema_rel::foreign_key& fk (
+ model_.new_node<sema_rel::foreign_key> (
+ id + ".id",
+ table_name (*context::top_object),
+ sema_rel::deferrable::not_deferrable,
+ sema_rel::foreign_key::cascade));
+ fk.set ("cxx-location", m.location ());
+ model_.new_edge<sema_rel::unames> (
+ t, fk, fkey_name (t.name (), id_name));
+
+ // Get referenced columns.
+ //
+ {
+ data_member_path& id (*id_member (*context::top_object));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (id);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+ }
+
+ // All the columns we have in this table so far are for the
+ // object id. Add them to the foreign key.
+ //
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end ();
+ ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
+ }
+
+ // Index. See if we have a custom index.
+ //
+ index* sin (m.count ("id-index") ? &m.get<index> ("id-index") : 0);
+ sema_rel::index* in (0);
+
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".id", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", location (sin->loc));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".id");
+ in->set ("cxx-location", m.location ());
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ t,
+ *in,
+ sin != 0 && !sin->name.empty ()
+ ? sin->name
+ : index_name (name, id_name));
+
+ // All the columns we have in this table so far are for the
+ // object id. Add them to the index.
+ //
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end ();
+ ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (
+ *in, *c, (sin != 0 ? sin->members.back ().options : ""));
+ }
+ }
+
+ // index (simple value)
+ //
+ bool ordered (ck == ck_ordered && !unordered (m));
+ if (ordered)
+ {
+ // Column.
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_it (m), "index", "index");
+ }
+
+ // This is a simple value so the name cannot be empty. It is
+ // also a top-level column, so there is no column prefix.
+ //
+ string col (column_name (m, "index", "index", column_prefix ()));
+
+ // Index. See if we have a custom index.
+ //
+ index* sin (m.count ("index-index")
+ ? &m.get<index> ("index-index")
+ : 0);
+ sema_rel::index* in (0);
+
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".index", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", location (sin->loc));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".index");
+ in->set ("cxx-location", m.location ());
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ t,
+ *in,
+ sin != 0 && !sin->name.empty ()
+ ? sin->name
+ : index_name (name, col));
+
+ column* c (t.find<column> (col));
+ assert (c != 0);
+
+ model_.new_edge<sema_rel::contains> (
+ *in,
+ *c,
+ (sin != 0 ? sin->members.back ().options : ""));
+ }
+
+ // key
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_kt (m), "key", "key");
+ }
+
+ // value
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_vt (m), "value", "value");
+ }
+ }
+
+ protected:
+ sema_rel::model& model_;
+ string id_prefix_;
+ };
+
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ (sema_rel::model& model): model_ (model) {}
+
+ virtual string
+ table_options (type& c)
+ {
+ return context::table_options (c);
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!options.at_once () && class_file (c) != unit.file ())
+ return;
+
+ if (!object (c))
+ return;
+
+ semantics::class_* poly (polymorphic (c));
+
+ if (abstract (c) && poly == 0)
+ return;
+
+ qname const& name (table_name (c));
+
+ // If the table with this name was already seen, assume the
+ // user knows what they are doing and skip it.
+ //
+ if (tables_.count (name))
+ return;
+
+ if (deleted (c))
+ {
+ model_.get<deleted_table_map> ("deleted-map")[name] = &c;
+ return;
+ }
+
+ string id (class_fq_name (c), 2); // Remove leading '::'.
+
+ sema_rel::table& t (model_.new_node<sema_rel::table> (id));
+ t.set ("cxx-location", c.location ());
+ t.set ("class", &c);
+ t.set ("deleted-map", deleted_column_map ());
+ model_.new_edge<sema_rel::qnames> (model_, t, name);
+
+ t.options (table_options (c));
+
+ t.extra ()["kind"] =(poly == 0
+ ? "object"
+ : (poly == &c
+ ? "polymorphic root object"
+ : "polymorphic derived object"));
+
+ // Add columns.
+ //
+ {
+ bool tr (true); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, tr);
+ oc->traverse (c);
+ }
+
+ // Add indexes.
+ //
+ {
+ instance<object_indexes> oi (model_, t);
+ oi->traverse (c);
+ }
+
+ tables_.insert (name);
+
+ // Create tables for members.
+ //
+ {
+ instance<member_create> mc (model_);
+ mc->traverse (c);
+ }
+ }
+
+ protected:
+ sema_rel::model& model_;
+ tables tables_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MODEL_HXX
diff --git a/odb/odb/relational/mssql/common.cxx b/odb/odb/relational/mssql/common.cxx
new file mode 100644
index 0000000..1070d21
--- /dev/null
+++ b/odb/odb/relational/mssql/common.cxx
@@ -0,0 +1,603 @@
+// file : odb/relational/mssql/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/mssql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ const sql_type& st (*mi.st);
+
+ // The same long/short data test as in context.cxx:long_data().
+ //
+ switch (st.type)
+ {
+ // Integral types.
+ //
+ case sql_type::BIT:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Fixed and floating point types.
+ //
+ case sql_type::DECIMAL:
+ {
+ traverse_decimal (mi);
+ break;
+ }
+ case sql_type::SMALLMONEY:
+ {
+ traverse_smallmoney (mi);
+ break;
+ }
+ case sql_type::MONEY:
+ {
+ traverse_money (mi);
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ if (st.prec > 24)
+ traverse_float8 (mi);
+ else
+ traverse_float4 (mi);
+
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ {
+ // Zero precision means max in VARCHAR(max).
+ //
+ if (st.prec == 0 || st.prec > options.mssql_short_limit ())
+ traverse_long_string (mi);
+ else
+ traverse_string (mi);
+
+ break;
+ }
+ case sql_type::TEXT:
+ {
+ traverse_long_string (mi);
+ break;
+ }
+ case sql_type::NCHAR:
+ case sql_type::NVARCHAR:
+ {
+ // Zero precision means max in NVARCHAR(max). Note that
+ // the precision is in 2-byte UCS-2 characters, not bytes.
+ //
+ if (st.prec == 0 || st.prec * 2 > options.mssql_short_limit ())
+ traverse_long_nstring (mi);
+ else
+ traverse_nstring (mi);
+
+ break;
+ }
+ case sql_type::NTEXT:
+ {
+ traverse_long_nstring (mi);
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::VARBINARY:
+ {
+ // Zero precision means max in VARCHAR(max).
+ //
+ if (st.prec == 0 || st.prec > options.mssql_short_limit ())
+ traverse_long_binary (mi);
+ else
+ traverse_binary (mi);
+
+ break;
+ }
+ case sql_type::IMAGE:
+ {
+ traverse_long_binary (mi);
+ break;
+ }
+
+ // Date-time types.
+ //
+ case sql_type::DATE:
+ {
+ traverse_date (mi);
+ break;
+ }
+ case sql_type::TIME:
+ {
+ traverse_time (mi);
+ break;
+ }
+ case sql_type::DATETIME:
+ case sql_type::DATETIME2:
+ case sql_type::SMALLDATETIME:
+ {
+ traverse_datetime (mi);
+ break;
+ }
+ case sql_type::DATETIMEOFFSET:
+ {
+ traverse_datetimeoffset (mi);
+ break;
+ }
+
+ // Other types.
+ //
+ case sql_type::UNIQUEIDENTIFIER:
+ {
+ traverse_uniqueidentifier (mi);
+ break;
+ }
+ case sql_type::ROWVERSION:
+ {
+ traverse_rowversion (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ static const char* integer_types[] =
+ {
+ "unsigned char",
+ "unsigned char",
+ "short",
+ "int",
+ "long long"
+ };
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_mssql >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ type_ = integer_types[mi.st->type - sql_type::BIT];
+ }
+
+ void member_image_type::
+ traverse_decimal (member_info&)
+ {
+ type_ = "mssql::decimal";
+ }
+
+ void member_image_type::
+ traverse_smallmoney (member_info&)
+ {
+ type_ = "mssql::smallmoney";
+ }
+
+ void member_image_type::
+ traverse_money (member_info&)
+ {
+ type_ = "mssql::money";
+ }
+
+ void member_image_type::
+ traverse_float4 (member_info&)
+ {
+ type_ = "float";
+ }
+
+ void member_image_type::
+ traverse_float8 (member_info&)
+ {
+ type_ = "double";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_long_string (member_info&)
+ {
+ type_ = "mssql::long_callback";
+ }
+
+ void member_image_type::
+ traverse_nstring (member_info&)
+ {
+ type_ = "mssql::ucs2_char*";
+ }
+
+ void member_image_type::
+ traverse_long_nstring (member_info&)
+ {
+ type_ = "mssql::long_callback";
+ }
+
+ void member_image_type::
+ traverse_binary (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_long_binary (member_info&)
+ {
+ type_ = "mssql::long_callback";
+ }
+
+ void member_image_type::
+ traverse_date (member_info&)
+ {
+ type_ = "mssql::date";
+ }
+
+ void member_image_type::
+ traverse_time (member_info&)
+ {
+ type_ = "mssql::time";
+ }
+
+ void member_image_type::
+ traverse_datetime (member_info&)
+ {
+ type_ = "mssql::datetime";
+ }
+
+ void member_image_type::
+ traverse_datetimeoffset (member_info&)
+ {
+ type_ = "mssql::datetimeoffset";
+ }
+
+ void member_image_type::
+ traverse_uniqueidentifier (member_info&)
+ {
+ type_ = "mssql::uniqueidentifier";
+ }
+
+ void member_image_type::
+ traverse_rowversion (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ static const char* integer_database_id[] =
+ {
+ "mssql::id_bit",
+ "mssql::id_tinyint",
+ "mssql::id_smallint",
+ "mssql::id_int",
+ "mssql::id_bigint"
+ };
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ())
+ {
+ }
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix)
+ {
+ }
+
+ string member_database_type_id::
+ database_type_id (semantics::data_member& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info& mi)
+ {
+ type_id_ = integer_database_id[mi.st->type - sql_type::BIT];
+ }
+
+ void member_database_type_id::
+ traverse_decimal (member_info&)
+ {
+ type_id_ = "mssql::id_decimal";
+ }
+
+ void member_database_type_id::
+ traverse_smallmoney (member_info&)
+ {
+ type_id_ = "mssql::id_smallmoney";
+ }
+
+ void member_database_type_id::
+ traverse_money (member_info&)
+ {
+ type_id_ = "mssql::id_money";
+ }
+
+ void member_database_type_id::
+ traverse_float4 (member_info&)
+ {
+ type_id_ = "mssql::id_float4";
+ }
+
+ void member_database_type_id::
+ traverse_float8 (member_info&)
+ {
+ type_id_ = "mssql::id_float8";
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info&)
+ {
+ type_id_ = "mssql::id_string";
+ }
+
+ void member_database_type_id::
+ traverse_long_string (member_info&)
+ {
+ type_id_ = "mssql::id_long_string";
+ }
+
+ void member_database_type_id::
+ traverse_nstring (member_info&)
+ {
+ type_id_ = "mssql::id_nstring";
+ }
+
+ void member_database_type_id::
+ traverse_long_nstring (member_info&)
+ {
+ type_id_ = "mssql::id_long_nstring";
+ }
+
+ void member_database_type_id::
+ traverse_binary (member_info&)
+ {
+ type_id_ = "mssql::id_binary";
+ }
+
+ void member_database_type_id::
+ traverse_long_binary (member_info&)
+ {
+ type_id_ = "mssql::id_long_binary";
+ }
+
+ void member_database_type_id::
+ traverse_date (member_info&)
+ {
+ type_id_ = "mssql::id_date";
+ }
+
+ void member_database_type_id::
+ traverse_time (member_info&)
+ {
+ type_id_ = "mssql::id_time";
+ }
+
+ void member_database_type_id::
+ traverse_datetime (member_info&)
+ {
+ type_id_ = "mssql::id_datetime";
+ }
+
+ void member_database_type_id::
+ traverse_datetimeoffset (member_info&)
+ {
+ type_id_ = "mssql::id_datetimeoffset";
+ }
+
+ void member_database_type_id::
+ traverse_uniqueidentifier (member_info&)
+ {
+ type_id_ = "mssql::id_uniqueidentifier";
+ }
+
+ void member_database_type_id::
+ traverse_rowversion (member_info&)
+ {
+ type_id_ = "mssql::id_rowversion";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ virtual void
+ column_ctor (string const& type, string const& name, string const& base)
+ {
+ os << name << " (";
+
+ if (multi_dynamic)
+ os << "odb::query_column< " << type << " >& qc," << endl;
+
+ os << "const char* t," << endl
+ << "const char* c," << endl
+ << "const char* conv," << endl
+ << "unsigned short p = 0," << endl
+ << "unsigned short s = 0xFFFF)" << endl
+ << " : " << base << " (" << (multi_dynamic ? "qc, " : "") <<
+ "t, c, conv, p, s)"
+ << "{"
+ << "}";
+ }
+
+ virtual void
+ column_ctor_args_extra (semantics::data_member& m)
+ {
+ // For some types we need to pass precision and scale.
+ //
+ sql_type const& st (parse_sql_type (column_type (), m));
+
+ switch (st.type)
+ {
+ case sql_type::DECIMAL:
+ {
+ os << ", " << st.prec << ", " << st.scale;
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ os << ", " << st.prec;
+ break;
+ }
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ {
+ os << ", " << st.prec;
+ break;
+ }
+ case sql_type::TEXT:
+ {
+ os << ", 0"; // Unlimited.
+ break;
+ }
+ case sql_type::NCHAR:
+ case sql_type::NVARCHAR:
+ {
+ os << ", " << st.prec; // In 2-byte characters.
+ break;
+ }
+ case sql_type::NTEXT:
+ {
+ os << ", 0"; // Unlimited.
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::VARBINARY:
+ {
+ os << ", " << st.prec;
+ break;
+ }
+ case sql_type::IMAGE:
+ {
+ os << ", 0"; // Unlimited.
+ break;
+ }
+ // Date-time types.
+ //
+ case sql_type::TIME:
+ case sql_type::DATETIME2:
+ case sql_type::DATETIMEOFFSET:
+ {
+ os << ", 0, " << st.scale; // Fractional seconds (scale).
+ break;
+ }
+ case sql_type::DATETIME:
+ {
+ os << ", 0, 3";
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ os << ", 0, 8";
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/mssql/common.hxx b/odb/odb/relational/mssql/common.hxx
new file mode 100644
index 0000000..42ea412
--- /dev/null
+++ b/odb/odb/relational/mssql/common.hxx
@@ -0,0 +1,293 @@
+// file : odb/relational/mssql/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MSSQL_COMMON_HXX
+#define ODB_RELATIONAL_MSSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+namespace relational
+{
+ namespace mssql
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the mssql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_smallmoney (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_money (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float4 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float8 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_long_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_nstring (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_long_nstring (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_binary (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_long_binary (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_datetime (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_rowversion (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_smallmoney (member_info&);
+
+ virtual void
+ traverse_money (member_info&);
+
+ virtual void
+ traverse_float4 (member_info&);
+
+ virtual void
+ traverse_float8 (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_long_string (member_info&);
+
+ virtual void
+ traverse_nstring (member_info&);
+
+ virtual void
+ traverse_long_nstring (member_info&);
+
+ virtual void
+ traverse_binary (member_info&);
+
+ virtual void
+ traverse_long_binary (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_time (member_info&);
+
+ virtual void
+ traverse_datetime (member_info&);
+
+ virtual void
+ traverse_datetimeoffset (member_info&);
+
+ virtual void
+ traverse_uniqueidentifier (member_info&);
+
+ virtual void
+ traverse_rowversion (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_smallmoney (member_info&);
+
+ virtual void
+ traverse_money (member_info&);
+
+ virtual void
+ traverse_float4 (member_info&);
+
+ virtual void
+ traverse_float8 (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_long_string (member_info&);
+
+ virtual void
+ traverse_nstring (member_info&);
+
+ virtual void
+ traverse_long_nstring (member_info&);
+
+ virtual void
+ traverse_binary (member_info&);
+
+ virtual void
+ traverse_long_binary (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_time (member_info&);
+
+ virtual void
+ traverse_datetime (member_info&);
+
+ virtual void
+ traverse_datetimeoffset (member_info&);
+
+ virtual void
+ traverse_uniqueidentifier (member_info&);
+
+ virtual void
+ traverse_rowversion (member_info&);
+
+ private:
+ string type_id_;
+ };
+
+ struct has_long_data: object_columns_base, context
+ {
+ has_long_data (bool& r): r_ (r) {}
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ if (!inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const&, bool)
+ {
+ if (long_data (parse_sql_type (column_type (), m)))
+ r_ = true;
+
+ return true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_MSSQL_COMMON_HXX
diff --git a/odb/odb/relational/mssql/context.cxx b/odb/odb/relational/mssql/context.cxx
new file mode 100644
index 0000000..afe1aa5
--- /dev/null
+++ b/odb/odb/relational/mssql/context.cxx
@@ -0,0 +1,766 @@
+// file : odb/relational/mssql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "BIT", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"wchar_t", "NCHAR(1)", 0, false},
+ {"signed char", "TINYINT", 0, false},
+ {"unsigned char", "TINYINT", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT", 0, false},
+
+ {"int", "INT", 0, false},
+ {"unsigned int", "INT", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT", 0, false},
+
+ {"float", "REAL", 0, false},
+ {"double", "FLOAT", 0, false},
+
+ {"::std::string", "VARCHAR(512)", "VARCHAR(256)", false},
+ {"::std::wstring", "NVARCHAR(512)", "NVARCHAR(256)", false},
+
+ {"::size_t", "BIGINT", 0, false},
+ {"::std::size_t", "BIGINT", 0, false},
+
+ // Windows GUID/UUID (typedef struct _GUID {...} GUID, UUID;).
+ //
+ {"::_GUID", "UNIQUEIDENTIFIER", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = false;
+ need_alias_as = true;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = true;
+ need_image_clone = true;
+ generate_bulk = true;
+ global_index = false;
+ global_fkey = true;
+ data_->bind_vector_ = "mssql::bind*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the 128 limit.
+ //
+ if (i->size () > 128)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than the "
+ << "SQL Server name limit of 128 characters and will be "
+ << "truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '[';
+ r.append (*i, 0, 128); // Max identifier length is 128.
+ r += ']';
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ bool c (bt.is_a<semantics::fund_char> ());
+
+ if (c || bt.is_a<semantics::fund_wchar> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ if (n == 1)
+ r = c ? "CHAR(" : "NCHAR(";
+ else
+ {
+ r = c ? "VARCHAR(" : "NVARCHAR(";
+ n--;
+ }
+
+ if (n > (c ? 8000 : 4000))
+ r += "max)";
+ else
+ {
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+ }
+
+ return r;
+ }
+
+ bool context::
+ long_data (sql_type const& st)
+ {
+ bool r (false);
+
+ // The same test as in common.cxx:traverse_simple().
+ //
+ switch (st.type)
+ {
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::BINARY:
+ case sql_type::VARBINARY:
+ {
+ // Zero precision means max in VARCHAR(max).
+ //
+ if (st.prec == 0 || st.prec > options.mssql_short_limit ())
+ r = true;
+
+ break;
+ }
+ case sql_type::NCHAR:
+ case sql_type::NVARCHAR:
+ {
+ // Zero precision means max in NVARCHAR(max). Note that
+ // the precision is in 2-byte UCS-2 characters, not bytes.
+ //
+ if (st.prec == 0 || st.prec * 2 > options.mssql_short_limit ())
+ r = true;
+
+ break;
+ }
+ case sql_type::TEXT:
+ case sql_type::NTEXT:
+ case sql_type::IMAGE:
+ {
+ r = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ namespace
+ {
+ struct sql_parser
+ {
+ typedef context::invalid_sql_type invalid_sql_type;
+
+ sql_parser (custom_db_types const* ct): ct_ (ct) {}
+
+ sql_type
+ parse (std::string sql)
+ {
+ r_ = sql_type ();
+ m_.clear ();
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct_ != 0)
+ {
+ for (custom_db_types::const_iterator i (ct_->begin ());
+ i != ct_->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sql))
+ {
+ r_.to = t.type.replace (sql, t.to);
+ r_.from = t.type.replace (sql, t.from);
+ sql = t.type.replace (sql, t.as);
+ break;
+ }
+ }
+ }
+
+ l_.lex (sql);
+
+ bool ok (true);
+
+ try
+ {
+ ok = parse_name ();
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ ok = false;
+ m_ = "invalid SQL Server type declaration: " + e.message;
+ }
+
+ if (!ok)
+ {
+ if (ct_ == 0)
+ return sql_type ();
+ else
+ throw invalid_sql_type (m_);
+ }
+
+ return r_;
+ }
+
+ bool
+ parse_name ()
+ {
+ sql_token t (l_.next ());
+
+ if (t.type () != sql_token::t_identifier)
+ {
+ m_ = "expected SQL Server type name instead of '" +
+ t.string () + "'";
+ return false;
+ }
+
+ string id (upcase (t.identifier ()));
+
+ if (id == "BIT")
+ {
+ r_.type = sql_type::BIT;
+ }
+ else if (id == "TINYINT")
+ {
+ r_.type = sql_type::TINYINT;
+ }
+ else if (id == "SMALLINT")
+ {
+ r_.type = sql_type::SMALLINT;
+ }
+ else if (id == "INT" ||
+ id == "INTEGER")
+ {
+ r_.type = sql_type::INT;
+ }
+ else if (id == "BIGINT")
+ {
+ r_.type = sql_type::BIGINT;
+ }
+ else if (id == "DECIMAL" ||
+ id == "NUMERIC" ||
+ id == "DEC")
+ {
+ r_.type = sql_type::DECIMAL;
+
+ r_.has_prec = true;
+ r_.prec = 18;
+
+ r_.has_scale = true;
+ r_.scale = 0;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "SMALLMONEY")
+ {
+ r_.type = sql_type::SMALLMONEY;
+ }
+ else if (id == "MONEY")
+ {
+ r_.type = sql_type::MONEY;
+ }
+ else if (id == "REAL")
+ {
+ r_.type = sql_type::FLOAT;
+
+ r_.has_prec = true;
+ r_.prec = 24;
+ }
+ else if (id == "FLOAT")
+ {
+ r_.type = sql_type::FLOAT;
+
+ r_.has_prec = true;
+ r_.prec = 53;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "DOUBLE")
+ {
+ t = l_.next ();
+
+ if (t.type () != sql_token::t_identifier ||
+ upcase (t.identifier ()) != "PRECISION")
+ {
+ m_ = "expected 'PRECISION' instead of '" + t.string () + "'";
+ return false;
+ }
+
+ r_.type = sql_type::FLOAT;
+
+ r_.has_prec = true;
+ r_.prec = 53;
+
+ // It appears that DOUBLE PRECISION can be followed by the
+ // precision specification.
+ //
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "CHAR" ||
+ id == "CHARACTER")
+ {
+ if (!parse_char_trailer (false))
+ return false;
+ }
+ else if (id == "VARCHAR")
+ {
+ r_.type = sql_type::VARCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "TEXT")
+ {
+ r_.type = sql_type::TEXT;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "NCHAR")
+ {
+ r_.type = sql_type::NCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "NVARCHAR")
+ {
+ r_.type = sql_type::NVARCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "NTEXT")
+ {
+ r_.type = sql_type::NTEXT;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "NATIONAL")
+ {
+ t = l_.next ();
+
+ if (t.type () == sql_token::t_identifier)
+ id = upcase (t.identifier ());
+
+ if (id == "TEXT")
+ {
+ r_.type = sql_type::NTEXT;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "CHAR" ||
+ id == "CHARACTER")
+ {
+ if (!parse_char_trailer (true))
+ return false;
+ }
+ else
+ {
+ m_ = "expected 'CHAR', 'CHARACTER', or 'TEXT' instead of '"
+ + t.string () + "'";
+ return false;
+ }
+ }
+ else if (id == "BINARY")
+ {
+ // Can be just BINARY or BINARY VARYING.
+ //
+ t = l_.next ();
+
+ if (t.type () == sql_token::t_identifier)
+ id = upcase (t.identifier ());
+
+ if (id == "VARYING")
+ {
+ r_.type = sql_type::VARBINARY;
+ t = l_.next ();
+ }
+ else
+ r_.type = sql_type::BINARY;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (t))
+ return false;
+ }
+ else if (id == "VARBINARY")
+ {
+ r_.type = sql_type::VARBINARY;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "IMAGE")
+ {
+ r_.type = sql_type::IMAGE;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "DATE")
+ {
+ r_.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r_.type = sql_type::TIME;
+
+ r_.has_scale = true;
+ r_.scale = 7;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "DATETIME")
+ {
+ r_.type = sql_type::DATETIME;
+ }
+ else if (id == "DATETIME2")
+ {
+ r_.type = sql_type::DATETIME2;
+
+ r_.has_scale = true;
+ r_.scale = 7;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "SMALLDATETIME")
+ {
+ r_.type = sql_type::SMALLDATETIME;
+ }
+ else if (id == "DATETIMEOFFSET")
+ {
+ r_.type = sql_type::DATETIMEOFFSET;
+
+ r_.has_scale = true;
+ r_.scale = 7;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "UNIQUEIDENTIFIER")
+ {
+ r_.type = sql_type::UNIQUEIDENTIFIER;
+ }
+ else if (id == "ROWVERSION" ||
+ id == "TIMESTAMP")
+ {
+ r_.type = sql_type::ROWVERSION;
+ }
+ else
+ {
+ m_ = "unexpected SQL Server type name '" + t.identifier () + "'";
+ return false;
+ }
+
+ return true;
+ }
+
+ bool
+ parse_precision (sql_token t)
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ // Parse the precision.
+ //
+ t = l_.next ();
+
+ if (t.type () == sql_token::t_identifier &&
+ upcase (t.identifier ()) == "MAX")
+ {
+ r_.prec = 0;
+ r_.has_prec = true;
+ }
+ else if (t.type () == sql_token::t_int_lit)
+ {
+ unsigned short v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ m_ = "invalid precision value '" + t.literal () + "' in SQL "
+ "Server type declaration";
+ return false;
+ }
+
+ switch (r_.type)
+ {
+ case sql_type::TIME:
+ case sql_type::DATETIME2:
+ case sql_type::DATETIMEOFFSET:
+ {
+ r_.scale = v;
+ r_.has_scale = true;
+ break;
+ }
+ default:
+ {
+ r_.prec = v;
+ r_.has_prec = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ m_ = "integer precision expected in SQL Server type declaration";
+ return false;
+ }
+
+ // Parse the scale if present.
+ //
+ t = l_.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // Scale can only be specified for the DECIMAL type.
+ //
+ if (r_.type != sql_type::DECIMAL)
+ {
+ m_ = "unexpected scale in SQL Server type declaration";
+ return false;
+ }
+
+ t = l_.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ m_ = "integer scale expected in SQL Server type declaration";
+ return false;
+ }
+
+ istringstream is (t.literal ());
+
+ if (!(is >> r_.scale && is.eof ()))
+ {
+ m_ = "invalid scale value '" + t.literal () + "' in SQL "
+ "Server type declaration";
+ return false;
+ }
+
+ r_.has_scale = true;
+ t = l_.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ m_ = "expected ')' in SQL Server type declaration";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool
+ parse_char_trailer (bool nat)
+ {
+ sql_token t (l_.next ());
+
+ string id;
+
+ if (t.type () == sql_token::t_identifier)
+ id = upcase (t.identifier ());
+
+ if (id == "VARYING")
+ {
+ r_.type = nat ? sql_type::NVARCHAR : sql_type::VARCHAR;
+ t = l_.next ();
+ }
+ else
+ r_.type = nat ? sql_type::NCHAR : sql_type::CHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ return parse_precision (t);
+ }
+
+ private:
+ string
+ upcase (string const& s)
+ {
+ return context::upcase (s);
+ }
+
+ private:
+ custom_db_types const* ct_;
+ sql_lexer l_;
+ sql_type r_;
+ string m_; // Error message.
+ };
+ }
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ sql_type context::
+ parse_sql_type (string const& sqlt, custom_db_types const* ct)
+ {
+ sql_parser p (ct);
+ return p.parse (sqlt);
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/context.hxx b/odb/odb/relational/mssql/context.hxx
new file mode 100644
index 0000000..7701aaa
--- /dev/null
+++ b/odb/odb/relational/mssql/context.hxx
@@ -0,0 +1,194 @@
+// file : odb/relational/mssql/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MSSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_MSSQL_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace mssql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ BIT,
+ TINYINT,
+ SMALLINT,
+ INT,
+ BIGINT,
+
+ // Fixed and floating point types.
+ //
+ DECIMAL,
+ SMALLMONEY,
+ MONEY,
+ FLOAT,
+
+ // String and binary types.
+ //
+ CHAR,
+ VARCHAR,
+ TEXT,
+
+ NCHAR,
+ NVARCHAR,
+ NTEXT,
+
+ BINARY,
+ VARBINARY,
+ IMAGE,
+
+ // Date-time types.
+ //
+ DATE,
+ TIME,
+ DATETIME,
+ DATETIME2,
+ SMALLDATETIME,
+ DATETIMEOFFSET,
+
+ // Other types.
+ //
+ UNIQUEIDENTIFIER,
+ ROWVERSION,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () :
+ type (invalid),
+ has_prec (false), prec (0),
+ has_scale (false), scale (0)
+ {
+ }
+
+ core_type type;
+
+ bool has_prec;
+ unsigned short prec; // Max numeric value is 8000. 0 indicates
+ // 'max' as in VARCHAR(max).
+ bool has_scale;
+ unsigned short scale; // Max value is 38.
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+
+ // Return true if this type is long data.
+ //
+ bool
+ long_data (sql_type const&);
+
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string const&, custom_db_types const* = 0);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MSSQL_CONTEXT_HXX
diff --git a/odb/odb/relational/mssql/header.cxx b/odb/odb/relational/mssql/header.cxx
new file mode 100644
index 0000000..ebdc734
--- /dev/null
+++ b/odb/odb/relational/mssql/header.cxx
@@ -0,0 +1,312 @@
+// file : odb/relational/mssql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct class1: relational::class1, context
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_pre (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+
+ // rowvesion
+ //
+ bool rv (false);
+ if (semantics::data_member* m = optimistic (c))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ rv = (t.type == sql_type::ROWVERSION);
+ }
+
+ os << "static const bool rowversion = " << rv << ";"
+ << endl;
+
+ // Disable bulk update if we have ROWVERSION since we don't
+ // yet support batch extraction of the version.
+ //
+ if (rv && c.count ("bulk-update"))
+ c.remove ("bulk-update");
+ }
+
+ virtual void
+ object_public_extra_post (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ if (semantics::data_member* m = optimistic (c))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ os << "static version_type" << endl
+ << "version (const id_image_type&);"
+ << endl;
+ }
+ }
+ }
+ };
+ entry<class1> class1_entry_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_pre (user_section&)
+ {
+ if (abstract (c_) && !polymorphic (c_))
+ return;
+
+ // rowvesion
+ //
+ bool rv (false);
+ if (semantics::data_member* m = optimistic (c_))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ rv = (t.type == sql_type::ROWVERSION);
+ }
+
+ os << "static const bool rowversion = " << rv << ";"
+ << endl;
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct image_type: relational::image_type, context
+ {
+ image_type (base const& x): base (x) {};
+
+ virtual void
+ image_extra (type& c)
+ {
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+
+ // If this is a polymorphic type, only add callback to the root.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ bool gc (options.generate_query ());
+
+ if (gc)
+ os << "mssql::change_callback change_callback_;"
+ << endl;
+
+ os << "mssql::change_callback*" << endl
+ << "change_callback ()"
+ << "{";
+
+ if (gc)
+ os << "return &change_callback_;";
+ else
+ os << "return 0;";
+
+ os << "}";
+ }
+ }
+ }
+ };
+ entry<image_type> image_type_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ // Extra character for the null-terminator that ODBC always adds.
+ //
+ os << "char " << mi.var << "value[" << mi.st->prec + 1 << "];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ // Extra character for the null-terminator that ODBC always adds.
+ //
+ os << "mssql::ucs2_char " << mi.var << "value[" <<
+ mi.st->prec + 1 << "];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << "char " << mi.var << "value[" << mi.st->prec << "];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << "unsigned char " << mi.var << "value[8];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/inline.cxx b/odb/odb/relational/mssql/inline.cxx
new file mode 100644
index 0000000..eb581d6
--- /dev/null
+++ b/odb/odb/relational/mssql/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/mssql/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "size_ind == SQL_NULL_DATA;";
+ else
+ os << "i." << mi.var << "size_ind = SQL_NULL_DATA;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/model.cxx b/odb/odb/relational/mssql/model.cxx
new file mode 100644
index 0000000..0f5a85c
--- /dev/null
+++ b/odb/odb/relational/mssql/model.cxx
@@ -0,0 +1,66 @@
+// file : odb/relational/mssql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to an integer or DECIMAL type.
+ //
+ switch (parse_sql_type (column_type (), m, false).type)
+ {
+ case sql_type::BIT:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ case sql_type::DECIMAL:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to SQL Server integer type" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/schema.cxx b/odb/odb/relational/mssql/schema.cxx
new file mode 100644
index 0000000..c5f6bc1
--- /dev/null
+++ b/odb/odb/relational/mssql/schema.cxx
@@ -0,0 +1,651 @@
+// file : odb/relational/mssql/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ struct sql_emitter: relational::sql_emitter
+ {
+ sql_emitter (const base& x): base (x) {}
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ {
+ os << ';' << endl
+ << "GO" << endl
+ << endl;
+ }
+ }
+ };
+ entry<sql_emitter> sql_emitter_;
+
+ //
+ // File.
+ //
+
+ struct sql_file: relational::sql_file, context
+ {
+ sql_file (const base& x): base (x) {}
+
+ virtual void
+ prologue ()
+ {
+ // Suppress the (x rows affected) messages from sqlcmd for DML
+ // statements. We only use DML for schema version management.
+ //
+ if ((model == 0 || model->version () != 0) &&
+ !options.suppress_schema_version ())
+ os << "SET NOCOUNT ON;" << endl
+ << endl;
+ }
+ };
+ entry<sql_file> sql_file_;
+
+ //
+ // Drop.
+ //
+
+ struct drop_column: relational::drop_column, context
+ {
+ drop_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (dc.name ());
+ }
+ };
+ entry<drop_column> drop_column_;
+
+ struct drop_foreign_key: relational::drop_foreign_key, context
+ {
+ drop_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ bool migration (dropped_ == 0);
+
+ if (migration)
+ {
+ if (fk.not_deferrable ())
+ pre_statement ();
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ }
+ }
+ else
+ {
+ // Here we drop potentially deferrable keys and also need to
+ // test if the key exists.
+ //
+ pre_statement ();
+
+ os << "IF OBJECT_ID(" << quote_string (fk.name ()) << ", " <<
+ quote_string ("F") << ") IS NOT NULL" << endl
+ << " ";
+ }
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << (migration ? " " : " ") << "DROP CONSTRAINT " <<
+ quote_id (fk.name ()) << endl;
+
+
+ if (!migration || fk.not_deferrable ())
+ post_statement ();
+ else
+ os << "*/" << endl
+ << endl;
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ // Find the foreign key we are dropping in the base model.
+ //
+ sema_rel::foreign_key& fk (find<sema_rel::foreign_key> (dfk));
+
+ bool c (!fk.not_deferrable () && !in_comment);
+
+ if (c && format_ != schema_format::sql)
+ return;
+
+ if (!first_)
+ os << (c ? "" : ",") << endl
+ << " ";
+
+ if (c)
+ os << "/* ";
+
+ os << quote_id (fk.name ());
+
+ if (c)
+ os << " */";
+
+ if (first_)
+ {
+ if (c)
+ // There has to be a real name otherwise the whole statement
+ // would have been commented out.
+ //
+ os << endl
+ << " ";
+ else
+ first_ = false;
+ }
+ }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::index& in)
+ {
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+
+ os << "DROP INDEX " << name (in) << " ON " <<
+ quote_id (t.name ()) << endl;
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, bool migration)
+ {
+ // SQL Server has no IF EXISTS conditional for dropping tables.
+ // The following approach appears to be the recommended way to
+ // drop a table if it exists.
+ //
+ sema_rel::qname const& name (t.name ());
+
+ pre_statement ();
+
+ if (!migration)
+ os << "IF OBJECT_ID(" << quote_string (name.string ()) <<
+ ", " << quote_string ("U") << ") IS NOT NULL" << endl
+ << " ";
+
+ os << "DROP TABLE " << quote_id (name) << endl;
+
+ post_statement ();
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ create (ac);
+ }
+
+ virtual void
+ auto_ (sema_rel::primary_key&)
+ {
+ os << " IDENTITY";
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ void
+ diagnose (sema_rel::foreign_key& fk)
+ {
+ if (fk.on_delete () != sema_rel::foreign_key::no_action)
+ {
+ cerr << "warning: foreign key '" << fk.name () << "' has " <<
+ "ON DELETE clause but is disabled in SQL Server due to lack "
+ "of deferrable constraint support" << endl;
+
+ cerr << "info: consider using non-deferrable foreign keys (" <<
+ "--fkeys-deferrable-mode)" << endl;
+ }
+ }
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ // SQL Server does not support deferrable constraint checking.
+ // Output such foreign keys as comments, for documentation,
+ // unless we are generating embedded schema.
+ //
+ if (fk.not_deferrable ())
+ base::traverse_create (fk);
+ else
+ {
+ diagnose (fk);
+
+ // Don't bloat C++ code with comment strings if we are
+ // generating embedded schema.
+ //
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*" << endl
+ << " CONSTRAINT ";
+ create (fk);
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ bool c (!fk.not_deferrable () && !in_comment);
+
+ if (c)
+ diagnose (fk);
+
+ if (c && format_ != schema_format::sql)
+ return;
+
+ if (!first_)
+ os << (c ? "" : ",") << endl
+ << " ";
+
+ if (c)
+ os << "/*" << endl
+ << " ";
+
+ os << "CONSTRAINT ";
+ create (fk);
+
+ if (c)
+ os << endl
+ << " */";
+
+ if (first_)
+ {
+ if (c)
+ // There has to be a real key otherwise the whole statement
+ // would have been commented out.
+ //
+ os << endl
+ << " ";
+ else
+ first_ = false;
+ }
+ }
+
+ virtual void
+ deferrable (sema_rel::deferrable)
+ {
+ // This will still be called to output the comment.
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ // See if there are any undefined foreign keys that are not
+ // deferrable.
+ //
+ bool
+ check_undefined_fk_deferrable_only (sema_rel::table& t)
+ {
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (&i->nameable ()))
+ {
+ if (!fk->count ("mssql-fk-defined") &&
+ fk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ if (pass_ == 1)
+ base::traverse (t);
+ else
+ {
+ // Add undefined foreign keys.
+ //
+ if (check_undefined_fk (t))
+ {
+ bool deferrable (check_undefined_fk_deferrable_only (t));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ADD ";
+
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (t, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+ }
+ }
+ };
+ entry<create_table> create_table_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Relax (NULL) in pre and tighten (NOT NULL) in post.
+ //
+ if (pre_ != c.null ())
+ return;
+
+ using sema_rel::table;
+ table& at (static_cast<table&> (c.scope ()));
+
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ALTER COLUMN ";
+ alter (c);
+ os << endl;
+
+ post_statement ();
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ // Check if we are only dropping deferrable foreign keys.
+ //
+ bool
+ check_drop_deferrable_only (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ if (drop_foreign_key* dfk =
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()))
+ {
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ if (fk.not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQL Server can only alter one kind of thing at a time.
+ //
+ if (check<sema_rel::drop_foreign_key> (at))
+ {
+ bool deferrable (check_drop_deferrable_only (at));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " DROP CONSTRAINT ";
+
+ instance<drop_foreign_key> dfc (*this);
+ trav_rel::unames n (*dfc);
+ names (at, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+
+ if (check<sema_rel::add_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD ";
+
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ // For ALTER COLUMN, SQL Server can only have one per ALTER TABLE.
+ //
+ {
+ bool tl (true); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, tl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ }
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ // Check if we are only adding deferrable foreign keys.
+ //
+ bool
+ check_add_deferrable_only (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_foreign_key;
+
+ if (add_foreign_key* afk =
+ dynamic_cast<add_foreign_key*> (&i->nameable ()))
+ {
+ if (afk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQL Server can only alter one kind of thing at a time.
+ //
+ if (check<sema_rel::drop_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " DROP COLUMN ";
+
+ instance<drop_column> dc (*this);
+ trav_rel::unames n (*dc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ // For ALTER COLUMN, SQL Server can only have one per ALTER TABLE.
+ //
+ {
+ bool fl (false); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, fl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ }
+
+ if (check<sema_rel::add_foreign_key> (at))
+ {
+ bool deferrable (check_add_deferrable_only (at));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD ";
+
+ instance<create_foreign_key> cfc (*this);
+ trav_rel::unames n (*cfc);
+ names (at, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "IF OBJECT_ID(" << quote_string (table_.string ()) <<
+ ", " << quote_string ("U") << ") IS NULL" << endl
+ << " CREATE TABLE " << qt_ << " (" << endl
+ << " " << qn_ << " VARCHAR(256) NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BIT NOT NULL)" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "IF NOT EXISTS (SELECT 1 FROM " << qt_ << " WHERE " << qn_ <<
+ " = " << qs_ << ")" << endl
+ << " INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/source.cxx b/odb/odb/relational/mssql/source.cxx
new file mode 100644
index 0000000..573104d
--- /dev/null
+++ b/odb/odb/relational/mssql/source.cxx
@@ -0,0 +1,1201 @@
+// file : odb/relational/mssql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ //
+ //
+ struct query_parameters: relational::query_parameters
+ {
+ query_parameters (base const& x): base (x) {}
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return "";
+ }
+ };
+ entry<query_parameters> query_parameters_;
+
+ //
+ //
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x)
+ : base (x), rowversion_ (false), column_count_ (0) {}
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ // Don't add a column for auto id in the INSERT statement.
+ // Only simple, direct id can be auto.
+ //
+ if (sk_ == statement_insert && key_prefix_.empty () && auto_ (m))
+ return false;
+
+ // Don't update the ROWVERSION column explicitly.
+ //
+ if (sk_ == statement_update)
+ {
+ sql_type t (parse_sql_type (column_type (), m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ rowversion_ = true;
+ return false;
+ }
+ }
+
+ bool r (base::column (m, table, column));
+
+ // Count the number of columns in the UPDATE statement, but
+ // excluding soft-deleted.
+ //
+ if (sk_ == statement_update && r && !deleted (member_path_))
+ column_count_++;
+
+ return r;
+ }
+
+ virtual void
+ traverse_post (semantics::nameable& n)
+ {
+ if (rowversion_ && column_count_ == 0)
+ {
+ location l (n.location ());
+ error (l) << "ROWVERSION in an object without any readwrite "
+ "data members" << endl;
+ error (l) << "UPDATE statement will be empty" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ bool rowversion_;
+ size_t column_count_;
+ };
+ entry<object_columns> object_columns_;
+
+ //
+ //
+ struct persist_statement_params: relational::persist_statement_params,
+ context
+ {
+ persist_statement_params (base const& x): base (x) {}
+
+ virtual string
+ version_value (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (), m));
+ return t.type == sql_type::ROWVERSION ? "DEFAULT" : "1";
+ }
+ };
+ entry<persist_statement_params> persist_statement_params_;
+
+ //
+ // bind
+ //
+
+ static const char* integer_buffer_types[] =
+ {
+ "mssql::bind::bit",
+ "mssql::bind::tinyint",
+ "mssql::bind::smallint",
+ "mssql::bind::int_",
+ "mssql::bind::bigint"
+ };
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = " <<
+ integer_buffer_types[mi.st->type - sql_type::BIT] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::decimal;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode precision (p) and scale (s) as (p * 100 + s).
+ //
+ << b << ".capacity = " << mi.st->prec * 100 + mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::smallmoney;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::money;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::float4;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::float8;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::string;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_string;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::nstring;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_nstring;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size (in bytes) with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec * 2 << ";";
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::binary;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_binary;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::date;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::time;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ unsigned short scale (0);
+
+ switch (mi.st->type)
+ {
+ case sql_type::DATETIME:
+ {
+ // Looks like it is 3 (rounded to 0.000, 0.003, or 0.007).
+ //
+ scale = 3;
+ break;
+ }
+ case sql_type::DATETIME2:
+ {
+ scale = mi.st->scale;
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ // No seconds in SMALLDATATIME. Encode it a special precision
+ // value (8).
+ //
+ scale = 8;
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ os << b << ".type = mssql::bind::datetime;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << scale << ";";
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::datetimeoffset;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::uniqueidentifier;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::rowversion;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "size_ind = SQL_NULL_DATA;";
+ }
+
+ virtual void
+ check_accessor (member_info& mi, member_access& ma)
+ {
+ // We cannot use accessors that return by-value for long data
+ // members.
+ //
+ if (long_data (*mi.st) && ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 4;";
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;";
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ // Don't mention the extra character for the null-terminator.
+ << "sizeof (i." << mi.var << "value) - 1," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size);";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ // Don't mention the extra character for the null-terminator.
+ << "sizeof (i." << mi.var << "value) / 2 - 1," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size * 2);";
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size);";
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << mi.st->scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null" << endl
+ << " ? SQL_NULL_DATA" << endl
+ << " : static_cast<SQLLEN> (sizeof (i." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ // The same code as in bind.
+ //
+ unsigned short scale (0);
+
+ switch (mi.st->type)
+ {
+ case sql_type::DATETIME:
+ {
+ scale = 3;
+ break;
+ }
+ case sql_type::DATETIME2:
+ {
+ scale = mi.st->scale;
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ scale = 8;
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << mi.st->scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null" << endl
+ << " ? SQL_NULL_DATA" << endl
+ << " : static_cast<SQLLEN> (sizeof (i." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "size_ind == SQL_NULL_DATA";
+ }
+
+ virtual void
+ check_modifier (member_info& mi, member_access& ma)
+ {
+ // We cannot use by-value modifier for long data members.
+ //
+ if (long_data (*mi.st) && ma.placeholder ())
+ {
+ error (ma.loc) << "modifier accepting a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "modifier returning a non-const reference is "
+ << "required" << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (i." << mi.var << "size_ind)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (" <<
+ "i." << mi.var << "size_ind / 2)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (i." << mi.var << "size_ind)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct statement_columns_common: context
+ {
+ void
+ process (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ using relational::statement_columns;
+
+ // Long data columns must come last in the SELECT statement. If
+ // this statement is going to be processed at runtime, then this
+ // will be taken care of then.
+ //
+ if (sk != statement_select || dynamic)
+ return;
+
+ // Go over the columns list while keeping track of how many
+ // columns we have examined. If the current column is long data,
+ // then move it to the back. Stop once we have examined all the
+ // columns.
+ //
+ size_t n (cols.size ());
+ for (statement_columns::iterator i (cols.begin ()); n != 0; --n)
+ {
+ if (long_data (parse_sql_type (i->type, *i->member)))
+ {
+ cols.push_back (*i);
+ i = cols.erase (i);
+ }
+ else
+ ++i;
+ }
+ }
+ };
+
+ struct container_traits: relational::container_traits,
+ statement_columns_common
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since with MARS enabled SQL Server
+ // can execute several interleaving statements.
+ //
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "sts.select_statement ().stream_result ();"
+ << endl;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits,
+ statement_columns_common
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+
+ virtual string
+ optimistic_version_increment (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : "version (sts.id_image ())";
+ }
+
+ virtual string
+ update_statement_extra (user_section&)
+ {
+ string r;
+
+ semantics::data_member* ver (optimistic (c_));
+
+ if (ver == 0 ||
+ parse_sql_type (column_type (*ver), *ver).type !=
+ sql_type::ROWVERSION)
+ return r;
+
+ // ROWVERSION & SQL Server 2005 incompatibility is detected
+ // in persist_statement_extra.
+ //
+ r = "OUTPUT INSERTED." +
+ convert_from (column_qname (*ver, column_prefix ()), *ver);
+
+ return r;
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, statement_columns_common
+ {
+ class_ (base const& x):
+ base (x), init_version_value_member_id_image_ ("v", "version_") {}
+
+ virtual void
+ init_image_pre (type& c)
+ {
+ if (options.generate_query () &&
+ !(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ os << "{"
+ << "root_traits::image_type& ri (root_image (i));"
+ << endl;
+
+ string i (poly_derived ? "ri" : "i");
+
+ os << "if (" << i << ".change_callback_.callback != 0)" << endl
+ << "(" << i << ".change_callback_.callback) (" <<
+ i << ".change_callback_.context);";
+
+ if (poly_derived)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ string r;
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // auto id/version are handled by the root.
+ //
+ if (poly_derived)
+ return r;
+
+ // See if we have auto id or ROWVERSION version.
+ //
+ data_member_path* id (id_member (c));
+ semantics::data_member* ver (optimistic (c));
+
+ if (id != 0 && !auto_ (*id))
+ id = 0;
+
+ if (ver != 0)
+ {
+ sql_type t (parse_sql_type (column_type (*ver), *ver));
+ if (t.type != sql_type::ROWVERSION)
+ ver = 0;
+ }
+
+ if (id == 0 && ver == 0)
+ return r;
+
+ // SQL Server 2005 has a bug that causes it to fail on an
+ // INSERT statement with the OUTPUT clause if data for one
+ // of the inserted columns is supplied at execution (long
+ // data). To work around this problem we use the less
+ // efficient batch of INSERT and SELECT statements.
+ //
+ if (options.mssql_server_version () <= mssql_version (9, 0))
+ {
+ bool ld (false);
+
+ if (c.count ("mssql-has-long-data"))
+ ld = c.get<bool> ("mssql-has-long-data");
+ else
+ {
+ has_long_data t (ld);
+ t.traverse (c);
+ c.set ("mssql-has-long-data", ld);
+ }
+
+ if (ld)
+ {
+ if (p == persist_after_values)
+ {
+ // SQL Server 2005 has no eqivalent of SCOPE_IDENTITY for
+ // ROWVERSION.
+ //
+ if (ver != 0)
+ {
+ error (c.location ()) << "in SQL Server 2005 ROWVERSION " <<
+ "value cannot be retrieved for a persistent class " <<
+ "containing long data" << endl;
+ throw operation_failed ();
+ }
+
+ // We also cannot support bulk INSERT.
+ //
+ if (c.count ("bulk-persist"))
+ {
+ error (c.location ()) << "in SQL Server 2005 bulk " <<
+ "persist operation cannot be implemented for a " <<
+ "persistent class containing long data" << endl;
+ throw operation_failed ();
+ }
+
+ r = "; SELECT " +
+ convert_from ("SCOPE_IDENTITY()", *id->back ());
+ }
+
+ return r;
+ }
+ }
+
+ if (p == persist_after_columns)
+ {
+ r = "OUTPUT ";
+
+ // Top-level auto id column.
+ //
+ if (id != 0)
+ r += "INSERTED." +
+ convert_from (column_qname (*id), *id->back ());
+
+ // Top-level version column.
+ //
+ if (ver != 0)
+ {
+ if (id != 0)
+ r += ',';
+
+ r += "INSERTED." + convert_from (
+ column_qname (*ver, column_prefix ()), *ver);
+ }
+ }
+
+ return r;
+ }
+
+ virtual string
+ update_statement_extra (type& c)
+ {
+ string r;
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // version is handled by the root.
+ //
+ if (poly_derived)
+ return r;
+
+ semantics::data_member* ver (optimistic (c));
+
+ if (ver == 0 ||
+ parse_sql_type (column_type (*ver), *ver).type !=
+ sql_type::ROWVERSION)
+ return r;
+
+ // Long data & SQL Server 2005 incompatibility is detected
+ // in persist_statement_extra.
+ //
+ r = "OUTPUT INSERTED." +
+ convert_from (column_qname (*ver, column_prefix ()), *ver);
+
+ return r;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+
+ virtual string
+ optimistic_version_init (semantics::data_member& m, bool index)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : (index
+ ? "version (sts.id_image (i))"
+ : "version (sts.id_image ())");
+ }
+
+ virtual string
+ optimistic_version_increment (semantics::data_member& m, bool index)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : (index
+ ? "version (sts.id_image (i))"
+ : "version (sts.id_image ())");
+ }
+
+ virtual bool
+ optimistic_insert_bind_version (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type == sql_type::ROWVERSION;
+ }
+
+ virtual void
+ object_extra (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ if (semantics::data_member* m = optimistic (c))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const id_image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_id_image_->traverse (*m);
+ os << "return v;"
+ << "}";
+ }
+ }
+ }
+
+ virtual string
+ from_trailer (type& c)
+ {
+ return c.get<view_query> ("query").for_update
+ ? " WITH (UPDLOCK)"
+ : "";
+ }
+
+ virtual string
+ select_trailer (type&) {return "";}
+
+ private:
+ // Go via the dynamic creation to get access to the constructor.
+ //
+ instance<relational::init_value_member>
+ init_version_value_member_id_image_;
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/common.cxx b/odb/odb/relational/mysql/common.cxx
new file mode 100644
index 0000000..d049443
--- /dev/null
+++ b/odb/odb/relational/mysql/common.cxx
@@ -0,0 +1,400 @@
+// file : odb/relational/mysql/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/mysql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Integral types.
+ //
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::MEDIUMINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Float types.
+ //
+ case sql_type::FLOAT:
+ case sql_type::DOUBLE:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::DECIMAL:
+ {
+ traverse_decimal (mi);
+ break;
+ }
+
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ case sql_type::TIME:
+ case sql_type::DATETIME:
+ case sql_type::TIMESTAMP:
+ case sql_type::YEAR:
+ {
+ traverse_date_time (mi);
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::TINYTEXT:
+ case sql_type::TEXT:
+ case sql_type::MEDIUMTEXT:
+ case sql_type::LONGTEXT:
+ {
+ // For string types the limit is in characters rather
+ // than in bytes. The fixed-length pre-allocated buffer
+ // optimization can only be used for 1-byte encodings.
+ // To support this we will need the character encoding
+ // in sql_type.
+ //
+ traverse_long_string (mi);
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::TINYBLOB:
+ {
+ // BINARY's range is always 255 or less from MySQL 5.0.3.
+ // TINYBLOB can only store up to 255 bytes.
+ //
+ traverse_short_string (mi);
+ break;
+ }
+ case sql_type::VARBINARY:
+ case sql_type::BLOB:
+ case sql_type::MEDIUMBLOB:
+ case sql_type::LONGBLOB:
+ {
+ if (mi.st->range && mi.st->range_value <= 255)
+ traverse_short_string (mi);
+ else
+ traverse_long_string (mi);
+
+ break;
+ }
+
+ // Other types.
+ //
+ case sql_type::BIT:
+ {
+ traverse_bit (mi);
+ break;
+ }
+ case sql_type::ENUM:
+ {
+ traverse_enum (mi);
+ break;
+ }
+ case sql_type::SET:
+ {
+ traverse_set (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ static const char* integer_types[] =
+ {
+ "char",
+ "short",
+ "int",
+ "int",
+ "long long"
+ };
+
+ static const char* float_types[] =
+ {
+ "float",
+ "double"
+ };
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_mysql >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ if (mi.st->unsign)
+ type_ = "unsigned ";
+ else if (mi.st->type == sql_type::TINYINT)
+ type_ = "signed ";
+
+ type_ += integer_types[mi.st->type - sql_type::TINYINT];
+ }
+
+ void member_image_type::
+ traverse_float (member_info& mi)
+ {
+ type_ = float_types[mi.st->type - sql_type::FLOAT];
+ }
+
+ void member_image_type::
+ traverse_decimal (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_date_time (member_info& mi)
+ {
+ if (mi.st->type == sql_type::YEAR)
+ type_ = "short";
+ else
+ type_ = "MYSQL_TIME";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_bit (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ void member_image_type::
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ type_ = "mysql::value_traits< " + mi.fq_type () +
+ ", mysql::id_enum >::image_type";
+ }
+
+ void member_image_type::
+ traverse_set (member_info&)
+ {
+ // Represented as string.
+ //
+ type_ = "details::buffer";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ static const char* integer_database_id[] =
+ {
+ "id_tiny",
+ "id_utiny",
+ "id_short",
+ "id_ushort",
+ "id_long", // INT24
+ "id_ulong", // INT24 UNSIGNED
+ "id_long",
+ "id_ulong",
+ "id_longlong",
+ "id_ulonglong"
+ };
+
+ static const char* float_database_id[] =
+ {
+ "id_float",
+ "id_double"
+ };
+
+ static const char* date_time_database_id[] =
+ {
+ "id_date",
+ "id_time",
+ "id_datetime",
+ "id_timestamp",
+ "id_year"
+ };
+
+ static const char* char_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_blob", // BINARY,
+ "id_string", // VARCHAR
+ "id_blob", // VARBINARY
+ "id_string", // TINYTEXT
+ "id_blob", // TINYBLOB
+ "id_string", // TEXT
+ "id_blob", // BLOB
+ "id_string", // MEDIUMTEXT
+ "id_blob", // MEDIUMBLOB
+ "id_string", // LONGTEXT
+ "id_blob" // LONGBLOB
+ };
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info& mi)
+ {
+ size_t i (
+ (mi.st->type - sql_type::TINYINT) * 2 + (mi.st->unsign ? 1 : 0));
+ type_id_ = string ("mysql::") + integer_database_id[i];
+ }
+
+ void member_database_type_id::
+ traverse_float (member_info& mi)
+ {
+ type_id_ = string ("mysql::") +
+ float_database_id[mi.st->type - sql_type::FLOAT];
+ }
+
+ void member_database_type_id::
+ traverse_decimal (member_info&)
+ {
+ type_id_ = "mysql::id_decimal";
+ }
+
+ void member_database_type_id::
+ traverse_date_time (member_info& mi)
+ {
+ type_id_ = string ("mysql::") +
+ date_time_database_id[mi.st->type - sql_type::DATE];
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info& mi)
+ {
+ type_id_ = string ("mysql::") +
+ char_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type_id::
+ traverse_bit (member_info&)
+ {
+ type_id_ = "mysql::id_bit";
+ }
+
+ void member_database_type_id::
+ traverse_enum (member_info&)
+ {
+ type_id_ = "mysql::id_enum";
+ }
+
+ void member_database_type_id::
+ traverse_set (member_info&)
+ {
+ type_id_ = "mysql::id_set";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/mysql/common.hxx b/odb/odb/relational/mysql/common.hxx
new file mode 100644
index 0000000..b43dc0d
--- /dev/null
+++ b/odb/odb/relational/mysql/common.hxx
@@ -0,0 +1,171 @@
+// file : odb/relational/mysql/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MYSQL_COMMON_HXX
+#define ODB_RELATIONAL_MYSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the mysql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ traverse_string (mi);
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ traverse_string (mi);
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_enum (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_set (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_enum (member_info&);
+
+ virtual void
+ traverse_set (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_enum (member_info&);
+
+ virtual void
+ traverse_set (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_MYSQL_COMMON_HXX
diff --git a/odb/odb/relational/mysql/context.cxx b/odb/odb/relational/mysql/context.cxx
new file mode 100644
index 0000000..8b3d983
--- /dev/null
+++ b/odb/odb/relational/mysql/context.cxx
@@ -0,0 +1,868 @@
+// file : odb/relational/mysql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/mysql/context.hxx>
+#include <odb/relational/mysql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "TINYINT(1)", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "TINYINT", 0, false},
+ {"unsigned char", "TINYINT UNSIGNED", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT UNSIGNED", 0, false},
+
+ {"int", "INT", 0, false},
+ {"unsigned int", "INT UNSIGNED", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT UNSIGNED", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT UNSIGNED", 0, false},
+
+ {"float", "FLOAT", 0, false},
+ {"double", "DOUBLE", 0, false},
+
+ {"::std::string", "TEXT", "VARCHAR(128)", false},
+
+ {"::size_t", "BIGINT UNSIGNED", 0, false},
+ {"::std::size_t", "BIGINT UNSIGNED", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = true;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = false;
+ global_index = false;
+ global_fkey = true;
+ data_->bind_vector_ = "MYSQL_BIND*";
+ data_->truncated_vector_ = "my_bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the 64 limit.
+ //
+ if (i->size () > 64)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than "
+ << "the MySQL name limit of 64 characters and will "
+ << "be truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '`';
+ r.append (*i, 0, 64); // Max identifier length is 64.
+ r += '`';
+ }
+
+ return r;
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ bool view (context::view (c));
+
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || view || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("mysql-grow"))
+ r_ = c.get<bool> ("mysql-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ if (!view)
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("mysql-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_long_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_short_string (member_info&)
+ {
+ r_ = true; // @@ Short string optimization disabled.
+ }
+
+ virtual void
+ traverse_enum (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_set (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("mysql-grow"))
+ return c.get<bool> ("mysql-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ using semantics::enum_;
+ using semantics::enumerator;
+ using semantics::array;
+
+ string r;
+
+ // Enum mapping.
+ //
+ if (enum_* e = dynamic_cast<enum_*> (&t))
+ {
+ // We can only map to ENUM if the C++ enumeration is contiguous
+ // and starts with 0.
+ //
+ enum_::enumerates_iterator i (e->enumerates_begin ()),
+ end (e->enumerates_end ());
+
+ if (i != end)
+ {
+ r += "ENUM(";
+
+ for (unsigned long long j (0); i != end; ++i, ++j)
+ {
+ enumerator const& er (i->enumerator ());
+
+ if (er.value () != j)
+ break;
+
+ if (j != 0)
+ r += ", ";
+
+ r += quote_string (er.name ());
+ }
+
+ if (i == end)
+ r += ")";
+ else
+ r.clear ();
+ }
+
+ if (!r.empty ())
+ return r;
+ }
+
+ r = base_context::database_type_impl (t, hint, id, null);
+
+ if (!r.empty ())
+ return r;
+
+ // char[N] mapping.
+ //
+ else if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR(";
+ else
+ {
+ r = "VARCHAR(";
+ n--;
+ }
+
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // NATIONAL CHAR|VARCHAR
+ // CHAR BYTE (BINARY)
+ // CHARACTER VARYING (VARCHAR)
+ // LONG VARBINARY (MEDIUMBLOB)
+ // LONG VARCHAR (MEDIUMTEXT)
+ //
+ //
+ enum state
+ {
+ parse_prefix,
+ parse_name,
+ parse_range,
+ parse_sign,
+ parse_done
+ };
+
+ state s (parse_prefix);
+ string prefix;
+ bool flt (false);
+
+ for (sql_token t (l.next ());
+ s != parse_done && t.type () != sql_token::t_eos;
+ t = l.next ())
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_prefix:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ if (id == "NATIONAL" ||
+ id == "CHAR" ||
+ id == "CHARACTER" ||
+ id == "LONG")
+ {
+ prefix = id;
+ s = parse_name;
+ continue;
+ }
+ }
+
+ s = parse_name;
+ }
+ // Fall through.
+ case parse_name:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ bool match (true);
+ string const& id (context::upcase (t.identifier ()));
+
+ // Numeric types.
+ //
+ if (id == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (id == "TINYINT" || id == "INT1")
+ {
+ r.type = sql_type::TINYINT;
+ }
+ else if (id == "BOOL" || id == "BOOLEAN")
+ {
+ r.type = sql_type::TINYINT;
+ r.range = true;
+ r.range_value = 1;
+ }
+ else if (id == "SMALLINT" || id == "INT2")
+ {
+ r.type = sql_type::SMALLINT;
+ }
+ else if (id == "MEDIUMINT" ||
+ id == "INT3" ||
+ id == "MIDDLEINT")
+ {
+ r.type = sql_type::MEDIUMINT;
+ }
+ else if (id == "INT" || id == "INTEGER" || id == "INT4")
+ {
+ r.type = sql_type::INT;
+ }
+ else if (id == "BIGINT" || id == "INT8")
+ {
+ r.type = sql_type::BIGINT;
+ }
+ else if (id == "SERIAL")
+ {
+ r.type = sql_type::BIGINT;
+ r.unsign = true;
+ }
+ else if (id == "FLOAT")
+ {
+ // Assign a type only once we know the precision of the
+ // float; it can be either 4 or 8 byte.
+ //
+ flt = true;
+ }
+ else if (id == "FLOAT4")
+ {
+ r.type = sql_type::FLOAT;
+ }
+ else if (id == "DOUBLE" || id == "FLOAT8")
+ {
+ r.type = sql_type::DOUBLE;
+ }
+ else if (id == "DECIMAL" ||
+ id == "DEC" ||
+ id == "NUMERIC" ||
+ id == "FIXED")
+ {
+ r.type = sql_type::DECIMAL;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE")
+ {
+ r.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r.type = sql_type::TIME;
+ }
+ else if (id == "DATETIME")
+ {
+ r.type = sql_type::DATETIME;
+ }
+ else if (id == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ }
+ else if (id == "YEAR")
+ {
+ r.type = sql_type::YEAR;
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "NCHAR")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (id == "VARCHAR")
+ {
+ r.type = prefix == "LONG"
+ ? sql_type::MEDIUMTEXT
+ : sql_type::VARCHAR;
+ }
+ else if (id == "NVARCHAR")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "VARYING" && prefix == "CHARACTER")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "BINARY")
+ {
+ r.type = sql_type::BINARY;
+ }
+ else if (id == "BYTE" && prefix == "CHAR")
+ {
+ r.type = sql_type::BINARY;
+ }
+ else if (id == "VARBINARY")
+ {
+ r.type = prefix == "LONG"
+ ? sql_type::MEDIUMBLOB
+ : sql_type::VARBINARY;
+ }
+ else if (id == "TINYBLOB")
+ {
+ r.type = sql_type::TINYBLOB;
+ }
+ else if (id == "TINYTEXT")
+ {
+ r.type = sql_type::TINYTEXT;
+ }
+ else if (id == "BLOB")
+ {
+ r.type = sql_type::BLOB;
+ }
+ else if (id == "TEXT")
+ {
+ r.type = sql_type::TEXT;
+ }
+ else if (id == "MEDIUMBLOB")
+ {
+ r.type = sql_type::MEDIUMBLOB;
+ }
+ else if (id == "MEDIUMTEXT")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ else if (id == "LONGBLOB")
+ {
+ r.type = sql_type::LONGBLOB;
+ }
+ else if (id == "LONGTEXT")
+ {
+ r.type = sql_type::LONGTEXT;
+ }
+ else if (id == "ENUM")
+ {
+ r.type = sql_type::ENUM;
+ }
+ else if (id == "SET")
+ {
+ r.type = sql_type::SET;
+ }
+ else
+ match = false;
+
+ if (match)
+ {
+ s = parse_range;
+ continue;
+ }
+ }
+
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (!prefix.empty ())
+ {
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (prefix == "LONG")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ return error (ct, "unknown MySQL type '" + t.identifier () +
+ "'");
+ }
+ else
+ return error (ct, "expected MySQL type name");
+ }
+
+ s = parse_range;
+ }
+ // Fall through.
+ case parse_range:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ // ENUM and SET have a list of members instead of the range.
+ //
+ if (r.type == sql_type::ENUM || r.type == sql_type::SET)
+ {
+ while (true)
+ {
+ if (t.type () != sql_token::t_string_lit)
+ {
+ return error (ct, "string literal expected in MySQL "
+ "ENUM or SET declaration");
+ }
+
+ if (r.type == sql_type::ENUM)
+ r.enumerators.push_back (t.literal ());
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_rparen)
+ break;
+ else if (t.punctuation () != sql_token::p_comma)
+ {
+ return error (ct, "comma expected in MySQL ENUM or "
+ "SET declaration");
+ }
+
+ t = l.next ();
+ }
+ }
+ else
+ {
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer range expected in MySQL type "
+ "declaration");
+ }
+
+ unsigned int v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid range value '" + t.literal () +
+ "' in MySQL type declaration");
+ }
+
+ r.range = true;
+ r.range_value = v;
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // We have the second range value. Skip it.
+ //
+ // In FLOAT the two-value range means something
+ // completely different than the single-value.
+ // Pretend we don't have the range in the former
+ // case.
+ //
+ if (flt)
+ r.range = false;
+
+ l.next ();
+ t = l.next ();
+ }
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in MySQL type declaration");
+ }
+
+ s = parse_sign;
+ continue;
+ }
+
+ s = parse_sign;
+ }
+ // Fall through.
+ case parse_sign:
+ {
+ if (tt == sql_token::t_identifier &&
+ context::upcase (t.identifier ()) == "UNSIGNED")
+ {
+ r.unsign = true;
+ }
+
+ s = parse_done;
+ break;
+ }
+ case parse_done:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ if (s == parse_name && !prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (prefix == "LONG")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ }
+
+ if (flt)
+ {
+ r.type = !r.range || r.range_value < 24
+ ? sql_type::FLOAT
+ : sql_type::DOUBLE;
+ }
+
+ if (r.type == sql_type::invalid)
+ return error (ct, "incomplete MySQL type declaration");
+
+ // If range is omitted for CHAR or BIT types, it defaults to 1.
+ //
+ if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
+ {
+ r.range = true;
+ r.range_value = 1;
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid MySQL type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/context.hxx b/odb/odb/relational/mysql/context.hxx
new file mode 100644
index 0000000..98574f2
--- /dev/null
+++ b/odb/odb/relational/mysql/context.hxx
@@ -0,0 +1,194 @@
+// file : odb/relational/mysql/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MYSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_MYSQL_CONTEXT_HXX
+
+#include <map>
+#include <vector>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ TINYINT,
+ SMALLINT,
+ MEDIUMINT,
+ INT,
+ BIGINT,
+
+ // Float types.
+ //
+ FLOAT,
+ DOUBLE,
+ DECIMAL,
+
+ // Data-time types.
+ //
+ DATE,
+ TIME,
+ DATETIME,
+ TIMESTAMP,
+ YEAR,
+
+ // String and binary types.
+ //
+ CHAR,
+ BINARY,
+ VARCHAR,
+ VARBINARY,
+ TINYTEXT,
+ TINYBLOB,
+ TEXT,
+ BLOB,
+ MEDIUMTEXT,
+ MEDIUMBLOB,
+ LONGTEXT,
+ LONGBLOB,
+
+ // Other types.
+ //
+ BIT,
+ ENUM,
+ SET,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () : type (invalid), unsign (false), range (false) {}
+
+ core_type type;
+ bool unsign;
+ bool range;
+ unsigned int range_value; // MySQL max value is 2^32 - 1 (LONGBLOG/TEXT).
+ std::vector<std::string> enumerators; // Enumerator strings for ENUM.
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string, custom_db_types const* = 0);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ protected:
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MYSQL_CONTEXT_HXX
diff --git a/odb/odb/relational/mysql/header.cxx b/odb/odb/relational/mysql/header.cxx
new file mode 100644
index 0000000..27bae48
--- /dev/null
+++ b/odb/odb/relational/mysql/header.cxx
@@ -0,0 +1,136 @@
+// file : odb/relational/mysql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // Exchanged as strings. Can have up to 65 digits not counting
+ // '-' and '.'. If range is not specified, the default is 10.
+ //
+
+ /*
+ @@ Disabled.
+ os << "char " << mi.var << "value[" <<
+ (t.range ? t.range_value : 10) + 3 << "];"
+ */
+
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // If range is not specified, the default buffer size is 255.
+ //
+ /*
+ @@ Disabled.
+ os << "char " << mi.var << "value[" <<
+ (t.range ? t.range_value : 255) + 1 << "];"
+ */
+
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Valid range is 1 to 64.
+ //
+ unsigned int n (mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
+
+ os << "unsigned char " << mi.var << "value[" << n << "];"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string. Since we don't know
+ // at the code generation time which one it is, we have to always
+ // keep size in case it is a string.
+ //
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as string.
+ //
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/inline.cxx b/odb/odb/relational/mysql/inline.cxx
new file mode 100644
index 0000000..bfa2c94
--- /dev/null
+++ b/odb/odb/relational/mysql/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/mysql/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "null;";
+ else
+ os << "i." << mi.var << "null = 1;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/model.cxx b/odb/odb/relational/mysql/model.cxx
new file mode 100644
index 0000000..17ed4c0
--- /dev/null
+++ b/odb/odb/relational/mysql/model.cxx
@@ -0,0 +1,161 @@
+// file : odb/relational/mysql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still
+ // use them for self-documentation.
+ //
+ return v ? "TRUE" : "FALSE";
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const& name)
+ {
+ // Make sure the column is mapped to an ENUM or integer type.
+ //
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+
+ switch (t.type)
+ {
+ case sql_type::ENUM:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::MEDIUMINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to MySQL ENUM or integer type"
+ << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enum_;
+ using semantics::enumerator;
+
+ enumerator& er (dynamic_cast<enumerator&> (*unit.find (en)));
+ enum_& e (er.enum_ ());
+
+ if (t.type == sql_type::ENUM)
+ {
+ // Assuming the enumerators in the C++ enum and MySQL ENUM are
+ // in the same order, calculate the poistion of the C++
+ // enumerator and use that as an index in the MySQL ENUM.
+ //
+ size_t pos (0);
+
+ for (enum_::enumerates_iterator i (e.enumerates_begin ()),
+ end (e.enumerates_end ()); i != end; ++i)
+ {
+ if (&i->enumerator () == &er)
+ break;
+
+ pos++;
+ }
+
+ if (pos < t.enumerators.size ())
+ return t.enumerators[pos];
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: unable to map C++ enumerator '" << name
+ << "' to MySQL ENUM value" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ ostringstream ostr;
+
+ if (e.unsigned_ ())
+ ostr << er.value ();
+ else
+ ostr << static_cast<long long> (er.value ());
+
+ return ostr.str ();
+ }
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ struct member_create: relational::member_create, context
+ {
+ member_create (base const& x): base (x) {}
+
+ virtual string
+ table_options (semantics::data_member& m, semantics::type& c)
+ {
+ string r (relational::member_create::table_options (m, c));
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ {
+ // Note: MySQL table options can be separated with spaces.
+ //
+ if (!r.empty ())
+ r += ' ';
+
+ r += "ENGINE=";
+ r += engine;
+ }
+
+ return r;
+ }
+ };
+ entry<member_create> member_create_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual string
+ table_options (type& c)
+ {
+ string r (relational::class_::table_options (c));
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ {
+ // Note: MySQL table options can be separated with spaces.
+ //
+ if (!r.empty ())
+ r += ' ';
+
+ r += "ENGINE=";
+ r += engine;
+ }
+
+ return r;
+ }
+ };
+ entry<class_> class__;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/schema.cxx b/odb/odb/relational/mysql/schema.cxx
new file mode 100644
index 0000000..60dc95b
--- /dev/null
+++ b/odb/odb/relational/mysql/schema.cxx
@@ -0,0 +1,489 @@
+// file : odb/relational/mysql/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ //
+ // Drop.
+ //
+
+ struct drop_foreign_key: relational::drop_foreign_key, context
+ {
+ drop_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ /*
+ // @@ This does not work: in MySQL control statements can only
+ // be used in stored procedures. It seems the only way to
+ // implement this is to define, execute, and drop a stored
+ // procedure, which is just too ugly.
+ //
+ // Another option would be to use CREATE TABLE IF NOT EXISTS
+ // to create a dummy table with a dummy constraint that makes
+ // the following DROP succeed. Note, however, that MySQL issues
+ // a notice if the table already exist so would need to suppress
+ // that as well. Still not sure that the utility of this support
+ // justifies this kind of a hack.
+ //
+ os << "IF EXISTS (SELECT NULL FROM information_schema.TABLE_CONSTRAINTS" << endl
+ << " WHERE CONSTRAINT_TYPE = " << quote_string ("FOREIGN KEY") << "AND" << endl
+ << " CONSTRAINT_SCHEMA = DATABASE() AND" << endl
+ << " CONSTRAINT_NAME = " << quote_string (fk.name ()) << ") THEN" << endl
+ << " ALTER TABLE " << quote_id (t.name ()) << " DROP FOREIGN KEY " << quote_id (fk.name ()) << ";" << endl
+ << "END IF;" << endl;
+ */
+
+ // So for now we only do this in migration.
+ //
+ if (dropped_ == 0)
+ {
+ if (fk.not_deferrable ())
+ pre_statement ();
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ }
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " DROP FOREIGN KEY " << quote_id (fk.name ()) << endl;
+
+ if (fk.not_deferrable ())
+ post_statement ();
+ else
+ os << "*/" << endl
+ << endl;
+ }
+ }
+
+ using base::drop;
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ // Find the foreign key we are dropping in the base model.
+ //
+ sema_rel::foreign_key& fk (find<sema_rel::foreign_key> (dfk));
+
+ if (fk.not_deferrable () || in_comment)
+ base::traverse (dfk);
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*"
+ << endl;
+
+ drop (dfk);
+
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ drop_header ()
+ {
+ os << "DROP FOREIGN KEY ";
+ }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::index& in)
+ {
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+
+ os << "DROP INDEX " << name (in) << " ON " <<
+ quote_id (t.name ()) << endl;
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ auto_ (sema_rel::primary_key&)
+ {
+ os << " AUTO_INCREMENT";
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ void
+ diagnose (sema_rel::foreign_key& fk)
+ {
+ if (fk.on_delete () != sema_rel::foreign_key::no_action)
+ {
+ cerr << "warning: foreign key '" << fk.name () << "' has " <<
+ "ON DELETE clause but is disabled in MySQL due to lack "
+ "of deferrable constraint support" << endl;
+
+ cerr << "info: consider using non-deferrable foreign keys (" <<
+ "--fkeys-deferrable-mode)" << endl;
+ }
+ }
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ // MySQL does not support deferrable constraint checking. Output
+ // such foreign keys as comments, for documentation, unless we
+ // are generating embedded schema.
+ //
+ if (fk.not_deferrable ())
+ base::traverse_create (fk);
+ else
+ {
+ diagnose (fk);
+
+ // Don't bloat C++ code with comment strings if we are
+ // generating embedded schema.
+ //
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*" << endl
+ << " CONSTRAINT ";
+ create (fk);
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ if (fk.not_deferrable () || in_comment)
+ base::traverse_add (fk);
+ else
+ {
+ diagnose (fk);
+
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*"
+ << endl;
+
+ add (fk);
+
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ deferrable (sema_rel::deferrable)
+ {
+ // This will still be called to output the comment.
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ os << in.type () << ' ';
+
+ os << "INDEX " << name (in);
+
+ if (!in.method ().empty ())
+ os << " USING " << in.method ();
+
+ os << endl
+ << " ON " << table_name (in) << " (";
+
+ columns (in);
+
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+ };
+ entry<create_index> create_index_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ // See if there are any undefined foreign keys that are not
+ // deferrable.
+ //
+ bool
+ check_undefined_fk_deferrable_only (sema_rel::table& t)
+ {
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (&i->nameable ()))
+ {
+ if (!fk->count ("mysql-fk-defined") &&
+ fk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ if (pass_ == 1)
+ base::traverse (t);
+ else
+ {
+ // Add undefined foreign keys.
+ //
+ if (check_undefined_fk (t))
+ {
+ bool deferrable (check_undefined_fk_deferrable_only (t));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ());
+
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (t, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+ }
+ }
+ };
+ entry<create_table> create_table_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ alter_header ()
+ {
+ os << "MODIFY COLUMN ";
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ // Check if we are only dropping deferrable foreign keys.
+ //
+ bool
+ check_drop_deferrable_only (sema_rel::alter_table& at)
+ {
+ if (check<sema_rel::add_column> (at) ||
+ check_alter_column_null (at, true))
+ return false;
+
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ if (drop_foreign_key* dfk =
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()))
+ {
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ if (fk.not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ if (check_drop_deferrable_only (at))
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ in_comment = true;
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+ instance<drop_foreign_key> dfk (*this);
+ trav_rel::unames n (*dfk);
+ names (at, n);
+ os << endl;
+
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ base::alter (at);
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ // Check if we are only adding deferrable foreign keys.
+ //
+ bool
+ check_add_deferrable_only (sema_rel::alter_table& at)
+ {
+ if (check<sema_rel::drop_column> (at) ||
+ check_alter_column_null (at, false))
+ return false;
+
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_foreign_key;
+
+ if (add_foreign_key* afk =
+ dynamic_cast<add_foreign_key*> (&i->nameable ()))
+ {
+ if (afk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ if (check_add_deferrable_only (at))
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ in_comment = true;
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (at, n);
+ os << endl;
+
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ base::alter (at);
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl
+ << " " << qn_ << " VARCHAR(128) NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT UNSIGNED NOT NULL," << endl
+ << " " << qm_ << " TINYINT(1) NOT NULL)" << endl;
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ os << " ENGINE=" << engine << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "INSERT IGNORE INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/source.cxx b/odb/odb/relational/mysql/source.cxx
new file mode 100644
index 0000000..9131ea7
--- /dev/null
+++ b/odb/odb/relational/mysql/source.cxx
@@ -0,0 +1,724 @@
+// file : odb/relational/mysql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ namespace
+ {
+ const char* integer_buffer_types[] =
+ {
+ "MYSQL_TYPE_TINY",
+ "MYSQL_TYPE_SHORT",
+ "MYSQL_TYPE_LONG", // *_bind_param() doesn't support INT24.
+ "MYSQL_TYPE_LONG",
+ "MYSQL_TYPE_LONGLONG"
+ };
+
+ const char* float_buffer_types[] =
+ {
+ "MYSQL_TYPE_FLOAT",
+ "MYSQL_TYPE_DOUBLE"
+ };
+
+ const char* date_time_buffer_types[] =
+ {
+ "MYSQL_TYPE_DATE",
+ "MYSQL_TYPE_TIME",
+ "MYSQL_TYPE_DATETIME",
+ "MYSQL_TYPE_TIMESTAMP",
+ "MYSQL_TYPE_SHORT"
+ };
+
+ const char* char_bin_buffer_types[] =
+ {
+ "MYSQL_TYPE_STRING", // CHAR
+ "MYSQL_TYPE_BLOB", // BINARY,
+ "MYSQL_TYPE_STRING", // VARCHAR
+ "MYSQL_TYPE_BLOB", // VARBINARY
+ "MYSQL_TYPE_STRING", // TINYTEXT
+ "MYSQL_TYPE_BLOB", // TINYBLOB
+ "MYSQL_TYPE_STRING", // TEXT
+ "MYSQL_TYPE_BLOB", // BLOB
+ "MYSQL_TYPE_STRING", // MEDIUMTEXT
+ "MYSQL_TYPE_BLOB", // MEDIUMBLOB
+ "MYSQL_TYPE_STRING", // LONGTEXT
+ "MYSQL_TYPE_BLOB" // LONGBLOB
+ };
+ }
+
+ //
+ //
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ // When we store a ENUM column in the MySQL database, if we bind
+ // an integer parameter, then it is treated as an index and if we
+ // bind a string, then it is treated as a enumerator. Everything
+ // would have worked well if the same logic applied to the select
+ // operation. That is, if we bind integer, then the database sends
+ // the index and if we bind string then the database sends the
+ // enumerator. Unfortunately, MySQL always sends the enumerator
+ // and to get the index one has to resort to the enum+0 hack.
+ //
+ // This causes the following problem: at code generation time we
+ // do not yet know which format we want. This is determined at
+ // C++ compile time by traits (the reason we don't know this is
+ // because we don't want to drag database-specific runtimes,
+ // which define the necessary traits, as well as their
+ // prerequisites into the ODB compilation process). As a result,
+ // we cannot decide at code generation time whether we need the
+ // +0 hack or not. One way to overcome this would be to construct
+ // the SELECT statements at runtime, something along these lines:
+ //
+ // "enum" + enum_traits<type>::hack + ","
+ //
+ // However, this complicates the code generator quite a bit: we
+ // either have to move to std::string storage for all the
+ // statements and all the databases, which is kind of a waste,
+ // or do some deep per-database customizations, which is hairy.
+ // So, instead, we are going to use another hack (hey, what the
+ // hell, right?) by loading both the index and enumerator
+ // combined into a string:
+ //
+ // CONCAT (enum+0, ' ', enum)
+ //
+ // For cases where we need the index, everything works since
+ // MySQL will convert the leading number and stop at the space.
+ // For cases where we need the enumerator, we do a bit of pre-
+ // processing (see enum_traits) before handing the value off
+ // to value_traits.
+ //
+
+ string const& type (column_type ());
+
+ if (sk_ != statement_select ||
+ parse_sql_type (type, m).type != sql_type::ENUM)
+ {
+ return base::column (m, table, column);
+ }
+
+ // Qualified column and conversion expression.
+ //
+ string qc;
+ if (!table.empty ())
+ {
+ qc += table;
+ qc += '.';
+ }
+ qc += column;
+ qc = convert_from (qc, type, m);
+
+ string r ("CONCAT(" + qc + "+0,' '," + qc + ")");
+
+ sc_.push_back (
+ relational::statement_column (table, r, type, m, key_prefix_));
+ return true;
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ struct view_columns: relational::view_columns, context
+ {
+ view_columns (base const& x): base (x) {}
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ // The same idea as in object_columns.
+ //
+ string const& type (column_type ());
+
+ if (parse_sql_type (type, m).type != sql_type::ENUM)
+ {
+ return base::column (m, table, column);
+ }
+
+ // Column and conversion expression.
+ //
+ string c (convert_from (column, type, m));
+
+ string r ("CONCAT(" + c + "+0,' '," + c + ")");
+ sc_.push_back (relational::statement_column (table, r, type, m));
+ return true;
+ }
+ };
+ entry<view_columns> view_columns_;
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ // While the is_unsigned should indicate whether the
+ // buffer variable is unsigned, rather than whether the
+ // database type is unsigned, in case of the image types,
+ // this is the same.
+ //
+ os << b << ".buffer_type = " <<
+ integer_buffer_types[mi.st->type - sql_type::TINYINT] << ";"
+ << b << ".is_unsigned = " << (mi.st->unsign ? "1" : "0") << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ float_buffer_types[mi.st->type - sql_type::FLOAT] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << b << ".buffer_type = MYSQL_TYPE_NEWDECIMAL;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;";
+
+ if (mi.st->type == sql_type::YEAR)
+ os << b << ".is_unsigned = 0;";
+
+ os << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // MySQL documentation is quite confusing about the use of
+ // buffer_length and length when it comes to input parameters.
+ // Source code, however, tells us that it uses buffer_length
+ // only if length is NULL.
+ //
+ os << b << ".buffer_type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Treated as a BLOB.
+ //
+ os << b << ".buffer_type = MYSQL_TYPE_BLOB;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "sizeof (" << arg << "." << mi.var << "value));"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ os << "mysql::enum_traits::bind (" << b << "," << endl
+ << arg << "." << mi.var << "value," << endl
+ << arg << "." << mi.var << "size," << endl
+ << "&" << arg << "." << mi.var << "null);";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << b << ".buffer_type = MYSQL_TYPE_STRING;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // @@ Optimization disabled.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // @@ Optimization disabled.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string (and we don't know
+ // at the code generation time which one it is).
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "if (mysql::enum_traits::grow (" <<
+ "i." << mi.var << "value, " <<
+ "i." << mi.var << "size))" << endl
+ << "grew = true;" // String
+ << "else" << endl
+ << e << " = 0;" // Integer.
+ << "}";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = 1;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Represented as a BLOB.
+ //
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);";
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ os << "if (mysql::enum_traits::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << "))" << endl
+ << "grew = true;"
+ << endl
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Represented as a BLOB.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ os << "mysql::enum_traits::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_auto_id (semantics::data_member& m, string const& im)
+ {
+ // Don't set the id value to 0 if this is a nullable wrapper. This
+ // will allow the user to use the NO_AUTO_VALUE_ON_ZERO mode by
+ // making it NULL when they want the auto semantics:
+ //
+ // #pragma db auto
+ // odb::nullable<int64_t> id;
+ //
+ semantics::type& t (utype (m));
+ if (wrapper (t) && t.template get<bool> ("wrapper-null-handler"))
+ return;
+
+ os << im << "value = 0;"
+ << endl;
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ if (vo.join == view_object::full)
+ {
+ error (vo.loc)
+ << "FULL OUTER JOIN is not supported by MySQL" << endl;
+ throw operation_failed ();
+ }
+
+ return base::join_syntax (vo);
+ }
+ };
+ entry<class_> class_entry_;
+
+ struct include: relational::include, context
+ {
+ include (base const& x): base (x) {}
+
+ virtual void
+ extra_post ()
+ {
+ os << "#include <odb/mysql/enum.hxx>" << endl;
+ }
+ };
+ entry<include> include_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/common.cxx b/odb/odb/relational/oracle/common.cxx
new file mode 100644
index 0000000..7caafc9
--- /dev/null
+++ b/odb/odb/relational/oracle/common.cxx
@@ -0,0 +1,522 @@
+// file : odb/relational/oracle/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/oracle/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Numeric types.
+ //
+ case sql_type::NUMBER:
+ {
+ const sql_type& st (*mi.st);
+
+ if (st.prec)
+ {
+ unsigned short r (st.prec_value);
+
+ if (!st.scale)
+ {
+ if (r <= 10)
+ traverse_int32 (mi);
+ // Only OCI versions 11.2 and later support insertion and
+ // extraction into a 64 bit integer.
+ //
+ else if (
+ (options.oracle_client_version () >= oracle_version (11, 2)) &&
+ (r <= 19 || (r == 20 && unsigned_integer (mi.t))))
+ traverse_int64 (mi);
+ else
+ traverse_big_int (mi);
+ }
+ else
+ {
+ // We can calculate the decimal exponent of the normalised
+ // floating point equivalent of the fixed point number using
+ // e = p - s, where p is the precision, s is the scale, and
+ // e the exponent. We can then use this to determine whether
+ // or not a value of Oracle SQL type NUMBER can be completely
+ // stored in the native floating point type.
+ //
+
+ // The maximum decimal precision of a float is 7 significant
+ // digits. The minimum and maximum decimal exponents
+ // representable by a float are -37 and 38 respectively.
+ //
+ if (r <= 7)
+ {
+ int e = r - st.scale_value;
+
+ if (e >= -37 && e <= 38)
+ traverse_float (mi);
+ else
+ traverse_double (mi);
+ }
+
+ // The maximum decimal precision of a double is 15 significant
+ // digits. The minimum and maximum decimal exponent representable
+ // by a double exceeds that of the Oracle NUMBER type.
+ //
+ else if (r <= 15)
+ traverse_double (mi);
+ else
+ traverse_big_float (mi);
+ }
+ }
+ else
+ // If there is no precision, then this is a floating-point number.
+ //
+ traverse_double (mi);
+
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ // We map FLOAT types based exclusively on their binary precision
+ // seeing that in 99% of cases it is the precision that is the
+ // limiting factor and not the exponent.
+ //
+ if (mi.st->prec_value <= 24)
+ traverse_float (mi);
+ else if (mi.st->prec_value <= 53)
+ traverse_double (mi);
+ else
+ traverse_big_float (mi);
+
+ break;
+ }
+ case sql_type::BINARY_FLOAT:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::BINARY_DOUBLE:
+ {
+ traverse_double (mi);
+ break;
+ }
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ {
+ traverse_date (mi);
+ break;
+ }
+ case sql_type::TIMESTAMP:
+ {
+ traverse_timestamp (mi);
+ break;
+ }
+ case sql_type::INTERVAL_YM:
+ {
+ traverse_interval_ym (mi);
+ break;
+ }
+ case sql_type::INTERVAL_DS:
+ {
+ traverse_interval_ds (mi);
+ break;
+ }
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::NCHAR:
+ case sql_type::VARCHAR2:
+ case sql_type::NVARCHAR2:
+ case sql_type::RAW:
+ {
+ traverse_string (mi);
+ break;
+ }
+ case sql_type::BLOB:
+ case sql_type::CLOB:
+ case sql_type::NCLOB:
+ {
+ traverse_lob (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_oracle >::image_type";
+ }
+
+ void member_image_type::
+ traverse_int32 (member_info& mi)
+ {
+ if (unsigned_integer (mi.t))
+ type_ = "unsigned int";
+ else
+ type_ = "int";
+ }
+
+ void member_image_type::
+ traverse_int64 (member_info& mi)
+ {
+ if (unsigned_integer (mi.t))
+ type_ = "unsigned long long";
+ else
+ type_ = "long long";
+ }
+
+ void member_image_type::
+ traverse_big_int (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_float (member_info&)
+ {
+ type_ = "float";
+ }
+
+ void member_image_type::
+ traverse_double (member_info&)
+ {
+ type_ = "double";
+ }
+
+ void member_image_type::
+ traverse_big_float (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_date (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_timestamp (member_info&)
+ {
+ type_ = "oracle::datetime";
+ }
+
+ void member_image_type::
+ traverse_interval_ym (member_info&)
+ {
+ type_ = "oracle::interval_ym";
+ }
+
+ void member_image_type::
+ traverse_interval_ds (member_info&)
+ {
+ type_ = "oracle::interval_ds";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_lob (member_info&)
+ {
+ type_ = "oracle::lob_callback";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ namespace
+ {
+ const char* string_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_nstring", // NCHAR
+ "id_string", // VARCHAR2
+ "id_nstring", // NVARCHAR2
+ "id_raw" // RAW
+ };
+
+ const char* lob_database_id[] =
+ {
+ "id_blob",
+ "id_clob",
+ "id_nclob"
+ };
+ }
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_int32 (member_info&)
+ {
+ type_id_ = "oracle::id_int32";
+ }
+
+ void member_database_type_id::
+ traverse_int64 (member_info&)
+ {
+ type_id_ = "oracle::id_int64";
+ }
+
+ void member_database_type_id::
+ traverse_big_int (member_info&)
+ {
+ type_id_ = "oracle::id_big_int";
+ }
+
+ void member_database_type_id::
+ traverse_float (member_info&)
+ {
+ type_id_ = "oracle::id_float";
+ }
+
+ void member_database_type_id::
+ traverse_double (member_info&)
+ {
+ type_id_ = "oracle::id_double";
+ }
+
+ void member_database_type_id::
+ traverse_big_float (member_info&)
+ {
+ type_id_ = "oracle::id_big_float";
+ }
+
+ void member_database_type_id::
+ traverse_date (member_info&)
+ {
+ type_id_ = "oracle::id_date";
+ }
+
+ void member_database_type_id::
+ traverse_timestamp (member_info&)
+ {
+ type_id_ = "oracle::id_timestamp";
+ }
+
+ void member_database_type_id::
+ traverse_interval_ym (member_info&)
+ {
+ type_id_ = "oracle::id_interval_ym";
+ }
+
+ void member_database_type_id::
+ traverse_interval_ds (member_info&)
+ {
+ type_id_ = "oracle::id_interval_ds";
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info& mi)
+ {
+ type_id_ = string ("oracle::") +
+ string_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type_id::
+ traverse_lob (member_info& mi)
+ {
+ type_id_ = string ("oracle::") +
+ lob_database_id[mi.st->type - sql_type::BLOB];
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ void
+ column_ctor (string const& type, string const& name, string const& base)
+ {
+ os << name << " (";
+
+ if (multi_dynamic)
+ os << "odb::query_column< " << type << " >& qc," << endl;
+
+ os << "const char* t," << endl
+ << "const char* c," << endl
+ << "const char* conv," << endl
+ << "unsigned short p = 0xFFF," << endl
+ << "short s = 0xFFF)" << endl
+ << " : " << base << " (" << (multi_dynamic ? "qc, " : "") <<
+ "t, c, conv, p, s)"
+ << "{"
+ << "}";
+ }
+
+ virtual void
+ column_ctor_args_extra (semantics::data_member& m)
+ {
+ // For some types we need to pass precision and scale.
+ //
+ sql_type const& st (parse_sql_type (column_type (), m));
+
+ switch (st.type)
+ {
+ case sql_type::NUMBER:
+ {
+ if (st.prec)
+ {
+ os << ", " << st.prec_value;
+
+ if (st.scale)
+ os << ", " << st.scale_value;
+ }
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ os << ", " << st.prec_value;
+ break;
+ }
+ case sql_type::TIMESTAMP:
+ {
+ os << ", " << st.prec_value;
+ break;
+ }
+ case sql_type::INTERVAL_YM:
+ {
+ os << ", " << st.prec_value;
+ break;
+ }
+ case sql_type::INTERVAL_DS:
+ {
+ // INTERVAL DAY TO SECOND has two precisions.
+ //
+ os << ", " << st.prec_value << ", " << st.scale_value;
+ break;
+ }
+ case sql_type::CHAR:
+ case sql_type::NCHAR:
+ case sql_type::VARCHAR2:
+ case sql_type::NVARCHAR2:
+ case sql_type::RAW:
+ {
+ // The same logic as in header.cxx.
+ //
+ size_t n (st.prec ? st.prec_value : 1);
+
+ if (!st.byte_semantics)
+ n *= 4;
+
+ if (st.type == sql_type::VARCHAR2 ||
+ st.type == sql_type::NVARCHAR2)
+ n = n > 4000 ? 4000 : n;
+ else
+ n = n > 2000 ? 2000 : n;
+
+ os << ", " << n;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/oracle/common.hxx b/odb/odb/relational/oracle/common.hxx
new file mode 100644
index 0000000..1958aab
--- /dev/null
+++ b/odb/odb/relational/oracle/common.hxx
@@ -0,0 +1,203 @@
+// file : odb/relational/oracle/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_ORACLE_COMMON_HXX
+#define ODB_RELATIONAL_ORACLE_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+namespace relational
+{
+ namespace oracle
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the oracle namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_int32 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_int64 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_big_int (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_double (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_big_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_timestamp (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_interval_ym (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_interval_ds (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_lob (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_int32 (member_info&);
+
+ virtual void
+ traverse_int64 (member_info&);
+
+ virtual void
+ traverse_big_int (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_double (member_info&);
+
+ virtual void
+ traverse_big_float (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_timestamp (member_info&);
+
+ virtual void
+ traverse_interval_ym (member_info&);
+
+ virtual void
+ traverse_interval_ds (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_lob (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_int32 (member_info&);
+
+ virtual void
+ traverse_int64 (member_info&);
+
+ virtual void
+ traverse_big_int (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_double (member_info&);
+
+ virtual void
+ traverse_big_float (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_timestamp (member_info&);
+
+ virtual void
+ traverse_interval_ym (member_info&);
+
+ virtual void
+ traverse_interval_ds (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_lob (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_ORACLE_COMMON_HXX
diff --git a/odb/odb/relational/oracle/context.cxx b/odb/odb/relational/oracle/context.cxx
new file mode 100644
index 0000000..12ce0aa
--- /dev/null
+++ b/odb/odb/relational/oracle/context.cxx
@@ -0,0 +1,795 @@
+// file : odb/relational/oracle/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "NUMBER(1)", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "NUMBER(3)", 0, false},
+ {"unsigned char", "NUMBER(3)", 0, false},
+
+ {"short int", "NUMBER(5)", 0, false},
+ {"short unsigned int", "NUMBER(5)", 0, false},
+
+ {"int", "NUMBER(10)", 0, false},
+ {"unsigned int", "NUMBER(10)", 0, false},
+
+ {"long int", "NUMBER(19)", 0, false},
+ {"long unsigned int", "NUMBER(20)", 0, false},
+
+ {"long long int", "NUMBER(19)", 0, false},
+ {"long long unsigned int", "NUMBER(20)", 0, false},
+
+ {"float", "BINARY_FLOAT", 0, false},
+ {"double", "BINARY_DOUBLE", 0, false},
+
+ // Oracle treats empty VARCHAR2 (and NVARCHAR2) strings as NULL.
+ //
+ {"::std::string", "VARCHAR2(512)", 0, true},
+
+ {"::size_t", "NUMBER(20)", 0, false},
+ {"::std::size_t", "NUMBER(20)", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = false;
+ need_alias_as = false;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = false;
+ need_image_clone = true;
+ generate_bulk = true;
+ global_index = true;
+ global_fkey = true;
+ data_->bind_vector_ = "oracle::bind*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r.append (*i, 0, 30); // Max identifier length is 30.
+ r += '"';
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR";
+ else
+ {
+ r = "VARCHAR2";
+ n--;
+ }
+
+ // Oracle VARCHAR2 limit is 4000 bytes. Since there are no good
+ // alternatives (CLOB?), let the user specify the mapping.
+ //
+ if (n > 4000)
+ return "";
+
+ // Allow empty VARCHAR2 values.
+ //
+ if (null != 0 && r == "VARCHAR2")
+ *null = true;
+
+ ostringstream ostr;
+ ostr << n;
+ r += '(';
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ bool context::
+ unsigned_integer (semantics::type& t)
+ {
+ semantics::type* wt (wrapper (t));
+ const string& s ((wt == 0 ? t : utype (*wt)).name ());
+
+ return s == "bool" ||
+ s == "unsigned char" ||
+ s == "short unsigned int" ||
+ s == "unsigned int" ||
+ s == "long unsigned int" ||
+ s == "long long unsigned int";
+ }
+
+ qname context::
+ sequence_name (qname const& table)
+ {
+ string n;
+
+ if (options.sequence_suffix ().count (db) != 0)
+ n = table.uname () + options.sequence_suffix ()[db];
+ else
+ n = compose_name (table.uname (), "seq");
+
+ n = transform_name (n, sql_name_sequence);
+
+ qname r (table.qualifier ());
+ r.append (n);
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // CHARACTER VARYING (VARCHAR2)
+ // CHAR VARYING (VARCHAR2)
+ // NATIONAL CHARACTER (NCHAR)
+ // NATIONAL CHAR (NCHAR)
+ // NCHAR VARYING (NVARCHAR2)
+ // NATIONAL CHARACTER VARYING (NVARCHAR2)
+ // NATIONAL CHAR VARYING (NVARCHAR2)
+ // NCHAR VARYING (NVARCHAR2)
+ // DOUBLE PRECISION (FLOAT(126))
+ // INTERVAL YEAR TO MONTH
+ // INTERVAL DAY TO SECOND
+ //
+ enum state
+ {
+ parse_identifier,
+ parse_prec,
+ parse_done
+ };
+
+ state s (parse_identifier);
+ string prefix;
+ sql_token t (l.next ());
+
+ while (t.type () != sql_token::t_eos)
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_identifier:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ //
+ // Numeric types.
+ //
+ if ((id == "NUMBER") && prefix.empty ())
+ {
+ // If NUMBER has no precision/scale, then it is a floating-
+ // point number. We indicate this by having no precision.
+ //
+ r.type = sql_type::NUMBER;
+ s = parse_prec;
+ }
+ else if ((id == "DEC" || id == "DECIMAL" || id == "NUMERIC")
+ && prefix.empty ())
+ {
+ // DEC, DECIMAL, and NUMERIC are equivalent to NUMBER in
+ // all ways except that they may not represent a floating
+ // point number. The scale defaults to zero.
+ //
+ r.type = sql_type::NUMBER;
+ s = parse_prec;
+ }
+ else if ((id == "INT" || id == "INTEGER" || id == "SMALLINT")
+ && prefix.empty ())
+ {
+ // INT, INTEGER, and SMALLINT map to NUMBER(38). They may not
+ // have precision or scale explicitly specified.
+ //
+ r.type = sql_type::NUMBER;
+ r.prec = true;
+ r.prec_value = 38;
+
+ s = parse_done;
+ }
+ //
+ // Floating point types.
+ //
+ else if (id == "FLOAT" && prefix.empty ())
+ {
+ r.type = sql_type::FLOAT;
+ r.prec = true;
+ r.prec_value = 126;
+
+ s = parse_prec;
+ }
+ else if (id == "DOUBLE" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "PRECISION" && prefix == "DOUBLE")
+ {
+ r.type = sql_type::FLOAT;
+ r.prec = true;
+ r.prec_value = 126;
+
+ s = parse_done;
+ }
+ else if (id == "REAL" && prefix.empty ())
+ {
+ r.type = sql_type::FLOAT;
+ r.prec = true;
+ r.prec_value = 63;
+
+ s = parse_done;
+ }
+ else if (id == "BINARY_FLOAT" && prefix.empty ())
+ {
+ r.type = sql_type::BINARY_FLOAT;
+ s = parse_done;
+ }
+ else if (id == "BINARY_DOUBLE" && prefix.empty ())
+ {
+ r.type = sql_type::BINARY_DOUBLE;
+ s = parse_done;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE" && prefix.empty ())
+ {
+ r.type = sql_type::DATE;
+ s = parse_done;
+ }
+ else if (id == "TIMESTAMP" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "INTERVAL" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "YEAR" && prefix == "INTERVAL")
+ {
+ prefix += " ";
+ prefix += id;
+
+ r.prec = true;
+ r.prec_value = 2;
+ s = parse_prec;
+ }
+ else if (id == "DAY" && prefix == "INTERVAL")
+ {
+ prefix += " ";
+ prefix += id;
+
+ r.prec = true;
+ r.prec_value = 2;
+ s = parse_prec;
+ }
+ else if (id == "TO" &&
+ (prefix == "INTERVAL YEAR" ||
+ prefix == "INTERVAL DAY"))
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "MONTH" && prefix == "INTERVAL YEAR TO")
+ {
+ r.type = sql_type::INTERVAL_YM;
+ s = parse_done;
+ }
+ else if (id == "SECOND" && prefix == "INTERVAL DAY TO")
+ {
+ r.type = sql_type::INTERVAL_DS;
+
+ // Store seconds precision in scale since prec holds
+ // the days precision.
+ //
+ r.scale = true;
+ r.scale_value = 6;
+ s = parse_prec;
+ }
+ //
+ // Timestamp with time zone (not supported).
+ //
+ else if (id == "WITH" && prefix == "TIMESTAMP")
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "TIME" &&
+ (prefix == "TIMESTAMP WITH" ||
+ prefix == "TIMESTAMP WITH LOCAL"))
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "LOCAL" && prefix == "TIMESTAMP WITH")
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "ZONE" &&
+ (prefix == "TIMESTAMP WITH LOCAL TIME" ||
+ prefix == "TIMESTAMP WITH TIME"))
+ {
+ return error (ct, "Oracle timestamps with time zones are "
+ "not currently supported");
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "CHAR")
+ {
+ prefix += prefix.empty () ? "" : " ";
+ prefix += id;
+ }
+ else if (id == "CHARACTER")
+ {
+ prefix += prefix.empty () ? "" : " ";
+ prefix += id;
+ }
+ else if (id == "NCHAR")
+ {
+ prefix += prefix.empty () ? "" : " ";
+ prefix += id;
+ }
+ else if (id == "VARCHAR" || id == "VARCHAR2")
+ {
+ // VARCHAR is currently mapped to VARCHAR2 in Oracle server.
+ // However, this may change in future versions.
+ //
+ r.type = sql_type::VARCHAR2;
+ r.byte_semantics = true;
+ s = parse_prec;
+ }
+ else if (id == "NVARCHAR2")
+ {
+ r.type = sql_type::NVARCHAR2;
+ r.byte_semantics = false;
+ s = parse_prec;
+ }
+ else if (id == "VARYING")
+ {
+ // VARYING always appears at the end of an identifier.
+ //
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::VARCHAR2;
+ r.byte_semantics = true;
+ }
+ else if (prefix == "NCHAR" ||
+ prefix == "NATIONAL CHAR" ||
+ prefix == "NATIONAL CHARACTER")
+ {
+ r.type = sql_type::NVARCHAR2;
+ r.byte_semantics = false;
+ }
+
+ s = parse_prec;
+ }
+ else if (id == "NATIONAL" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "RAW" && prefix.empty ())
+ {
+ r.type = sql_type::RAW;
+ s = parse_prec;
+ }
+ //
+ // LOB types.
+ //
+ else if (id == "BLOB" && prefix.empty ())
+ {
+ r.type = sql_type::BLOB;
+ s = parse_done;
+ }
+ else if (id == "CLOB" && prefix.empty ())
+ {
+ r.type = sql_type::CLOB;
+ s = parse_done;
+ }
+ else if (id == "NCLOB" && prefix.empty ())
+ {
+ r.type = sql_type::NCLOB;
+ s = parse_done;
+ }
+ //
+ // LONG types.
+ //
+ else if (id == "LONG")
+ return error (ct, "Oracle LONG types are not supported");
+ else
+ return error (ct, "unknown Oracle type '" +
+ t.identifier () + "'");
+
+ t = l.next ();
+ continue;
+ }
+ else if (!prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ r.byte_semantics = true;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "NCHAR" ||
+ prefix == "NATIONAL CHAR" ||
+ prefix == "NATIONAL CHARACTER")
+ {
+ r.type = sql_type::NCHAR;
+ r.byte_semantics = false;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ r.prec = true;
+ r.prec_value = 6;
+ }
+ else
+ return error (ct, "incomplete Oracle type declaration: '" +
+ prefix + "'");
+
+ // All of the possible types handled in this block can take
+ // an optional precision specifier. Set the state and fall
+ // through to the parse_prec handler.
+ //
+ s = parse_prec;
+ }
+ else
+ {
+ assert (r.type == sql_type::invalid);
+ return error (ct, "unexepected '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+ }
+ // Fall through.
+ case parse_prec:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer size/precision expected in "
+ "Oracle type declaration");
+ }
+
+ // Parse the precision.
+ //
+ {
+ unsigned short v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid prec value '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+
+ // Store seconds precision in scale since prec holds
+ // the days precision for INTERVAL DAY TO SECOND.
+ //
+ if (r.type == sql_type::INTERVAL_DS)
+ {
+ r.scale = true;
+ r.scale_value = static_cast<short> (v);
+ }
+ else
+ {
+ r.prec = true;
+ r.prec_value = v;
+ }
+
+ t = l.next ();
+ }
+
+ // Parse the scale if present.
+ //
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // Scale can only be specified for NUMBER.
+ //
+ if (r.type != sql_type::NUMBER)
+ {
+ return error (ct, "invalid scale in Oracle type "
+ "declaration");
+ }
+
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer scale expected in Oracle type "
+ "declaration");
+ }
+
+ short v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid scale value '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+
+ r.scale = true;
+ r.scale_value = v;
+
+ t = l.next ();
+ }
+ else if (t.type () == sql_token::t_identifier)
+ {
+ const string& id (context::upcase (t.identifier ()));
+
+ if (id == "CHAR")
+ r.byte_semantics = false;
+ else if (id != "BYTE")
+ {
+ return error (ct, "invalid keyword '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+
+ t = l.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in Oracle type declaration");
+ }
+ else
+ t = l.next ();
+ }
+
+ s = r.type == sql_type::invalid ? parse_identifier : parse_done;
+ continue;
+ }
+ case parse_done:
+ {
+ return error (ct, "unexepected '" + t.literal () + "' in Oracle "
+ "type declaration");
+ break;
+ }
+ }
+ }
+
+ // Some prefixes can also be type names if not followed by the actual
+ // type name.
+ //
+ if (r.type == sql_type::invalid)
+ {
+ if (!prefix.empty ())
+ {
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ r.byte_semantics = true;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "NCHAR" ||
+ prefix == "NATIONAL CHAR" ||
+ prefix == "NATIONAL CHARACTER")
+ {
+ r.type = sql_type::NCHAR;
+ r.byte_semantics = false;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ r.prec = true;
+ r.prec_value = 6;
+ }
+ else
+ return error (ct, "incomplete Oracle type declaration: '" +
+ prefix + "'");
+ }
+ else
+ return error (ct, "invalid Oracle type declaration");
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid Oracle type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/context.hxx b/odb/odb/relational/oracle/context.hxx
new file mode 100644
index 0000000..6c55853
--- /dev/null
+++ b/odb/odb/relational/oracle/context.hxx
@@ -0,0 +1,188 @@
+// file : odb/relational/oracle/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_ORACLE_CONTEXT_HXX
+#define ODB_RELATIONAL_ORACLE_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace oracle
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Numeric types.
+ //
+ NUMBER,
+ FLOAT,
+
+ // Floating point types.
+ //
+ BINARY_FLOAT,
+ BINARY_DOUBLE,
+
+ // Date-time types.
+ //
+ DATE,
+ TIMESTAMP,
+ INTERVAL_YM,
+ INTERVAL_DS,
+
+ // String and binary types.
+ //
+ CHAR,
+ NCHAR,
+ VARCHAR2,
+ NVARCHAR2,
+ RAW,
+
+ // LOB types.
+ //
+ BLOB,
+ CLOB,
+ NCLOB,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () :
+ type (invalid), prec (false), scale (false), byte_semantics (true)
+ {
+ }
+
+ core_type type;
+
+ bool prec;
+ unsigned short prec_value; // Oracle max value is 4000.
+
+ bool scale;
+ short scale_value; // Oracle min value is -84. Max value is 127.
+
+ bool byte_semantics;
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string, custom_db_types const* = 0);
+
+ public:
+ // If necessary, unwraps.
+ //
+ static bool
+ unsigned_integer (semantics::type&);
+
+ public:
+ // Construct sequence name from a given table name.
+ //
+ qname
+ sequence_name (qname const& table);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_ORACLE_CONTEXT_HXX
diff --git a/odb/odb/relational/oracle/header.cxx b/odb/odb/relational/oracle/header.cxx
new file mode 100644
index 0000000..bf50bb2
--- /dev/null
+++ b/odb/odb/relational/oracle/header.cxx
@@ -0,0 +1,230 @@
+// file : odb/relational/oracle/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct image_type: relational::image_type, context
+ {
+ image_type (base const& x): base (x) {};
+
+ virtual void
+ image_extra (type& c)
+ {
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+
+ // If this is a polymorphic type, only add callback to the root.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ bool gc (options.generate_query ());
+
+ if (gc)
+ os << "oracle::change_callback change_callback_;"
+ << endl;
+
+ os << "oracle::change_callback*" << endl
+ << "change_callback ()"
+ << "{";
+
+ if (gc)
+ os << "return &change_callback_;";
+ else
+ os << "return 0;";
+
+ os << "}";
+ }
+ }
+ }
+ };
+ entry<image_type> image_type_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ // Each significant base-100 digit requires a byte of storage
+ // in the manitissa. The default precision is 38 decimal digits,
+ // which is equivalent to 19 base-100 digits.
+ //
+ size_t n (19);
+
+ if (mi.st->prec)
+ n = mi.st->prec_value / 2 + mi.st->prec_value % 2;
+
+ // We require an additional byte for each of the exponent and
+ // negative value terminator values.
+ //
+ n += 2;
+
+ os << "char " << mi.var << "value[" << n << "];"
+ << "ub2 " << mi.var << "size;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ // big_float is mapped to the OCI type SQLT_NUM, which requires 21
+ // bytes of storage.
+ //
+ os << "char " << mi.var << "value[21];"
+ << "ub2 " << mi.var << "size;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << "char " << mi.var << "value[7];"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ size_t n (mi.st->prec ? mi.st->prec_value : 1);
+
+ // National characters can be either UTF-8 or UTF-16 encoded,
+ // both of which have a maximum character encoding size of 4
+ // bytes. Database character set can also be UTF-8 so if the
+ // size is specified in characters, then conservatively assume
+ // each character can take up to 4 bytes.
+ //
+ if (!mi.st->byte_semantics) // N*CHAR always has CHAR semantics.
+ n *= 4;
+
+ if (mi.st->type == sql_type::VARCHAR2 ||
+ mi.st->type == sql_type::NVARCHAR2)
+ n = n > 4000 ? 4000 : n;
+ else
+ n = n > 2000 ? 2000 : n;
+
+ os << "char " << mi.var << "value[" << n << "];"
+ << "ub2 " << mi.var << "size;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "sb2 " << mi.var << "indicator;"
+ << "oracle::lob " << mi.var << "lob;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+
+ struct class1: relational::class1
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_pre (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+ }
+ };
+ entry<class1> class1_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/inline.cxx b/odb/odb/relational/oracle/inline.cxx
new file mode 100644
index 0000000..1b6d606
--- /dev/null
+++ b/odb/odb/relational/oracle/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/oracle/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "indicator == -1;";
+ else
+ os << "i." << mi.var << "indicator = -1;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/model.cxx b/odb/odb/relational/oracle/model.cxx
new file mode 100644
index 0000000..b65e201
--- /dev/null
+++ b/odb/odb/relational/oracle/model.cxx
@@ -0,0 +1,64 @@
+// file : odb/relational/oracle/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to Oracle NUMBER.
+ //
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+ if (t.type != sql_type::NUMBER)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to Oracle NUMBER" << endl;
+
+ throw operation_failed ();
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+
+ virtual void
+ primary_key (sema_rel::primary_key& pk)
+ {
+ if (pk.auto_ ())
+ pk.extra ()["sequence"] = sequence_name (table_.name ()).string ();
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/schema.cxx b/odb/odb/relational/oracle/schema.cxx
new file mode 100644
index 0000000..75100b1
--- /dev/null
+++ b/odb/odb/relational/oracle/schema.cxx
@@ -0,0 +1,696 @@
+// file : odb/relational/oracle/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+#include <utility> // pair
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ struct sql_emitter: relational::sql_emitter
+ {
+ sql_emitter (const base& x): base (x) {}
+
+ virtual void
+ line (const std::string& l)
+ {
+ // SQLPlus doesn't like empty line in the middle of a statement.
+ //
+ if (!l.empty ())
+ {
+ base::line (l);
+ last_ = l;
+ }
+ }
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ {
+ if (last_ == "END;")
+ os << endl
+ << '/' << endl
+ << endl;
+
+ else
+ os << ';' << endl
+ << endl;
+ }
+ }
+
+ private:
+ string last_;
+ };
+ entry<sql_emitter> sql_emitter_;
+
+ //
+ // File.
+ //
+
+ struct sql_file: relational::sql_file, context
+ {
+ sql_file (const base& x): base (x) {}
+
+ virtual void
+ prologue ()
+ {
+ // Quiet down SQLPlus and make sure it exits with an error
+ // code if there is an error.
+ //
+ os << "SET FEEDBACK OFF;" << endl
+ << "WHENEVER SQLERROR EXIT FAILURE;" << endl
+ << "WHENEVER OSERROR EXIT FAILURE;" << endl
+ << endl;
+ }
+
+ virtual void
+ epilogue ()
+ {
+ os << "EXIT;" << endl;
+ }
+ };
+ entry<sql_file> sql_file_;
+
+ //
+ // Drop.
+ //
+
+ struct drop_column: relational::drop_column, context
+ {
+ drop_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (dc.name ());
+ }
+ };
+ entry<drop_column> drop_column_;
+
+ struct drop_foreign_key: relational::drop_foreign_key, context
+ {
+ drop_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ os << endl;
+ drop (dfk);
+ }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In Oracle, index names can be qualified with the schema.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+ return quote_id (n);
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, bool migration)
+ {
+ using sema_rel::primary_key;
+
+ sema_rel::table::names_iterator i (t.find ("")); // Special name.
+ primary_key* pk (i != t.names_end ()
+ ? &dynamic_cast<primary_key&> (i->nameable ())
+ : 0);
+
+ string qt (quote_id (t.name ()));
+ string qs (pk != 0 && pk->auto_ ()
+ ? quote_id (qname::from_string (pk->extra ()["sequence"]))
+ : "");
+
+ if (migration)
+ {
+ pre_statement ();
+ os << "DROP TABLE " << qt << endl;
+ post_statement ();
+
+ // Drop the sequence if we have auto primary key.
+ //
+ if (!qs.empty ())
+ {
+ pre_statement ();
+ os << "DROP SEQUENCE " << qs << endl;
+ post_statement ();
+ }
+ }
+ else
+ {
+ // Oracle has no IF EXISTS conditional for dropping objects. The
+ // PL/SQL approach below seems to be the least error-prone and the
+ // most widely used of the alternatives.
+ //
+ pre_statement ();
+ os << "BEGIN" << endl
+ << " BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'DROP TABLE " << qt << " CASCADE " <<
+ "CONSTRAINTS';" << endl
+ << " EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -942 THEN RAISE; END IF;" << endl
+ << " END;" << endl;
+
+ // Drop the sequence if we have auto primary key.
+ //
+ if (!qs.empty ())
+ {
+ os << " BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'DROP SEQUENCE " << qs <<
+ "';" << endl
+ << " EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -2289 THEN RAISE; END IF;" << endl
+ << " END;" << endl;
+ }
+
+ os << "END;" << endl;
+ post_statement ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // For migration drop foreign keys explicitly in pre-migration.
+ //
+ if (migration)
+ {
+ base::traverse (t, migration);
+ return;
+ }
+
+ // For schema creation we use the CASCADE clause to drop foreign
+ // keys.
+ //
+ if (pass_ != 2)
+ return;
+
+ drop (t, migration);
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+ static sema_rel::uname
+ truncate (location const& l, const char* kind, sema_rel::uname n, bool w)
+ {
+ if (n.size () > 30)
+ {
+ if (w)
+ warn (l) << kind << " name '" << n << "' is longer than 30 "
+ << "characters and will be truncated" << endl;
+
+ n.resize (30);
+ }
+
+ return n;
+ }
+
+ static sema_rel::qname
+ truncate (location const& l,
+ const char* kind,
+ sema_rel::qname const& n,
+ bool w)
+ {
+ // Don't bother verifying the schema name since that is
+ // specified explicitly and in a single place.
+ //
+ qname r (n.qualifier ());
+ r.append (truncate (l, kind, n.uname (), w));
+ return r;
+ }
+
+ template <typename N>
+ struct scope
+ {
+ typedef std::map<N, pair<N, location> > map;
+
+ scope (const char* k, const char* p, bool w)
+ : kind_ (k), prag_ (p), warn_ (w) {}
+
+ void
+ check (location const& l, N const& n)
+ {
+ N tn (truncate (l, kind_, n, warn_));
+
+ pair<typename map::iterator, bool> r (
+ map_.insert (make_pair (tn, make_pair (n, l))));
+
+ if (r.second)
+ return;
+
+ error (l) << kind_ << " name '" << tn << "' conflicts with an "
+ << "already defined " << kind_ << " name" << endl;
+
+ if (tn != n)
+ info (l) << kind_ << " name '" << tn << "' is truncated '"
+ << n << "'" << endl;
+
+ N const& n1 (r.first->second.first);
+ location const& l1 (r.first->second.second);
+
+ info (l1) << "conflicting " << kind_ << " is defined here" << endl;
+
+ if (tn != n)
+ info (l1) << "conflicting " << kind_ << " name '" << tn
+ << "' is truncated '" << n1 << "'" << endl;
+
+ info (l) << "use #pragma db " << prag_ << " to change one of "
+ << "the names" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ clear () {map_.clear ();}
+
+ const char* kind_;
+ const char* prag_;
+ bool warn_;
+ map map_;
+ };
+
+ struct scopes
+ {
+ scopes (bool warn)
+ : tables ("table", "table", warn),
+ fkeys ("foreign key", "column", warn), // Change column name.
+ indexes ("index", "index", warn),
+ sequences ("sequence", "table", warn), // Change table name.
+ columns ("column", "column", warn) {}
+
+ // In Oracle, all these entities are in their own name spaces,
+ // as in an index and a foreign key with the same name do not
+ // conflict.
+ //
+ scope<sema_rel::qname> tables;
+ scope<sema_rel::uname> fkeys; // Global but can't have schema.
+ scope<sema_rel::qname> indexes;
+ scope<sema_rel::qname> sequences;
+ scope<sema_rel::uname> columns;
+ };
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->columns.check (c.get<location> ("cxx-location"), c.name ());
+
+ base::traverse (c);
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ create (ac);
+ }
+
+ virtual void
+ constraints (sema_rel::column& c, sema_rel::primary_key* pk)
+ {
+ // Oracle wants DEFAULT before NULL even though we can end
+ // up with mouthfulls like DEFAULT NULL NULL.
+ //
+ if (!c.default_ ().empty ())
+ os << " DEFAULT " << c.default_ ();
+
+ null (c);
+
+ // If this is a single-column primary key, generate it inline.
+ //
+ if (pk != 0 && pk->contains_size () == 1)
+ primary_key ();
+
+ if (pk != 0 && pk->auto_ ())
+ auto_ (*pk);
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->fkeys.check (fk.get<location> ("cxx-location"), fk.name ());
+
+ base::traverse_create (fk);
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->fkeys.check (fk.get<location> ("cxx-location"), fk.name ());
+
+ os << endl
+ << " ADD CONSTRAINT ";
+ create (fk);
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In Oracle, index names can be qualified with the schema.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->indexes.check (in.get<location> ("cxx-location"), n);
+
+ return quote_id (n);
+ }
+ };
+ entry<create_index> create_index_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::table& t)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ {
+ if (pass_ == 1)
+ {
+ s->tables.check (t.get<location> ("cxx-location"), t.name ());
+ s->columns.clear ();
+ }
+ }
+
+ base::traverse (t);
+
+ if (pass_ == 1)
+ {
+ // Create the sequence if we have auto primary key.
+ //
+ using sema_rel::primary_key;
+
+ sema_rel::table::names_iterator i (t.find ("")); // Special name.
+ primary_key* pk (i != t.names_end ()
+ ? &dynamic_cast<primary_key&> (i->nameable ())
+ : 0);
+
+ if (pk != 0 && pk->auto_ ())
+ {
+ // Already qualified with the table's schema, if any.
+ //
+ sema_rel::qname n (
+ qname::from_string (pk->extra ()["sequence"]));
+
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->sequences.check (pk->get<location> ("cxx-location"), n);
+
+ pre_statement ();
+ os_ << "CREATE SEQUENCE " << quote_id (n) << endl
+ << " START WITH 1 INCREMENT BY 1" << endl;
+ post_statement ();
+ }
+ }
+ }
+ };
+ entry<create_table> create_table_;
+
+ struct create_model: relational::create_model, context
+ {
+ create_model (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::model& m)
+ {
+ scopes s (options.oracle_warn_truncation ());
+ context::extra = &s;
+ base::traverse (m);
+ context::extra = 0;
+ }
+ };
+ entry<create_model> create_model_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Relax (NULL) in pre and tighten (NOT NULL) in post.
+ //
+ if (pre_ != c.null ())
+ return;
+
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (c.name ()) << (c.null () ? " NULL" : " NOT NULL");
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // Oracle can only alter certain kinds of things together but
+ // grouped one at a time.
+ //
+ if (check<sema_rel::drop_foreign_key> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ instance<drop_foreign_key> dfc (*this);
+ trav_rel::unames n (*dfc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ if (check<sema_rel::add_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD (";
+
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ if (check_alter_column_null (at, true))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " MODIFY (";
+
+ bool tl (true); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, tl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // Oracle can only alter certain kinds of things together but
+ // grouped one at a time.
+ //
+ if (check<sema_rel::drop_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " DROP (";
+
+ instance<drop_column> dc (*this);
+ trav_rel::unames n (*dc);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ if (check_alter_column_null (at, false))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " MODIFY (";
+
+ bool fl (false); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, fl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ if (check<sema_rel::add_foreign_key> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ instance<create_foreign_key> cfc (*this);
+ trav_rel::unames n (*cfc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x)
+ : base (x)
+ {
+ // If the schema name is empty, replace it with a single space
+ // to workaround the VARCHAR2 empty/NULL issue.
+ //
+ if (qs_ == "''")
+ qs_ = "' '";
+ }
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'CREATE TABLE " << qt_ << " (" << endl
+ << " " << qn_ << " VARCHAR2(512) NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " NUMBER(20) NOT NULL," << endl
+ << " " << qm_ << " NUMBER(1) NOT NULL)';" << endl
+ << "EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -955 THEN RAISE; END IF;" << endl
+ << "END;" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "MERGE INTO " << qt_ << " USING DUAL ON (" << qn_ << " = " <<
+ qs_ << ")" << endl
+ << " WHEN NOT MATCHED THEN INSERT (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/source.cxx b/odb/odb/relational/oracle/source.cxx
new file mode 100644
index 0000000..adf9864
--- /dev/null
+++ b/odb/odb/relational/oracle/source.cxx
@@ -0,0 +1,646 @@
+// file : odb/relational/oracle/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters, context
+ {
+ query_parameters (base const& x): base (x), i_ (0) {}
+
+ virtual string
+ next (semantics::data_member&, const string&, const string&)
+ {
+ ostringstream ss;
+ ss << ":" << ++i_;
+
+ return ss.str ();
+ }
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return quote_id (sequence_name (table_)) + ".nextval";
+ }
+
+ private:
+ size_t i_;
+ };
+ entry<query_parameters> query_parameters_;
+
+ namespace
+ {
+ const char* string_buffer_types[] =
+ {
+ "oracle::bind::string", // CHAR
+ "oracle::bind::nstring", // NCHAR
+ "oracle::bind::string", // VARCHAR2
+ "oracle::bind::nstring", // NVARCHAR2
+ "oracle::bind::raw" // RAW
+ };
+
+ const char* lob_buffer_types[] =
+ {
+ "oracle::bind::blob",
+ "oracle::bind::clob",
+ "oracle::bind::nclob"
+ };
+ }
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::" <<
+ (unsigned_integer (mi.t) ? "uinteger" : "integer") << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 4;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::" <<
+ (unsigned_integer (mi.t) ? "uinteger" : "integer") << ";"
+ << b << ".buffer= &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 8;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::number;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg <<
+ "." << mi.var << "value));"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::binary_float;"
+ << b << ".buffer= &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 4;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::binary_double;"
+ << b << ".buffer= &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 8;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::number;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg << "." <<
+ mi.var << "value));"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::date;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg << "." <<
+ mi.var << "value));"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::timestamp;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::interval_ym;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::interval_ds;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = " <<
+ string_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg <<
+ "." << mi.var << "value));"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << b << ".type = " <<
+ lob_buffer_types[mi.st->type - sql_type::BLOB] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "lob;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;"
+ << b << ".callback = &" << arg << "." << mi.var << "callback;"
+ << endl;
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ check_accessor (member_info& mi, member_access& ma)
+ {
+ // We cannot use accessors that return by-value for LOB
+ // members.
+ //
+ if ((mi.st->type == sql_type::BLOB ||
+ mi.st->type == sql_type::CLOB ||
+ mi.st->type == sql_type::NCLOB) &&
+ ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a data member of Oracle LOB type" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "indicator = -1;";
+ }
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
+ << "i." << mi.var << "size = static_cast<ub2> (size);";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
+ << "i." << mi.var << "size = static_cast<ub2> (size);";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
+ << "i." << mi.var << "size = static_cast<ub2> (size);";
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << "i." << mi.var << "lob.position = 0;"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "indicator == -1";
+ }
+
+ virtual void
+ check_modifier (member_info& mi, member_access& ma)
+ {
+ // We cannot use by-value modifier for LOB members.
+ //
+ if ((mi.st->type == sql_type::BLOB ||
+ mi.st->type == sql_type::CLOB ||
+ mi.st->type == sql_type::NCLOB) &&
+ ma.placeholder ())
+ {
+ error (ma.loc) << "modifier accepting a value cannot be used "
+ << "for a data member of Oracle LOB type" << endl;
+ info (ma.loc) << "modifier returning a non-const reference is "
+ << "required" << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct container_traits: relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since Oracle can execute several
+ // interleaving statements.
+ //
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "sts.select_statement ().stream_result ();"
+ << endl;
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_image_pre (type& c)
+ {
+ if (options.generate_query () &&
+ !(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ os << "{"
+ << "root_traits::image_type& ri (root_image (i));"
+ << endl;
+
+ string i (poly_derived ? "ri" : "i");
+
+ os << "if (" << i << ".change_callback_.callback != 0)" << endl
+ << "(" << i << ".change_callback_.callback) (" <<
+ i << ".change_callback_.context);";
+
+ if (poly_derived)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters& qp,
+ persist_position p)
+ {
+ string r;
+
+ if (p == persist_after_values)
+ {
+ data_member_path* id (id_member (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // Top-level auto id.
+ //
+ if (id != 0 && !poly_derived && auto_ (*id))
+ {
+ semantics::data_member& idb (*id->back ());
+
+ const string& name (column_qname (*id));
+ const string& type (column_type (idb));
+
+ r = "RETURNING " + convert_from (name, type, idb) +
+ " INTO " + qp.next (idb, name, type);
+ }
+ }
+
+ return r;
+ }
+
+ virtual string
+ select_trailer (type& c)
+ {
+ view_query const& vq (c.get<view_query> ("query"));
+
+ if (vq.for_update && vq.distinct)
+ {
+ error (vq.loc)
+ << "Oracle does not support FOR UPDATE with DISTINCT" << endl;
+ throw operation_failed ();
+ }
+
+ return base::select_trailer (c);
+ }
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/common.cxx b/odb/odb/relational/pgsql/common.cxx
new file mode 100644
index 0000000..6a59954
--- /dev/null
+++ b/odb/odb/relational/pgsql/common.cxx
@@ -0,0 +1,351 @@
+// file : odb/relational/pgsql/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/pgsql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Integral types.
+ //
+ case sql_type::BOOLEAN:
+ case sql_type::SMALLINT:
+ case sql_type::INTEGER:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Float types.
+ //
+ case sql_type::REAL:
+ case sql_type::DOUBLE:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::NUMERIC:
+ {
+ traverse_numeric (mi);
+ break;
+ }
+
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ case sql_type::TIME:
+ case sql_type::TIMESTAMP:
+ {
+ traverse_date_time (mi);
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::TEXT:
+ case sql_type::BYTEA:
+ {
+ traverse_string (mi);
+ break;
+ }
+ case sql_type::BIT:
+ {
+ traverse_bit (mi);
+ break;
+ }
+ case sql_type::VARBIT:
+ {
+ traverse_varbit (mi);
+ break;
+ }
+ // Other types.
+ //
+ case sql_type::UUID:
+ {
+ traverse_uuid (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ namespace
+ {
+ const char* integer_types[] =
+ {
+ "bool",
+ "short",
+ "int",
+ "long long"
+ };
+
+ const char* float_types[] =
+ {
+ "float",
+ "double"
+ };
+
+ const char* date_time_types[] =
+ {
+ "int",
+ "long long",
+ "long long"
+ };
+ }
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_pgsql >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ type_ += integer_types[mi.st->type - sql_type::BOOLEAN];
+ }
+
+ void member_image_type::
+ traverse_float (member_info& mi)
+ {
+ type_ = float_types[mi.st->type - sql_type::REAL];
+ }
+
+ void member_image_type::
+ traverse_numeric (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_date_time (member_info& mi)
+ {
+ type_ = date_time_types[mi.st->type - sql_type::DATE];
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_bit (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ void member_image_type::
+ traverse_varbit (member_info&)
+ {
+ type_ = "details::ubuffer";
+ }
+
+ void member_image_type::
+ traverse_uuid (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ namespace
+ {
+ const char* integer_database_id[] =
+ {
+ "id_boolean",
+ "id_smallint",
+ "id_integer",
+ "id_bigint"
+ };
+
+ const char* float_database_id[] =
+ {
+ "id_real",
+ "id_double"
+ };
+
+ const char* date_time_database_id[] =
+ {
+ "id_date",
+ "id_time",
+ "id_timestamp"
+ };
+
+ const char* char_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_string", // VARCHAR
+ "id_string", // TEXT,
+ "id_bytea" // BYTEA
+ };
+ }
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ integer_database_id[mi.st->type - sql_type::BOOLEAN];
+ }
+
+ void member_database_type_id::
+ traverse_float (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ float_database_id[mi.st->type - sql_type::REAL];
+ }
+
+ void member_database_type_id::
+ traverse_numeric (member_info&)
+ {
+ type_id_ = "pgsql::id_numeric";
+ }
+
+ void member_database_type_id::
+ traverse_date_time (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ date_time_database_id[mi.st->type - sql_type::DATE];
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ char_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type_id::
+ traverse_bit (member_info&)
+ {
+ type_id_ = "pgsql::id_bit";
+ }
+
+ void member_database_type_id::
+ traverse_varbit (member_info&)
+ {
+ type_id_ = "pgsql::id_varbit";
+ }
+
+ void member_database_type_id::
+ traverse_uuid (member_info&)
+ {
+ type_id_ = "pgsql::id_uuid";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/pgsql/common.hxx b/odb/odb/relational/pgsql/common.hxx
new file mode 100644
index 0000000..1d383bf
--- /dev/null
+++ b/odb/odb/relational/pgsql/common.hxx
@@ -0,0 +1,159 @@
+// file : odb/relational/pgsql/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PGSQL_COMMON_HXX
+#define ODB_RELATIONAL_PGSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the pgsql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_numeric (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_varbit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_uuid (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_numeric (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_varbit (member_info&);
+
+ virtual void
+ traverse_uuid (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_numeric (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_varbit (member_info&);
+
+ virtual void
+ traverse_uuid (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_PGSQL_COMMON_HXX
diff --git a/odb/odb/relational/pgsql/context.cxx b/odb/odb/relational/pgsql/context.cxx
new file mode 100644
index 0000000..7f99f5d
--- /dev/null
+++ b/odb/odb/relational/pgsql/context.cxx
@@ -0,0 +1,786 @@
+// file : odb/relational/pgsql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/pgsql/context.hxx>
+#include <odb/relational/pgsql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "BOOLEAN", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "SMALLINT", 0, false},
+ {"unsigned char", "SMALLINT", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT", 0, false},
+
+ {"int", "INTEGER", 0, false},
+ {"unsigned int", "INTEGER", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT", 0, false},
+
+ {"float", "REAL", 0, false},
+ {"double", "DOUBLE PRECISION", 0, false},
+
+ {"::std::string", "TEXT", 0, false},
+
+ {"::size_t", "BIGINT", 0, false},
+ {"::std::size_t", "BIGINT", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = true;
+ global_index = true;
+ global_fkey = false;
+ data_->bind_vector_ = "pgsql::bind*";
+ data_->truncated_vector_ = "bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("pgsql-grow"))
+ r_ = c.get<bool> ("pgsql-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("pgsql-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_numeric (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_varbit (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("pgsql-grow"))
+ return c.get<bool> ("pgsql-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ if (i->size () > 63)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r += *i;
+ r += '"';
+ }
+
+ return r;
+ }
+
+ string context::
+ statement_name (string const& type, string const& name, semantics::node& n)
+ {
+ // Put the type first so that in the case of truncation it
+ // remains thus lowering the chance of a clash.
+ //
+ string r (type);
+ r += '_';
+ r += name;
+
+ r = transform_name (r, sql_name_statement);
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ // Note that we have to do it in addition to the above since this
+ // name doesn't go through quote_id().
+ //
+ if (r.size () > 63)
+ {
+ location const& l (n.location ());
+
+ warn (l) << "prepared statement name '" << r << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ info (l) << "consider shortening the corresponding namespace "
+ << "name, class name, or data member name" << endl;
+
+ info (l) << "or shortening the statement name itself using the "
+ << "--statement-regex option" << endl;
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR(";
+ else
+ {
+ r = "VARCHAR(";
+ n--;
+ }
+
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // BIT VARYING (VARBIT)
+ // CHARACTER VARYING (VARRCHAR)
+ // DOUBLE PRECISION (DOUBLE)
+ // TIME WITH TIME ZONE (not currently supported)
+ // TIMESTAMP WITH TIME ZONE (not currently supported)
+ //
+
+ enum state
+ {
+ parse_prefix,
+ parse_name,
+ parse_range,
+ parse_suffix,
+ parse_done
+ };
+
+ state s (parse_prefix);
+ string prefix;
+ bool flt (false);
+
+ for (sql_token t (l.next ());
+ s != parse_done && t.type () != sql_token::t_eos;
+ t = l.next ())
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_prefix:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ if (id == "BIT" ||
+ id == "CHARACTER" ||
+ id == "DOUBLE")
+ {
+ prefix = id;
+ s = parse_name;
+ continue;
+ }
+ }
+
+ s = parse_name;
+ }
+ // Fall through.
+ case parse_name:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ bool match (true);
+ string const& id (context::upcase (t.identifier ()));
+
+ //
+ // Numeric types.
+ //
+ if (id == "BOOL" || id == "BOOLEAN")
+ {
+ r.type = sql_type::BOOLEAN;
+ }
+ else if (id == "SMALLINT" || id == "INT2")
+ {
+ r.type = sql_type::SMALLINT;
+ }
+ else if (id == "INT" ||
+ id == "INTEGER" ||
+ id == "INT4")
+ {
+ r.type = sql_type::INTEGER;
+ }
+ else if (id == "BIGINT")
+ {
+ r.type = sql_type::BIGINT;
+ }
+ else if (id == "REAL" || id == "FLOAT4")
+ {
+ r.type = sql_type::REAL;
+ }
+ else if ((id == "PRECISION" && prefix == "DOUBLE") ||
+ id == "FLOAT8")
+ {
+ r.type = sql_type::DOUBLE;
+ }
+ else if (id == "FLOAT")
+ {
+ // Assign a type only once we know the precision of the
+ // float.
+ //
+ flt = true;
+ }
+ else if (id == "NUMERIC" || id == "DECIMAL")
+ {
+ r.type = sql_type::NUMERIC;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE")
+ {
+ r.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r.type = sql_type::TIME;
+ }
+ else if (id == "TIMETZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ else if (id == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ }
+ else if (id == "TIMESTAMPTZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "CHAR")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (id == "VARCHAR")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "TEXT")
+ {
+ r.type = sql_type::TEXT;
+ }
+ else if (id == "VARYING")
+ {
+ if (prefix == "BIT")
+ r.type = sql_type::VARBIT;
+ else if (prefix == "CHARACTER")
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "BYTEA")
+ {
+ r.type = sql_type::BYTEA;
+ }
+ else if (id == "VARBIT")
+ {
+ r.type = sql_type::VARBIT;
+ }
+ //
+ // Other types.
+ //
+ else if (id == "UUID")
+ {
+ r.type = sql_type::UUID;
+ }
+ else
+ match = false;
+
+ if (match)
+ {
+ s = parse_range;
+ continue;
+ }
+ }
+
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (!prefix.empty ())
+ {
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "expected PostgreSQL type name");
+ }
+
+ s = parse_range;
+ }
+ // Fall through.
+ case parse_range:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer range expected in PostgreSQL "
+ "type declaration");
+ }
+
+ unsigned int v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid range value '" + t.literal () +
+ "' in PostgreSQL type declaration");
+ }
+
+ r.range = true;
+ r.range_value = v;
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // We have the second range value. Skip it.
+ //
+ l.next ();
+ t = l.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in PostgreSQL type "
+ "declaration");
+ }
+
+ s = parse_suffix;
+ continue;
+ }
+
+ s = parse_suffix;
+ }
+ // Fall through.
+ case parse_suffix:
+ {
+ if (r.type == sql_type::TIME || r.type == sql_type::TIMESTAMP)
+ {
+ string const& id1 (context::upcase (t.identifier ()));
+
+ if (id1 == "WITH")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id2 (context::upcase (t.identifier ()));
+
+ if (id2 == "TIME")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id3 (context::upcase (t.identifier ()));
+
+ if (id3 == "ZONE")
+ {
+ // This code shall not fall through.
+ //
+ return error (ct, "PostgreSQL time zones are not "
+ "currently supported");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "unknown PostgreSQL type");
+ }
+ case parse_done:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ if (s == parse_name && !prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (flt)
+ {
+ r.type = r.range && r.range_value < 25 ?
+ sql_type::REAL :
+ sql_type::DOUBLE;
+ }
+
+ if (r.type == sql_type::invalid)
+ return error (ct, "incomplete PostgreSQL type declaration");
+
+ // If range is omitted for CHAR or BIT types, it defaults to 1.
+ //
+ if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
+ {
+ r.range = true;
+ r.range_value = 1;
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid PostgreSQL type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/context.hxx b/odb/odb/relational/pgsql/context.hxx
new file mode 100644
index 0000000..64e0b1a
--- /dev/null
+++ b/odb/odb/relational/pgsql/context.hxx
@@ -0,0 +1,192 @@
+// file : odb/relational/pgsql/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PGSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_PGSQL_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ BOOLEAN,
+ SMALLINT,
+ INTEGER,
+ BIGINT,
+
+ // Float types.
+ //
+ REAL,
+ DOUBLE,
+ NUMERIC,
+
+ // Data-time types.
+ //
+ DATE,
+ TIME,
+ TIMESTAMP,
+
+ // String and binary types.
+ //
+ CHAR,
+ VARCHAR,
+ TEXT,
+ BYTEA,
+ BIT,
+ VARBIT,
+
+ // Other types.
+ //
+ UUID,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () : type (invalid), range (false) {}
+
+ core_type type;
+
+ // VARBIT maximum length is 2^31 - 1 bit. String types can hold a
+ // maximum of 1GB of data.
+ //
+ bool range;
+ unsigned int range_value;
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string, custom_db_types const* = 0);
+
+ public:
+ // Construct statement name from a given type and name.
+ //
+ string
+ statement_name (string const& type,
+ string const& name,
+ semantics::node&);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_PGSQL_CONTEXT_HXX
diff --git a/odb/odb/relational/pgsql/header.cxx b/odb/odb/relational/pgsql/header.cxx
new file mode 100644
index 0000000..c3efc3e
--- /dev/null
+++ b/odb/odb/relational/pgsql/header.cxx
@@ -0,0 +1,285 @@
+// file : odb/relational/pgsql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct class1: relational::class1
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_post (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ // Statement names.
+ //
+ os << "static const char persist_statement_name[];";
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ os << "static const char* const find_statement_names[" <<
+ (abst ? "1" : "depth") << "];";
+ else
+ os << "static const char find_statement_name[];";
+
+ if (poly && !poly_derived)
+ os << "static const char find_discriminator_statement_name[];";
+
+ if (update_columns != 0)
+ os << "static const char update_statement_name[];";
+
+ os << "static const char erase_statement_name[];";
+
+ if (optimistic != 0)
+ os << "static const char optimistic_erase_statement_name[];";
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ os << "static const char query_statement_name[];"
+ << "static const char erase_query_statement_name[];";
+
+ os << endl;
+
+ // Statement types.
+ //
+ os << "static const unsigned int persist_statement_types[];";
+
+ if (id != 0)
+ {
+ os << "static const unsigned int find_statement_types[];";
+
+ if (update_columns != 0)
+ os << "static const unsigned int update_statement_types[];";
+
+ if (optimistic != 0)
+ os << "static const unsigned int " <<
+ "optimistic_erase_statement_types[];";
+ }
+
+ os << endl;
+
+ if (poly_derived)
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+ }
+
+ virtual void
+ view_public_extra_post (type&)
+ {
+ // Statement names.
+ //
+ os << "static const char query_statement_name[];"
+ << endl;
+ }
+ };
+ entry<class1> class1_entry_;
+
+ struct container_traits: relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_public_extra_pre (semantics::data_member& m,
+ semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ bool smart (!inverse (m, "value") && !unordered (m) &&
+ container_smart (t));
+
+ // Container statement names.
+ //
+ os << "static const char select_name[];"
+ << "static const char insert_name[];";
+
+ if (smart)
+ os << "static const char update_name[];";
+
+ os << "static const char delete_name[];"
+ << endl;
+
+ // Container statement types.
+ //
+ os << "static const unsigned int insert_types[];";
+
+ if (smart)
+ os << "static const unsigned int update_types[];"
+ << "static const unsigned int delete_types[];";
+
+ os << endl;
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_post (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Statement names.
+ //
+ if (load || load_opt)
+ os << "static const char select_name[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_name[];"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ os << "static const unsigned int update_types[];";
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // Exchanged as strings. Can have up to 1000 digits not counting
+ // '-' and '.'.
+ //
+
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Additional 4 bytes at the beginning of the array specify
+ // the number of significant bits in the image. This number
+ // is stored in network byte order.
+ //
+ unsigned int n (4 + mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
+
+ os << "unsigned char " << mi.var << "value[" << n << "];"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ // UUID is a 16-byte sequence.
+ //
+ os << "unsigned char " << mi.var << "value[16];"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/inline.cxx b/odb/odb/relational/pgsql/inline.cxx
new file mode 100644
index 0000000..08688c3
--- /dev/null
+++ b/odb/odb/relational/pgsql/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/pgsql/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "null;";
+ else
+ os << "i." << mi.var << "null = true;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/model.cxx b/odb/odb/relational/pgsql/model.cxx
new file mode 100644
index 0000000..092f8bb
--- /dev/null
+++ b/odb/odb/relational/pgsql/model.cxx
@@ -0,0 +1,101 @@
+// file : odb/relational/pgsql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ base::traverse_object (c);
+
+ if (context::top_object == &c)
+ {
+ // Make sure that the auto id type is INTEGER or BIGINT.
+ //
+ if (pkey_ != 0 && pkey_->auto_ ())
+ {
+ // Should be a single column.
+ //
+ sema_rel::column& c (pkey_->contains_begin ()->column ());
+
+ // This should never fail since we have already parsed this.
+ //
+ sql_type const& t (parse_sql_type (c.type ()));
+
+ if (t.type != sql_type::INTEGER && t.type != sql_type::BIGINT)
+ {
+ location const& l (c.get<location> ("cxx-location"));
+ error (l) << "automatically assigned object id must map "
+ << "to PostgreSQL INTEGER or BIGINT" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ return v ? "TRUE" : "FALSE";
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to an integer type.
+ //
+ switch (parse_sql_type (column_type (), m, false).type)
+ {
+ case sql_type::SMALLINT:
+ case sql_type::INTEGER:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to PostgreSQL integer type" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/schema.cxx b/odb/odb/relational/pgsql/schema.cxx
new file mode 100644
index 0000000..b9c3f2e
--- /dev/null
+++ b/odb/odb/relational/pgsql/schema.cxx
@@ -0,0 +1,266 @@
+// file : odb/relational/pgsql/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ //
+ // Drop.
+ //
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // For migration drop foreign keys explicitly in pre-migration.
+ //
+ if (migration)
+ {
+ base::traverse (t, migration);
+ return;
+ }
+
+ // For schema creation we use the CASCADE clause to drop foreign
+ // keys.
+ //
+ if (pass_ != 2)
+ return;
+
+ pre_statement ();
+ os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
+ quote_id (t.name ()) << " CASCADE" << endl;
+ post_statement ();
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ type (sema_rel::column& c, bool auto_)
+ {
+ if (auto_)
+ {
+ // This should never fail since we have already parsed this.
+ //
+ sql_type const& t (parse_sql_type (c.type ()));
+
+ // The model creation code makes sure it is one of these type.
+ //
+ if (t.type == sql_type::INTEGER)
+ os << "SERIAL";
+ else if (t.type == sql_type::BIGINT)
+ os << "BIGSERIAL";
+ }
+ else
+ base::type (c, auto_);
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ deferrable (sema_rel::deferrable d)
+ {
+ os << endl
+ << " INITIALLY " << d;
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ {
+ // Handle the CONCURRENTLY keyword.
+ //
+ string const& t (in.type ());
+
+ if (t == "concurrently" || t == "CONCURRENTLY")
+ {
+ os << "INDEX " << t;
+ }
+ else
+ {
+ size_t p (t.rfind (' '));
+ string s (t, (p != string::npos ? p + 1 : 0), string::npos);
+
+ if (s == "concurrently" || s == "CONCURRENTLY")
+ os << string (t, 0, p) << " INDEX " << s;
+ else
+ os << t << " INDEX";
+ }
+ }
+ else
+ os << "INDEX";
+
+ os << " " << name (in) << endl
+ << " ON " << table_name (in);
+
+ if (!in.method ().empty ())
+ os << " USING " << in.method ();
+
+ os << " (";
+ columns (in);
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+ };
+ entry<create_index> create_index_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::column& c)
+ {
+ os << quote_id (c.name ()) << " " <<
+ (c.null () ? "DROP" : "SET") << " NOT NULL";
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ // PostgreSQL prior to 9.1 doesn't support IF NOT EXISTS in
+ // CREATE TABLE. We also cannot use IF-ELSE construct in plain
+ // SQL. To make it at least work for a single schema, we are
+ // going to drop the schema version table after the DROP
+ // statements and then unconditionally create it after CREATE.
+ //
+ virtual void
+ create_table ()
+ {
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ {
+ pre_statement ();
+
+ os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BOOLEAN NOT NULL)" << endl;
+
+ post_statement ();
+ }
+ }
+
+ virtual void
+ drop ()
+ {
+ pre_statement ();
+
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ os << "DELETE FROM " << qt_ << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+ else
+ os << "DROP TABLE IF EXISTS " << qt_ << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ {
+ os << "INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " SELECT " << qs_ << ", " << v << ", FALSE" << endl
+ << " WHERE NOT EXISTS (" << endl
+ << " SELECT 1 FROM " << qt_ << " WHERE " << qn_ << " = " <<
+ qs_ << ")" << endl;
+ }
+ else
+ {
+ os << "CREATE TABLE " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BOOLEAN NOT NULL)" << endl;
+
+ post_statement ();
+ pre_statement ();
+
+ os << "INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", FALSE)" << endl;
+ }
+
+ post_statement ();
+ }
+
+ virtual void
+ migrate_pre (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qv_ << " = " << v << ", " << qm_ << " = TRUE" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ migrate_post ()
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qm_ << " = FALSE" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/source.cxx b/odb/odb/relational/pgsql/source.cxx
new file mode 100644
index 0000000..b881e48
--- /dev/null
+++ b/odb/odb/relational/pgsql/source.cxx
@@ -0,0 +1,1140 @@
+// file : odb/relational/pgsql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters
+ {
+ query_parameters (base const& x): base (x), i_ (0) {}
+
+ virtual string
+ next (semantics::data_member&, const string&, const string&)
+ {
+ ostringstream ss;
+ ss << "$" << ++i_;
+
+ return ss.str ();
+ }
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return "DEFAULT";
+ }
+
+ private:
+ size_t i_;
+ };
+ entry<query_parameters> query_parameters_;
+
+ namespace
+ {
+ const char* integer_buffer_types[] =
+ {
+ "pgsql::bind::boolean_",
+ "pgsql::bind::smallint",
+ "pgsql::bind::integer",
+ "pgsql::bind::bigint"
+ };
+
+ const char* float_buffer_types[] =
+ {
+ "pgsql::bind::real",
+ "pgsql::bind::double_"
+ };
+
+ const char* char_bin_buffer_types[] =
+ {
+ "pgsql::bind::text", // CHAR
+ "pgsql::bind::text", // VARCHAR
+ "pgsql::bind::text", // TEXT
+ "pgsql::bind::bytea" // BYTEA
+ };
+
+ const char* date_time_buffer_types[] =
+ {
+ "pgsql::bind::date",
+ "pgsql::bind::time",
+ "pgsql::bind::timestamp"
+ };
+
+ const char* oids[] =
+ {
+ "pgsql::bool_oid", // BOOLEAN
+ "pgsql::int2_oid", // SMALLINT
+ "pgsql::int4_oid", // INTEGER
+ "pgsql::int8_oid", // BIGINT
+ "pgsql::float4_oid", // REAL
+ "pgsql::float8_oid", // DOUBLE
+ "pgsql::numeric_oid", // NUMERIC
+ "pgsql::date_oid", // DATE
+ "pgsql::time_oid", // TIME
+ "pgsql::timestamp_oid", // TIMESTAMP
+ "pgsql::text_oid", // CHAR
+ "pgsql::text_oid", // VARCHAR
+ "pgsql::text_oid", // TEXT
+ "pgsql::bytea_oid", // BYTEA
+ "pgsql::bit_oid", // BIT
+ "pgsql::varbit_oid", // VARBIT
+ "pgsql::uuid_oid" // UUID
+ };
+ }
+
+ struct statement_oids: object_columns_base, context
+ {
+ statement_oids (statement_kind sk,
+ bool first = true,
+ object_section* section = 0)
+ : object_columns_base (first, column_prefix (), section), sk_ (sk)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (!(inverse (m, key_prefix_) && sk_ != statement_select))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m,
+ string const&,
+ bool first)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (id ())
+ {
+ if (sk_ == statement_update ||
+ (sk_ == statement_insert && auto_ (m)))
+ return false;
+ }
+
+ if (sk_ == statement_update &&
+ readonly (member_path_, member_scope_))
+ return false;
+
+ if ((sk_ == statement_insert || sk_ == statement_update) &&
+ version (m))
+ return false;
+
+ if (!first)
+ os << ',' << endl;
+
+ os << oids[parse_sql_type (column_type (), m).type];
+
+ return true;
+ }
+
+ private:
+ statement_kind sk_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = " <<
+ integer_buffer_types[mi.st->type - sql_type::BOOLEAN] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".type = " <<
+ float_buffer_types[mi.st->type - sql_type::REAL] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::numeric;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << b << ".type = " <<
+ date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::bit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = sizeof (" << arg << "." << mi.var << "value);"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::varbit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::uuid;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_uuid (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = true;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Presented as byte.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ // Presented as bytea.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ string r;
+
+ if (p == persist_after_values)
+ {
+ data_member_path* id (id_member (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // Top-level auto id.
+ //
+ if (id != 0 && !poly_derived && auto_ (*id))
+ r = "RETURNING " +
+ convert_from (column_qname (*id), *id->back ());
+ }
+
+ return r;
+ }
+
+ virtual void
+ object_extra (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::object_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "persist_statement_name[] = " <<
+ strlit (statement_name ("persist", fn, c)) << ";"
+ << endl;
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ {
+ os << "const char* const " << traits << "::" << endl
+ << "find_statement_names[] ="
+ << "{";
+
+ for (size_t i (0), n (abst ? 1 : polymorphic_depth (c));
+ i < n;
+ ++i)
+ {
+ if (i != 0)
+ os << "," << endl;
+
+ ostringstream ostr;
+ ostr << "find_" << i;
+ os << strlit (statement_name (ostr.str (), fn, c));
+ }
+
+ os << "};";
+ }
+ else
+ os << "const char " << traits << "::" << endl
+ << "find_statement_name[] = " <<
+ strlit (statement_name ("find", fn, c)) << ";"
+ << endl;
+
+ if (poly && !poly_derived)
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement_name[] = " <<
+ strlit (statement_name ("find_discriminator", fn, c)) << ";"
+ << endl;
+
+ if (update_columns != 0)
+ os << "const char " << traits << "::" << endl
+ << "update_statement_name[] = " <<
+ strlit (statement_name ("update", fn, c)) << ";"
+ << endl;
+
+ os << "const char " << traits << "::" << endl
+ << "erase_statement_name[] = " <<
+ strlit (statement_name ("erase", fn, c)) << ";"
+ << endl;
+
+ if (optimistic != 0)
+ os << "const char " << traits << "::" << endl
+ << "optimistic_erase_statement_name[] = " <<
+ strlit (statement_name ("erase_optimistic", fn, c)) << ";"
+ << endl;
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ {
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl
+ << "const char " << traits << "::" << endl
+ << "erase_query_statement_name[] = " <<
+ strlit (statement_name ("erase_query", fn, c)) << ";"
+ << endl;
+ }
+
+ // Statement types.
+ //
+
+ // persist_statement_types.
+ //
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "persist_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_insert);
+ st.traverse (c);
+
+ // Empty array is not portable. So add a dummy member if we
+ // are not sending anything with the insert statement.
+ //
+ if (cc.total == cc.inverse + cc.optimistic_managed +
+ (id != 0 && !poly_derived && auto_ (*id) ? cc.id : 0))
+ os << "0";
+
+ os << "};";
+ }
+
+ // find_statement_types.
+ //
+ if (id != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "find_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_select, true);
+ st.traverse (*id);
+
+ os << "};";
+ }
+
+ // update_statement_types.
+ //
+ if (id != 0 && update_columns != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "update_statement_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &main_section);
+ st.traverse (c);
+ }
+
+ // Not the same as update_columns.
+ //
+ bool first (cc.total == cc.id + cc.inverse + cc.readonly +
+ cc.separate_update + cc.optimistic_managed);
+
+ statement_oids st (statement_where, first);
+ st.traverse (*id);
+
+ if (optimistic != 0)
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+
+ if (id != 0 && optimistic != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "optimistic_erase_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_where);
+ st.traverse (*id);
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+ }
+
+ virtual void
+ extra_statement_cache_extra_args (bool c, bool s)
+ {
+ bool u (c || s);
+
+ os << "," << endl
+ << db << "::native_binding&" << (u ? " idn" : "") << "," << endl
+ << "const unsigned int*" << (u ? " idt" : "");
+ }
+
+ virtual void
+ view_extra (type& c)
+ {
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::view_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl;
+ }
+
+ virtual void
+ object_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << "text," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ object_erase_query_statement_ctor_args (type&)
+ {
+ os << "conn," << endl
+ << "erase_query_statement_name," << endl
+ << "text," << endl
+ << "q.parameter_types ()," << endl
+ << "q.parameter_count ()," << endl
+ << "q.parameters_binding ()";
+ }
+
+ virtual void
+ view_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << q << ".clause ()," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ post_query_ (type&, bool once_off)
+ {
+ if (once_off)
+ os << "st->deallocate ();";
+ }
+ };
+ entry<class_> class_entry_;
+
+ struct container_traits : relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_extra (semantics::data_member& m, semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ container_kind_type ck (container_kind (t));
+
+ string const& pn (public_name (m));
+ string scope (scope_ + "::" + flat_prefix_ + pn + "_traits");
+
+ data_member_path* imp (inverse (m, "value"));
+ bool inv (imp != 0);
+
+ bool smart (!inv && !unordered (m) && container_smart (t));
+
+ // Statment names.
+ //
+
+ // Prefix top-object name to avoid conflicts with inherited
+ // member statement names.
+ //
+ string fn (
+ flat_name (
+ class_fq_name (*top_object) + "_" + flat_prefix_ + pn));
+
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, m)) << ";"
+ << endl
+ << "const char " << scope << "::" << endl
+ << "insert_name[] = " <<
+ strlit (statement_name ("insert", fn, m)) << ";"
+ << endl;
+
+ if (smart)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, m)) << ";"
+ << endl;
+
+ os << "const char " << scope << "::" << endl
+ << "delete_name[] = " <<
+ strlit (statement_name ("delete", fn, m)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+
+ semantics::type& vt (container_vt (m));
+ semantics::type& idt (container_idt (m));
+
+ // insert statement types.
+ //
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "insert_types[] ="
+ << "{";
+
+ if (!inv)
+ {
+ statement_oids so (statement_insert);
+
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ so.traverse (m, container_kt (m), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ so.traverse (m, vt, "value", "value");
+ }
+ else
+ // MSVC does not allow zero length arrays or uninitialized
+ // non-extern const values.
+ //
+ os << "0";
+
+ os << "};";
+ }
+
+ // update statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ // Use insert instead of update to include read-only members.
+ //
+ statement_oids so (statement_insert);
+ so.traverse (m, vt, "value", "value");
+ }
+
+ statement_oids so (statement_where, false);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+
+ // delete statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "delete_types[] ="
+ << "{";
+
+ statement_oids so (statement_where);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits : relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_extra (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ semantics::data_member* opt (optimistic (c_));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ string name (public_name (*s.member));
+ string scope (scope_ + "::" + name + "_traits");
+
+ // Statment names.
+ //
+
+ // Prefix object name to avoid conflicts with inherited member
+ // statement names.
+ //
+ string fn (flat_name (class_fq_name (c_) + "_" + name));
+
+ if (load || load_opt)
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, *s.member)) << ";"
+ << endl;
+
+ if (update || update_opt)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, *s.member)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &s);
+ st.traverse (c_);
+ }
+
+ statement_oids st (statement_where, !update);
+ st.traverse (*id_member (c_));
+
+ if (s.optimistic ()) // Note: not update_opt.
+ st.traverse (*opt);
+
+ os << "};";
+ }
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct container_cache_init_members:
+ relational::container_cache_init_members
+ {
+ container_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<container_cache_init_members> container_cache_init_members_;
+
+ struct section_cache_init_members:
+ relational::section_cache_init_members
+ {
+ section_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<section_cache_init_members> section_cache_init_members_;
+ }
+ }
+}
diff --git a/odb/odb/relational/processor.cxx b/odb/odb/relational/processor.cxx
new file mode 100644
index 0000000..0f60359
--- /dev/null
+++ b/odb/odb/relational/processor.cxx
@@ -0,0 +1,1564 @@
+// file : odb/relational/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <vector>
+#include <algorithm>
+
+#include <odb/diagnostics.hxx>
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/common.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace
+ {
+ // Indirect (dynamic) context values.
+ //
+ static string
+ id_column_type ()
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return id.back ()->get<string> ("column-id-type");
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (t, whint)))
+ wt = &utype (*wt, whint);
+
+ // Determine the member kind.
+ //
+ enum {simple, composite, container, unknown} kind (unknown);
+
+ // See if this is a composite value type.
+ //
+ if (composite_wrapper (t))
+ kind = composite;
+
+ // If not, see if it is a simple value.
+ //
+ if (kind == unknown)
+ {
+ string type, id_type;
+
+ if (m.count ("id-type"))
+ id_type = m.get<string> ("id-type");
+
+ if (m.count ("type"))
+ {
+ type = m.get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+ }
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ // An object pointer in view doesn't really have a "column"
+ // so pretend that it has already been handled.
+ //
+ if (view_member (m))
+ kind = simple;
+ else
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // The id type can be a composite value type.
+ //
+ if (composite_wrapper (idt))
+ kind = composite;
+ else
+ {
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest should be identical to the code for the id_type in
+ // the else block.
+ //
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+
+ id_type = type;
+ }
+ }
+ }
+ else
+ {
+ if (id_type.empty () && t.count ("id-type"))
+ id_type = t.get<string> ("id-type");
+
+ if (id_type.empty () && wt != 0 && wt->count ("id-type"))
+ id_type = wt->get<string> ("id-type");
+
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+
+ if (id_type.empty ())
+ id_type = database_type (t, hint, true);
+
+ if (id_type.empty () && wt != 0)
+ id_type = database_type (*wt, whint, true);
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, false, &null);
+
+ // Use id mapping for discriminators.
+ //
+ if (id (m) || discriminator (m))
+ type = id_type;
+ // Allow NULL if requested by the default mapping.
+ //
+ else if (null && !m.count ("not-null"))
+ m.set ("null", true);
+ }
+
+ if (kind == unknown && !type.empty ())
+ {
+ m.set ("column-type", type);
+ m.set ("column-id-type", id_type);
+
+ // Issue a warning if we are relaxing null-ness.
+ //
+ if (m.count ("null") && t.count ("not-null"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " warning: data member declared null while its type is "
+ << "declared not null" << endl;
+ }
+
+ kind = simple;
+ }
+ }
+
+ // If not a simple value, see if this is a container.
+ //
+ if (kind == unknown && context::container (m))
+ {
+ process_container (m, (wt != 0 ? *wt : t));
+ kind = container;
+ }
+
+ // If it is none of the above then we have an error.
+ //
+ if (kind == unknown)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << t.fq_name (hint)
+ << "' used in data member '" << m.name () << "' to a "
+ << db.name () << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db type' to specify the database type"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ if (m.count ("polymorphic-ref"))
+ {
+ // Copy the column name from the root's id member, if specified.
+ //
+ {
+ semantics::class_& r (*object_pointer (t));
+ semantics::data_member& id (*id_member (r)->front ());
+
+ if (id.count ("column"))
+ m.set ("column", id.get<table_column> ("column"));
+ }
+
+ m.set ("not-null", true);
+ m.set ("deferrable",
+ sema_rel::deferrable (sema_rel::deferrable::not_deferrable));
+ m.set ("on-delete", sema_rel::foreign_key::cascade);
+ }
+
+ process_index (m);
+ }
+
+ // Convert index/unique specifiers to the index entry in the object.
+ //
+ void
+ process_index (semantics::data_member& m)
+ {
+ bool ip (m.count ("index"));
+ bool up (m.count ("unique"));
+
+ if (ip || up)
+ {
+ using semantics::class_;
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ index in;
+ in.loc = m.get<location_t> (
+ ip ? "index-location" : "unique-location");
+
+ if (up)
+ in.type = "UNIQUE";
+
+ index::member im;
+ im.loc = in.loc;
+ im.name = m.name ();
+ im.path.push_back (&m);
+ in.members.push_back (im);
+
+ // Insert it in the location order.
+ //
+ ins.insert (
+ lower_bound (ins.begin (), ins.end (), in, index_comparator ()),
+ in);
+ }
+ }
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::names* hint,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ semantics::names* wh (0);
+ semantics::type* wt (wrapper (t, wh));
+
+ string type;
+ semantics::type& ct (utype (m));
+
+ // Custom mapping can come from these places (listed in the order
+ // of priority): member, container type, value type. To complicate
+ // things a bit, for object references, it can also come from the
+ // member and value type of the id member.
+ //
+ if (m.count (prefix + "-type"))
+ type = m.get<string> (prefix + "-type");
+
+ if (type.empty () && ct.count (prefix + "-type"))
+ type = ct.get<string> (prefix + "-type");
+
+ semantics::class_* c;
+ if (obj_ptr && (c = object_pointer (t)))
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // Nothing to do if this is a composite value type.
+ //
+ if (composite_wrapper (idt))
+ return;
+
+ semantics::type* wt (0);
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest of the code is identical to the else block except here
+ // we have to check for "id-type" before checking for "type".
+ //
+
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+ }
+ else
+ {
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, wh, false, &null);
+
+ // Allow NULL if requested by the default mapping.
+ //
+ if (null && !m.count (prefix + "-not-null"))
+ m.set (prefix + "-null", true);
+ }
+
+ if (!type.empty ())
+ {
+ m.set (prefix + "-column-type", type);
+ m.set (prefix + "-column-id-type", type);
+ return;
+ }
+
+ // We do not support nested containers so skip that test.
+ //
+
+ // If it is none of the above then we have an error.
+ //
+ string fq_type (t.fq_anonymous () ? "<anonymous>" : t.fq_name ());
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << fq_type << "' used in "
+ << "data member '" << m.name () << "' to a " << db.name ()
+ << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db " << prefix << "_type' to specify the "
+ << "database type" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ container_kind_type ck (t.get<container_kind_type> ("container-kind"));
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ semantics::type* vt (&utype (m, vh, "value"));
+ semantics::type* it (ck == ck_ordered ? &utype (m, ih, "index") : 0);
+ semantics::type* kt (ck == ck_map || ck == ck_multimap
+ ? &utype (m, kh, "key")
+ : 0);
+
+ // Process member data.
+ //
+ m.set ("id-column-type", &id_column_type);
+
+ process_container_value (*vt, vh, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, ih, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, kh, m, "key", true);
+ }
+ };
+
+ //
+ //
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ query_ (c.get<view_query> ("query")),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map"))
+ {
+ }
+
+ struct assoc_member
+ {
+ semantics::data_member* m;
+ view_object* vo;
+ };
+
+ typedef vector<assoc_member> assoc_members;
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ // Object pointers are associated with objects.
+ //
+ if (object_pointer (t))
+ return;
+
+ data_member* src_m (0); // Source member.
+
+ // Resolve member references in column expressions.
+ //
+ if (m.count ("column"))
+ {
+ // Column literal.
+ //
+ if (query_.kind != view_query::condition)
+ {
+ warn (m.get<location_t> ("column-location"))
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ }
+
+ return;
+ }
+ else if (m.count ("column-expr"))
+ {
+ column_expr& e (m.get<column_expr> ("column-expr"));
+
+ if (query_.kind != view_query::condition)
+ {
+ warn (e.loc)
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ return;
+ }
+
+ for (column_expr::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ // This code is quite similar to translate_expression in the
+ // source generator.
+ //
+ try
+ {
+ using semantics::scope;
+ using semantics::class_;
+
+ if (i->kind != column_expr_part::reference)
+ continue;
+
+ lex_.start (i->value);
+
+ string tl;
+ tree tn;
+ cpp_ttype tt (lex_.next (tl, &tn));
+
+ data_member* m (0);
+ view_object* vo (0);
+
+ // Check if this is an alias.
+ //
+ if (tt == CPP_NAME)
+ {
+ view_alias_map::iterator j (amap_.find (tl));
+
+ if (j != amap_.end ())
+ {
+ vo = j->second;
+
+ // Skip '::'.
+ //
+ if (lex_.next (tl, &tn) != CPP_SCOPE)
+ {
+ error (i->loc) << "member name expected after an alias " <<
+ "in db pragma column" << endl;
+ throw operation_failed ();
+ }
+
+ if (lex_.next (tl, &tn) != CPP_NAME)
+ throw lookup::invalid_name ();
+
+ m = &vo->obj->lookup<data_member> (
+ tl, scope::include_hidden);
+
+ tt = lex_.next (tl, &tn);
+ }
+ }
+
+ // If it is not an alias, do the normal lookup.
+ //
+ if (vo == 0)
+ {
+ // Also get the object type. We need to do it so that
+ // we can get the correct (derived) table name (the
+ // member itself can come from a base class).
+ //
+ scope* s;
+ string name;
+ cpp_ttype ptt; // Not used.
+ m = &lookup::resolve_scoped_name<data_member> (
+ lex_, tt, tl, tn, ptt,
+ dynamic_cast<scope&> (*unit.find (i->scope)),
+ name,
+ false,
+ &s);
+
+ view_object_map::iterator j (
+ omap_.find (dynamic_cast<class_*> (s)));
+
+ if (j == omap_.end ())
+ {
+ error (i->loc) << "name '" << name << "' in db pragma " <<
+ "column does not refer to a data member of a " <<
+ "persistent class that is used in this view" << endl;
+ throw operation_failed ();
+ }
+
+ vo = j->second;
+ }
+
+ i->member_path.push_back (m);
+
+ // Figure out the table name/alias for this member.
+ //
+ if (class_* root = polymorphic (*vo->obj))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (&static_cast<class_&> (m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (vo->alias.empty ())
+ i->table = t;
+ else
+ i->table = qname (vo->alias + "_" + t.uname ());
+ }
+ else
+ i->table = vo->alias.empty ()
+ ? table_name (*vo->obj)
+ : qname (vo->alias);
+
+ // Finally, resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = lex_.next (tl, &tn))
+ {
+ lex_.next (tl, &tn); // Get CPP_NAME.
+
+ // Check that the outer member is composite and also
+ // unwrap it while at it.
+ //
+ class_* comp (composite_wrapper (utype (*m)));
+ if (comp == 0)
+ {
+ error (i->loc) << "data member '" << m->name () << "' " <<
+ "specified in db pragma column is not composite" << endl;
+ throw operation_failed ();
+ }
+
+ m = &comp->lookup<data_member> (tl, class_::include_hidden);
+ i->member_path.push_back (m);
+ }
+
+ // If the expression is just this reference, then we have
+ // a source member.
+ //
+ if (e.size () == 1)
+ src_m = m;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (i->loc) << "invalid name in db pragma column" << endl;
+ throw operation_failed ();
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (i->loc) << "name '" << e.name << "' in db pragma " <<
+ "column does not refer to a data member" << endl;
+ else
+ error (i->loc) << "unable to resolve data member '" <<
+ e.name << "' specified with db pragma column" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (i->loc) << "data member name '" << e.first.name () <<
+ "' specified with db pragma column is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to " <<
+ "this data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve " <<
+ "to this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Check that the source member is not transient or inverse. Also
+ // check that the C++ types are the same (sans cvr-qualification
+ // and wrapping) and issue a warning if they differ. In rare cases
+ // where this is not a mistake, the user can use a phony expression
+ // (e.g., "" + person:name) to disable the warning. Note that in
+ // this case there will be no type pragma copying, which is probably
+ // ok seeing that the C++ types are different.
+ //
+ //
+ if (src_m != 0)
+ {
+ string reason;
+
+ if (transient (*src_m))
+ reason = "transient";
+ else if (inverse (*src_m))
+ reason = "inverse";
+
+ if (!reason.empty ())
+ {
+ error (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column is " << reason << endl;
+ throw operation_failed ();
+ }
+
+ if (!member_resolver::check_types (utype (*src_m), utype (m)))
+ {
+ warn (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column has a different type compared to the "
+ << "view data member" << endl;
+
+ info (src_m->file (), src_m->line (), src_m->column ())
+ << "object data member is defined here" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "view data member is defined here" << endl;
+ }
+ }
+ }
+ // This member has no column information. If we are generating our
+ // own query, try to find a member with the same (or similar) name
+ // in one of the associated objects.
+ //
+ else if (query_.kind == view_query::condition)
+ {
+ view_objects& objs (view_.get<view_objects> ("objects"));
+
+ assoc_members exact_members, pub_members;
+ member_resolver resolver (exact_members, pub_members, m);
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind == view_object::object)
+ resolver.traverse (*i);
+ }
+
+ assoc_members& members (
+ !exact_members.empty () ? exact_members : pub_members);
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (members.empty ())
+ {
+ error (m.file (), m.line (), m.column ())
+ << "unable to find a corresponding data member for '"
+ << m.name () << "' in any of the associated objects" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to specify the corresponding data "
+ << "member or column name" << endl;
+
+ throw operation_failed ();
+ }
+ else if (members.size () > 1)
+ {
+ error (m.file (), m.line (), m.column ())
+ << "corresponding data member for '" << m.name () << "' is "
+ << "ambiguous" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "candidates are:" << endl;
+
+ for (assoc_members::const_iterator i (members.begin ());
+ i != members.end ();
+ ++i)
+ {
+ info (i->m->file (), i->m->line (), i->m->column ())
+ << " '" << i->m->name () << "' in object '"
+ << i->vo->name () << "'" << endl;
+ }
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the column expression for this member.
+ //
+ assoc_member const& am (members.back ());
+
+ column_expr& e (m.set ("column-expr", column_expr ()));
+ e.push_back (column_expr_part ());
+ column_expr_part& ep (e.back ());
+
+ ep.kind = column_expr_part::reference;
+
+
+ // If this object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding table.
+ //
+ using semantics::class_;
+
+ if (class_* root = polymorphic (*am.vo->obj))
+ {
+ class_* c (&static_cast<class_&> (am.m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (am.vo->alias.empty ())
+ ep.table = t;
+ else
+ ep.table = qname (am.vo->alias + "_" + t.uname ());
+ }
+ else
+ ep.table = am.vo->alias.empty ()
+ ? table_name (*am.vo->obj)
+ : qname (am.vo->alias);
+
+ ep.member_path.push_back (am.m);
+
+ src_m = am.m;
+ }
+
+ // If we have the source member and don't have the type pragma of
+ // our own, but the source member does, then copy the columnt type
+ // over. In case the source member is a pointer, also check the id
+ // member.
+ //
+ if (src_m != 0 && !m.count ("type"))
+ {
+ if (src_m->count ("type"))
+ m.set ("column-type", src_m->get<string> ("column-type"));
+ else if (semantics::class_* c = object_pointer (utype (*src_m)))
+ {
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ if (id.count ("type"))
+ m.set ("column-type", id.get<string> ("column-type"));
+ }
+ }
+
+ // Check the return statements above if you add any extra logic
+ // here.
+ }
+
+ struct member_resolver: traversal::class_
+ {
+ member_resolver (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : member_ (members, pub_members, m)
+ {
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+ void
+ traverse (view_object& vo)
+ {
+ member_.vo_ = &vo;
+
+ // First look for an exact match.
+ //
+ {
+ member_.exact_ = true;
+ member_.found_ = false;
+ traverse (*vo.obj);
+ }
+
+ // If we didn't find an exact match, then look for a public
+ // name match.
+ //
+ if (!member_.found_)
+ {
+ member_.exact_ = false;
+ traverse (*vo.obj);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c))
+ return; // Ignore transient bases.
+
+ names (c);
+
+ // If we already found a match in one of the derived classes,
+ // don't go into bases to get the standard "hiding" behavior.
+ //
+ if (!member_.found_)
+ inherits (c);
+ }
+
+ public:
+ static bool
+ check_types (semantics::type& ot, semantics::type& vt)
+ {
+ using semantics::type;
+
+ // Require that the types be the same sans the wrapping and
+ // cvr-qualification. If the object member type is a pointer,
+ // use the id type of the pointed-to object.
+ //
+ type* t1;
+
+ if (semantics::class_* c = object_pointer (ot))
+ t1 = &utype (*id_member (*c));
+ else
+ t1 = &ot;
+
+ type* t2 (&vt);
+
+ if (type* wt1 = context::wrapper (*t1))
+ t1 = &utype (*wt1);
+
+ if (type* wt2 = context::wrapper (*t2))
+ t2 = &utype (*wt2);
+
+ if (t1 != t2)
+ return false;
+
+ return true;
+ }
+
+ private:
+ struct data_member: traversal::data_member
+ {
+ data_member (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : members_ (members),
+ pub_members_ (pub_members),
+ name_ (m.name ()),
+ pub_name_ (context::current ().public_name (m)),
+ type_ (utype (m))
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ if (exact_)
+ {
+ if (name_ == m.name () && check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ members_.push_back (am);
+ found_ = true;
+ }
+ }
+ else
+ {
+ if (pub_name_ == context::current ().public_name (m) &&
+ check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ pub_members_.push_back (am);
+ found_ = true;
+ }
+ }
+ }
+
+ bool
+ check (semantics::data_member& m)
+ {
+ // Make sure that the found node can possibly match.
+ //
+ if (context::transient (m) ||
+ context::inverse (m) ||
+ m.count ("polymorphic-ref"))
+ return false;
+
+ return check_types (utype (m), type_);
+ }
+
+ assoc_members& members_;
+ assoc_members& pub_members_;
+
+ string name_;
+ string pub_name_;
+ semantics::type& type_;
+
+ view_object* vo_;
+ bool exact_;
+ bool found_;
+ };
+
+ traversal::names names_;
+ data_member member_;
+ traversal::inherits inherits_;
+ };
+
+ private:
+ semantics::class_& view_;
+ view_query& query_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ cxx_string_lexer lex_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object (c);
+ else if (k == class_view)
+ traverse_view (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Remove the bulk pragma if this database doesn't support bulk
+ // operations.
+ //
+ if (c.count ("bulk") && !generate_bulk)
+ c.remove ("bulk");
+
+ // Process indexes. Here we need to do two things: resolve member
+ // names to member paths and assign names to unnamed indexes. We
+ // are also going to handle the special container indexes.
+ //
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end ();)
+ {
+ index& in (*i);
+
+ // This should never happen since a db index pragma without
+ // the member specifier will be treated as a member pragma.
+ //
+ assert (!in.members.empty ());
+
+ // First resolve member names.
+ //
+ index::members_type::iterator j (in.members.begin ());
+ for (; j != in.members.end (); ++j)
+ {
+ index::member& im (*j);
+
+ if (!im.path.empty ())
+ continue; // Already resolved.
+
+ im.path = resolve_data_members (c, im.name, im.loc, lex_);
+
+ if (container (*im.path.back ()))
+ break;
+ }
+
+ // Add the table prefix if this database has global index names.
+ //
+ if (!in.name.empty () && global_index)
+ in.name = table_name_prefix (class_scope (c)) + in.name;
+
+ // Handle container indexes.
+ //
+ if (j != in.members.end ())
+ {
+ // Do some sanity checks.
+ //
+ if (in.members.size () != 1)
+ {
+ error (in.loc) << "multiple data members specified for a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string tl;
+ if (lex_.next (tl) != CPP_DOT || lex_.next (tl) != CPP_NAME ||
+ (tl != "id" && tl != "index"))
+ {
+ error (j->loc) << ".id or .index special member expected in a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string n (tl);
+
+ if (lex_.next (tl) != CPP_EOF)
+ {
+ error (j->loc) << "unexpected text after ." << n << " in "
+ << "db pragma member" << endl;
+ throw operation_failed ();
+ }
+
+ // Move this index to the container member.
+ //
+ j->path.back ()->set (n + "-index", *i);
+ i = ins.erase (i);
+ continue;
+ }
+
+ // Now assign the name if the index is unnamed. We have to
+ // add table name as a prefix here since there is not way
+ // to distinguish between user-assigned and auto-derived
+ // names in the model.
+ //
+ if (in.name.empty ())
+ {
+ // Make sure there is only one member.
+ //
+ if (in.members.size () > 1)
+ {
+ error (in.loc) << "unnamed index with more than one data "
+ << "member" << endl;
+ throw operation_failed ();
+ }
+
+ // Generally, we want the index name to be based on the column
+ // name. This is straightforward for single-column members. In
+ // case of a composite member, we will need to use the column
+ // prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can
+ // be empty, in which case we will just fall back on the
+ // member's public name.
+ //
+ string n (column_prefix (in.members.front ().path, true).prefix);
+
+ if (n.empty ())
+ n = public_name_db (*in.members.front ().path.back ());
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+
+ in.name = index_name (table_name (c), n);
+ }
+
+ ++i;
+ }
+ }
+
+ //
+ // View.
+ //
+
+ struct relationship
+ {
+ semantics::data_member* member;
+ string name;
+ view_object* pointer;
+ view_object* pointee;
+ };
+
+ typedef vector<relationship> relationships;
+
+ virtual void
+ traverse_view (type& c)
+ {
+ bool has_q (c.count ("query"));
+ bool has_o (c.count ("objects"));
+
+ // Determine the kind of query template we've got.
+ //
+ view_query& vq (has_q
+ ? c.get<view_query> ("query")
+ : c.set ("query", view_query ()));
+ if (has_q)
+ {
+ if (!vq.literal.empty ())
+ {
+ string q (upcase (vq.literal));
+
+ //@@ We need to recognize database-specific list of prefixes. For
+ // example, PG has WITH. Alternatively (or in addition) we could
+ // do the same comment trick (e.g., /*SELECT*/ to treat it as a
+ // SELECT-like queiry).
+ //
+ if (q.compare (0, 7, "SELECT ") == 0)
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 ||
+ q.compare (0, 5, "CALL ") == 0 ||
+ q.compare (0, 8, "EXECUTE ") == 0)
+ vq.kind = view_query::complete_execute;
+ //
+ // Hint for databases that use SELECT for stored procedure
+ // calls (e.g., PostgreSQL).
+ //
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.literal = string (vq.literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else if (!vq.expr.empty ())
+ {
+ // If the first token in the expression is a string and
+ // it starts with "SELECT " or is equal to "SELECT" or
+ // one of the stored procedure call keywords, then we
+ // have a complete query.
+ //
+ if (vq.expr.front ().type == CPP_STRING)
+ {
+ string q (upcase (vq.expr.front ().literal));
+
+ if (q.compare (0, 7, "SELECT ") == 0 || q == "SELECT")
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 || q == "EXEC" ||
+ q.compare (0, 5, "CALL ") == 0 || q == "CALL" ||
+ q.compare (0, 8, "EXECUTE ") == 0 || q == "EXECUTE")
+ vq.kind = view_query::complete_execute;
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.expr.front ().literal =
+ string (vq.expr.front ().literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = (vq.distinct || vq.for_update)
+ ? view_query::condition // The query(distinct) case.
+ : view_query::runtime;
+ }
+ else
+ vq.kind = has_o ? view_query::condition : view_query::runtime;
+
+ if ((vq.distinct || vq.for_update) && vq.kind != view_query::condition)
+ {
+ error (vq.loc)
+ << "result modifier specified for "
+ << (vq.kind == view_query::runtime ? "runtime" : "native")
+ << " query" << endl;
+
+ throw operation_failed ();
+ }
+
+ // We cannot have an incomplete query if there are not objects
+ // to derive the rest from.
+ //
+ if (vq.kind == view_query::condition && !has_o)
+ {
+ error (c.file (), c.line (), c.column ())
+ << "view '" << class_fq_name (c) << "' has an incomplete query "
+ << "template and no associated objects" << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "use db pragma query to provide a complete query template"
+ << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "or use db pragma object to associate one or more objects "
+ << "with the view"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Process join conditions.
+ //
+ if (has_o)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i == objs.begin () && i->join != view_object::left)
+ {
+ error (i->loc)
+ << "no join type can be specified for the first associated "
+ << (i->kind == view_object::object ? "object" : "table")
+ << endl;
+ throw operation_failed ();
+ }
+
+ if (i->kind != view_object::object)
+ {
+ // Make sure we have join conditions for tables unless it
+ // is the first entry.
+ //
+ if (i != objs.begin () && i->cond.empty ())
+ {
+ error (i->loc)
+ << "missing join condition in db pragma table" << endl;
+
+ throw operation_failed ();
+ }
+
+ continue;
+ }
+
+ // If we have to generate the query and there was no JOIN
+ // condition specified by the user, try to come up with one
+ // automatically based on object relationships. CROSS JOIN
+ // has no condition.
+ //
+ if (vq.kind == view_query::condition &&
+ i->cond.empty () &&
+ i != objs.begin () &&
+ i->join != view_object::cross)
+ {
+ relationships rs;
+
+ // Check objects specified prior to this one for any
+ // relationships. We don't examine objects that were
+ // specified after this one because that would require
+ // rearranging the JOIN order.
+ //
+ for (view_objects::iterator j (objs.begin ()); j != i; ++j)
+ {
+ if (j->kind != view_object::object)
+ continue; // Skip tables.
+
+ // First see if any of the objects that were specified
+ // prior to this object point to it.
+ //
+ {
+ relationship_resolver r (rs, *i, true);
+ r.traverse (*j);
+ }
+
+ // Now see if this object points to any of the objects
+ // specified prior to it.
+ //
+ {
+ relationship_resolver r (rs, *j, false);
+ r.traverse (*i);
+ }
+ }
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (rs.empty ())
+ {
+ error (i->loc)
+ << "unable to find an object relationship involving "
+ << "object '" << i->name () << "' and any of the previously "
+ << "associated objects" << endl;
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to specify a custom join condition" << endl;
+
+ throw operation_failed ();
+ }
+ else if (rs.size () > 1)
+ {
+ error (i->loc)
+ << "object relationship for object '" << i->name () << "' "
+ << "is ambiguous" << endl;
+
+ info (i->loc)
+ << "candidates are:" << endl;
+
+ for (relationships::const_iterator j (rs.begin ());
+ j != rs.end ();
+ ++j)
+ {
+ semantics::data_member& m (*j->member);
+
+ info (m.file (), m.line (), m.column ())
+ << " '" << j->name << "' "
+ << "in object '" << j->pointer->name () << "' "
+ << "pointing to '" << j->pointee->name () << "'"
+ << endl;
+ }
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the condition.
+ //
+ relationship const& r (rs.back ());
+
+ string name (r.pointer->alias.empty ()
+ ? class_fq_name (*r.pointer->obj)
+ : r.pointer->alias);
+ name += "::";
+ name += r.name;
+
+ lex_.start (name);
+
+ string t;
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ i->cond.push_back (cxx_token (lex_.location (), tt, t));
+ }
+ }
+ }
+ }
+
+ // Handle forced versioning. When versioning is forced, ignore
+ // it for native views.
+ //
+ if (force_versioned && vq.kind == view_query::condition)
+ c.set ("versioned", true);
+
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+ }
+
+ struct relationship_resolver: object_members_base
+ {
+ relationship_resolver (relationships& rs,
+ view_object& pointee,
+ bool forward)
+ // Look in polymorphic bases only for previously-associated
+ // objects since backward pointers from bases will result in
+ // the pathological case (we will have to join the base table
+ // first, which means we will get both bases and derived objects
+ // instead of just derived).
+ //
+ : object_members_base (false, false, true, forward),
+ relationships_ (rs),
+ // Ignore self-references if we are looking for backward
+ // pointers since they were already added to the list in
+ // the previous pass.
+ //
+ self_pointer_ (forward),
+ pointer_ (0),
+ pointee_ (pointee)
+ {
+ }
+
+ void
+ traverse (view_object& pointer)
+ {
+ pointer_ = &pointer;
+ object_members_base::traverse (*pointer.obj);
+ }
+
+ using object_members_base::traverse; // Unhide.
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Ignore inverse sides of the same relationship to avoid
+ // phony conflicts caused by the direct side that will end
+ // up in the relationship list as well. Unless the inverse
+ // member is in the polymorphic base in which case we will
+ // miss it since we don't examine inside poly bases on the
+ // backwards scan (see above).
+ //
+ if (data_member_path* imp = inverse (m))
+ {
+ if (&imp->front ()->scope () == &c) // Direct member.
+ return;
+ }
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == &c)
+ return;
+
+ if (pointee_.obj == &c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ if (semantics::class_* c =
+ object_pointer (context::container_vt (m)))
+ {
+ if (inverse (m, "value"))
+ return;
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == c)
+ return;
+
+ if (pointee_.obj == c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+ }
+
+ private:
+ relationships& relationships_;
+ bool self_pointer_;
+ view_object* pointer_;
+ view_object& pointee_;
+ };
+
+ private:
+ cxx_string_lexer lex_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+ };
+ }
+
+ void
+ process ()
+ {
+ context ctx;
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+}
diff --git a/odb/odb/relational/processor.hxx b/odb/odb/relational/processor.hxx
new file mode 100644
index 0000000..71b8643
--- /dev/null
+++ b/odb/odb/relational/processor.hxx
@@ -0,0 +1,15 @@
+// file : odb/relational/processor.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PROCESSOR_HXX
+#define ODB_RELATIONAL_PROCESSOR_HXX
+
+namespace relational
+{
+ // Issues diagnostics and throws operation_failed in case of an error.
+ //
+ void
+ process ();
+}
+
+#endif // ODB_RELATIONAL_PROCESSOR_HXX
diff --git a/odb/odb/relational/schema-source.cxx b/odb/odb/relational/schema-source.cxx
new file mode 100644
index 0000000..5659485
--- /dev/null
+++ b/odb/odb/relational/schema-source.cxx
@@ -0,0 +1,281 @@
+// file : odb/relational/schema-source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema-source.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace schema
+ {
+ void
+ generate_source (sema_rel::changelog* log)
+ {
+ context ctx;
+ ostream& os (ctx.os);
+ database db (ctx.db);
+ options const& ops (ctx.options);
+ sema_rel::model& model (*ctx.model);
+ string const& schema_name (ops.schema_name ()[db]);
+
+ if (log != 0 && ops.suppress_migration ())
+ log = 0;
+
+ bool schema_version (
+ model.version () != 0 && !ctx.options.suppress_schema_version ());
+
+ instance<cxx_emitter> emitter;
+ emitter_ostream emitter_os (*emitter);
+ schema_format format (schema_format::embedded);
+
+ if (!model.names_empty () || log != 0 || schema_version)
+ os << "namespace odb"
+ << "{";
+
+ // Schema.
+ //
+ if (!model.names_empty () || schema_version)
+ {
+ os << "static bool" << endl
+ << "create_schema (database& db, unsigned short pass, bool drop)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (pass);"
+ << "ODB_POTENTIALLY_UNUSED (drop);"
+ << endl;
+
+ // Drop.
+ //
+ if (!ops.omit_drop ())
+ {
+ bool close (false);
+
+ os << "if (drop)"
+ << "{";
+
+ instance<drop_model> dmodel (*emitter, emitter_os, format);
+ instance<drop_table> dtable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ dmodel >> names >> dtable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ dmodel->pass (pass);
+ dtable->pass (pass);
+
+ dmodel->traverse (model);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (schema_version)
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->create_table ();
+ vt->drop ();
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ // Create.
+ //
+ if (!ops.omit_create ())
+ {
+ bool close (false);
+
+ if (ops.omit_drop ())
+ os << "if (!drop)";
+ else
+ os << "else";
+
+ os << "{";
+
+ instance<create_model> cmodel (*emitter, emitter_os, format);
+ instance<create_table> ctable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ cmodel >> names >> ctable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ cmodel->pass (pass);
+ ctable->pass (pass);
+
+ cmodel->traverse (model);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (schema_version)
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->create_table ();
+ vt->create (model.version ());
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ os << "return false;"
+ << "}";
+
+ os << "static const schema_catalog_create_entry" << endl
+ << "create_schema_entry_ (" << endl
+ << "id_" << db << "," << endl
+ << context::strlit (schema_name) << "," << endl
+ << "&create_schema);"
+ << endl;
+ }
+
+ // Migration.
+ //
+ if (log != 0)
+ {
+ // Create NULL migration entry for the base version so that we
+ // get the complete version range (base, current) at runtime.
+ // Code in schema_catalog relies on this.
+ //
+ os << "static const schema_catalog_migrate_entry" << endl
+ << "migrate_schema_entry_" << log->model ().version () <<
+ "_ (" << endl
+ << "id_" << db << "," << endl
+ << context::strlit (schema_name) << "," << endl
+ << log->model ().version () << "ULL," << endl
+ << "0);"
+ << endl;
+
+ for (sema_rel::changelog::contains_changeset_iterator i (
+ log->contains_changeset_begin ());
+ i != log->contains_changeset_end (); ++i)
+ {
+ sema_rel::changeset& cs (i->changeset ());
+
+ os << "static bool" << endl
+ << "migrate_schema_" << cs.version () << " (database& db, " <<
+ "unsigned short pass, bool pre)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (pass);"
+ << "ODB_POTENTIALLY_UNUSED (pre);"
+ << endl;
+
+ // Pre.
+ //
+ {
+ bool close (false);
+
+ os << "if (pre)"
+ << "{";
+
+ instance<changeset_pre> changeset (*emitter, emitter_os, format);
+ instance<create_table> ctable (*emitter, emitter_os, format);
+ instance<alter_table_pre> atable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ changeset >> names;
+ names >> ctable;
+ names >> atable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ changeset->pass (pass);
+ ctable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->migrate_pre (cs.version ());
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ // Post.
+ //
+ {
+ bool close (false);
+
+ os << "else"
+ << "{";
+
+ instance<changeset_post> changeset (*emitter, emitter_os, format);
+ instance<drop_table> dtable (*emitter, emitter_os, format);
+ instance<alter_table_post> atable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ changeset >> names;
+ names >> dtable;
+ names >> atable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ changeset->pass (pass);
+ dtable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->migrate_post ();
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ os << "return false;"
+ << "}";
+
+ os << "static const schema_catalog_migrate_entry" << endl
+ << "migrate_schema_entry_" << cs.version () << "_ (" << endl
+ << "id_" << db << "," << endl
+ << context::strlit (schema_name) << "," << endl
+ << cs.version () << "ULL," << endl
+ << "&migrate_schema_" << cs.version () << ");"
+ << endl;
+ }
+ }
+
+ if (!model.names_empty () || log != 0 || schema_version)
+ os << "}"; // namespace odb
+ }
+ }
+}
diff --git a/odb/odb/relational/schema-source.hxx b/odb/odb/relational/schema-source.hxx
new file mode 100644
index 0000000..d2235f5
--- /dev/null
+++ b/odb/odb/relational/schema-source.hxx
@@ -0,0 +1,126 @@
+// file : odb/relational/schema-source.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SCHEMA_SOURCE_HXX
+#define ODB_RELATIONAL_SCHEMA_SOURCE_HXX
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/emitter.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/schema.hxx>
+
+namespace relational
+{
+ namespace schema
+ {
+ struct cxx_emitter: emitter, virtual context
+ {
+ typedef cxx_emitter base;
+
+ void
+ pass (unsigned short p)
+ {
+ empty_ = true;
+ pass_ = p;
+ new_pass_ = true;
+
+ if (pass_ == 1)
+ empty_passes_ = 0; // New set of passes.
+
+ // Assume this pass is empty.
+ //
+ empty_passes_++;
+ }
+
+ // Did this pass produce anything?
+ //
+ bool
+ empty () const
+ {
+ return empty_;
+ }
+
+ virtual void
+ pre ()
+ {
+ first_ = true;
+ }
+
+ virtual void
+ line (const string& l)
+ {
+ if (l.empty ())
+ return; // Ignore empty lines.
+
+ if (first_)
+ {
+ first_ = false;
+
+ // If this line starts a new pass, then output the switch/case
+ // blocks.
+ //
+ if (new_pass_)
+ {
+ new_pass_ = false;
+ empty_ = false;
+ empty_passes_--; // This pass is not empty.
+
+ // Output case statements for empty preceeding passes, if any.
+ //
+ if (empty_passes_ != 0)
+ {
+ unsigned short s (pass_ - empty_passes_);
+
+ if (s == 1)
+ os << "switch (pass)"
+ << "{";
+ else
+ os << "return true;" // One more pass.
+ << "}";
+
+ for (; s != pass_; ++s)
+ os << "case " << s << ":" << endl;
+
+ os << "{";
+ empty_passes_ = 0;
+ }
+
+ if (pass_ == 1)
+ os << "switch (pass)"
+ << "{";
+ else
+ os << "return true;" // One more pass.
+ << "}";
+
+ os << "case " << pass_ << ":" << endl
+ << "{";
+ }
+
+ os << "db.execute (";
+ }
+ else
+ os << strlit (line_ + '\n') << endl;
+
+ line_ = l;
+ }
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ os << strlit (line_) << ");";
+ }
+
+ private:
+ std::string line_;
+ bool first_;
+ bool empty_;
+ bool new_pass_;
+ unsigned short pass_;
+ unsigned short empty_passes_; // Number of preceding empty passes.
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SCHEMA_SOURCE_HXX
diff --git a/odb/odb/relational/schema.cxx b/odb/odb/relational/schema.cxx
new file mode 100644
index 0000000..dd70bfa
--- /dev/null
+++ b/odb/odb/relational/schema.cxx
@@ -0,0 +1,174 @@
+// file : odb/relational/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <limits>
+#include <sstream>
+
+#include <odb/relational/schema.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace schema
+ {
+ void
+ generate_prologue ()
+ {
+ instance<sql_file> file;
+ file->prologue ();
+ }
+
+ void
+ generate_epilogue ()
+ {
+ instance<sql_file> file;
+ file->epilogue ();
+ }
+
+ void
+ generate_drop ()
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<drop_model> model (*em, emos, f);
+ instance<drop_table> table (*em, emos, f);
+ trav_rel::qnames names;
+
+ model >> names >> table;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ model->pass (pass);
+ table->pass (pass);
+
+ model->traverse (*ctx.model);
+ }
+
+ if (ctx.model->version () != 0 &&
+ !ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+ vt->create_table ();
+ vt->drop ();
+ }
+ }
+
+ void
+ generate_create ()
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<create_model> model (*em, emos, f);
+ instance<create_table> table (*em, emos, f);
+ trav_rel::qnames names;
+
+ model >> names >> table;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ model->pass (pass);
+ table->pass (pass);
+
+ model->traverse (*ctx.model);
+ }
+
+ if (ctx.model->version () != 0 &&
+ !ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+
+ if (ctx.options.omit_drop ())
+ vt->create_table ();
+
+ vt->create (ctx.model->version ());
+ }
+ }
+
+ void
+ generate_migrate_pre (sema_rel::changeset& cs)
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<changeset_pre> changeset (*em, emos, f);
+ instance<create_table> ctable (*em, emos, f);
+ instance<alter_table_pre> atable (*em, emos, f);
+ trav_rel::qnames names;
+
+ changeset >> names;
+ names >> ctable;
+ names >> atable;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ changeset->pass (pass);
+ ctable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+ vt->migrate_pre (cs.version ());
+ }
+ }
+
+ void
+ generate_migrate_post (sema_rel::changeset& cs)
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<changeset_post> changeset (*em, emos, f);
+ instance<drop_table> dtable (*em, emos, f);
+ instance<alter_table_post> atable (*em, emos, f);
+ trav_rel::qnames names;
+
+ changeset >> names;
+ names >> dtable;
+ names >> atable;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ changeset->pass (pass);
+ dtable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+ vt->migrate_post ();
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/schema.hxx b/odb/odb/relational/schema.hxx
new file mode 100644
index 0000000..cd975b7
--- /dev/null
+++ b/odb/odb/relational/schema.hxx
@@ -0,0 +1,1606 @@
+// file : odb/relational/schema.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SCHEMA_HXX
+#define ODB_RELATIONAL_SCHEMA_HXX
+
+#include <set>
+#include <vector>
+#include <cassert>
+
+#include <odb/emitter.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace schema
+ {
+ typedef std::set<qname> table_set;
+
+ struct common: virtual context
+ {
+ typedef ::emitter emitter_type;
+
+ common (emitter_type& e, ostream& os, schema_format f)
+ : e_ (e), os_ (os), format_ (f) {}
+
+ void
+ pre_statement ()
+ {
+ e_.pre ();
+ diverge (os_);
+ }
+
+ void
+ post_statement ()
+ {
+ restore ();
+ e_.post ();
+ }
+
+ emitter_type&
+ emitter () const
+ {
+ return e_;
+ }
+
+ ostream&
+ stream () const
+ {
+ return os_;
+ }
+
+ public:
+ // Find an entity corresponding to the drop node in alter_table.
+ //
+ template <typename T, typename D>
+ T&
+ find (D& d)
+ {
+ using sema_rel::model;
+ using sema_rel::changeset;
+ using sema_rel::table;
+ using sema_rel::alter_table;
+
+ alter_table& at (dynamic_cast<alter_table&> (d.scope ()));
+ changeset& cs (dynamic_cast<changeset&> (at.scope ()));
+ model& bm (cs.base_model ());
+ table* bt (bm.find<table> (at.name ()));
+ assert (bt != 0);
+ T* b (bt->find<T> (d.name ()));
+ assert (b != 0);
+ return *b;
+ }
+
+ protected:
+ emitter_type& e_;
+ ostream& os_;
+ schema_format format_;
+ };
+
+ //
+ // Drop.
+ //
+
+ // Only used in migration.
+ //
+ struct drop_column: trav_rel::drop_column, common
+ {
+ typedef drop_column base;
+
+ drop_column (common const& c, bool* first = 0)
+ : common (c),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ drop_column (drop_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ drop_header ()
+ {
+ // By default ADD COLUMN though some databases use just ADD.
+ //
+ os << "DROP COLUMN ";
+ }
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ drop_header ();
+ os << quote_id (dc.name ());
+ }
+
+ protected:
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Normally only used in migration but some databases use it as a
+ // base to also drop foreign keys in schema.
+ //
+ struct drop_foreign_key: trav_rel::foreign_key,
+ trav_rel::drop_foreign_key,
+ trav_rel::add_foreign_key, // Override.
+ common
+ {
+ typedef drop_foreign_key base;
+
+ // Schema constructor.
+ //
+ drop_foreign_key (common const& c, table_set& dropped, bool* first = 0)
+ : common (c),
+ dropped_ (&dropped),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Migration constructor.
+ //
+ drop_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ dropped_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ drop_foreign_key (drop_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ dropped_ (c.dropped_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ drop_header ()
+ {
+ os << "DROP CONSTRAINT ";
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // If the table which we reference is droped before us, then
+ // we need to drop the constraint first. Similarly, if the
+ // referenced table is not part if this model, then assume
+ // it is dropped before us. In migration we always do this
+ // first.
+ //
+ sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
+
+ if (dropped_ != 0)
+ {
+ sema_rel::qname const& rt (fk.referenced_table ());
+ sema_rel::model& m (dynamic_cast<sema_rel::model&> (t.scope ()));
+
+ if (dropped_->find (rt) == dropped_->end () &&
+ m.find (rt) != m.names_end ())
+ return;
+ }
+
+ drop (t, fk);
+ }
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ // When generating schema we would need to check if the key exists.
+ // So this implementation will need to be customize on the per-
+ // database level.
+ //
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ";
+ drop_header ();
+ os << quote_id (fk.name ()) << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ drop (dfk);
+ }
+
+ virtual void
+ drop (sema_rel::drop_foreign_key& dfk)
+ {
+ os << " ";
+ drop_header ();
+ os << quote_id (dfk.name ());
+ }
+
+ protected:
+ table_set* dropped_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Currently only used in migration.
+ //
+ struct drop_index: trav_rel::drop_index, common
+ {
+ typedef drop_index base;
+
+ enum index_type {unique, non_unique, all};
+
+ drop_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
+
+ virtual void
+ traverse (sema_rel::drop_index& di)
+ {
+ // Find the index we are dropping in the base model.
+ //
+ traverse (find<sema_rel::index> (di));
+ }
+
+ virtual void
+ traverse (sema_rel::index& in)
+ {
+ if (type_ == unique &&
+ in.type ().find ("UNIQUE") == string::npos &&
+ in.type ().find ("unique") == string::npos)
+ return;
+
+ if (type_ == non_unique && (
+ in.type ().find ("UNIQUE") != string::npos ||
+ in.type ().find ("unique") != string::npos))
+ return;
+
+ pre_statement ();
+ drop (in);
+ post_statement ();
+ }
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ return quote_id (in.name ());
+ }
+
+ virtual void
+ drop (sema_rel::index& in)
+ {
+ os << "DROP INDEX " << name (in) << endl;
+ }
+
+ protected:
+ index_type type_;
+ };
+
+ struct drop_table: trav_rel::table,
+ trav_rel::drop_table,
+ trav_rel::add_table, // Override.
+ trav_rel::alter_table, // Override.
+ common
+ {
+ typedef drop_table base;
+
+ drop_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ drop (sema_rel::table& t, bool migration)
+ {
+ pre_statement ();
+ os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
+ quote_id (t.name ()) << endl;
+ post_statement ();
+ }
+
+ virtual void
+ delete_ (sema_rel::qname const& rtable,
+ sema_rel::qname const& dtable,
+ sema_rel::primary_key& rkey,
+ sema_rel::primary_key& dkey)
+ {
+ pre_statement ();
+
+ // This might not be the most efficient way for every database.
+ //
+ os << "DELETE FROM " << quote_id (rtable) << endl
+ << " WHERE EXISTS (SELECT 1 FROM " << quote_id (dtable) << endl
+ << " WHERE ";
+
+ for (size_t i (0); i != rkey.contains_size (); ++i)
+ {
+ if (i != 0)
+ os << endl
+ << " AND ";
+
+ os << quote_id (rtable) << "." <<
+ quote_id (rkey.contains_at (i).column ().name ()) << " = " <<
+ quote_id (dtable) << "." <<
+ quote_id (dkey.contains_at (i).column ().name ());
+ }
+
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // By default drop foreign keys referencing tables that would
+ // have already been dropped on the first pass.
+ //
+ if (pass_ == 1)
+ {
+ // Drop constraints. In migration this is always done on pass 1.
+ //
+ if (migration)
+ {
+ instance<drop_foreign_key> dfk (*this);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ else
+ {
+ dropped_.insert (t.name ()); // Add it before to cover self-refs.
+
+ instance<drop_foreign_key> dfk (*this, dropped_);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ }
+ else
+ {
+ if (migration && t.extra ()["kind"] == "polymorphic derived object")
+ {
+ // If we are dropping a polymorphic derived object, then we
+ // also have to clean the base tables. Note that this won't
+ // trigger cascade deletion since we have dropped all the
+ // keys on pass 1. But we still need to do this in the base
+ // to root order in order not to trigger other cascades.
+ //
+ using sema_rel::model;
+ using sema_rel::table;
+ using sema_rel::primary_key;
+ using sema_rel::foreign_key;
+
+ model& m (dynamic_cast<model&> (t.scope ()));
+
+ table* p (&t);
+ do
+ {
+ // The polymorphic link is the first primary key.
+ //
+ for (table::names_iterator i (p->names_begin ());
+ i != p->names_end (); ++i)
+ {
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (
+ &i->nameable ()))
+ {
+ p = m.find<table> (fk->referenced_table ());
+ assert (p != 0); // Base table should be there.
+ break;
+ }
+ }
+
+ primary_key& rkey (*p->find<primary_key> (""));
+ primary_key& dkey (*t.find<primary_key> (""));
+ assert (rkey.contains_size () == dkey.contains_size ());
+ delete_ (p->name (), t.name (), rkey, dkey);
+ }
+ while (p->extra ()["kind"] != "polymorphic root object");
+ }
+
+ drop (t, migration);
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ traverse (t, false);
+ }
+
+ virtual void
+ traverse (sema_rel::drop_table& dt)
+ {
+ using sema_rel::model;
+ using sema_rel::changeset;
+ using sema_rel::table;
+
+ // Find the table we are dropping in the base model.
+ //
+ changeset& cs (dynamic_cast<changeset&> (dt.scope ()));
+ model& bm (cs.base_model ());
+ table* t (bm.find<table> (dt.name ()));
+ assert (t != 0);
+ traverse (*t, true);
+ }
+
+ using add_table::traverse; // Unhide.
+ using alter_table::traverse; // Unhide.
+
+ using table::names;
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ table_set dropped_;
+ };
+
+ struct drop_model: trav_rel::model, common
+ {
+ typedef drop_model base;
+
+ drop_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::model& m)
+ {
+ // Traverse named entities in the reverse order. This way we
+ // drop them in the order opposite to creating.
+ //
+ for (sema_rel::model::names_iterator begin (m.names_begin ()),
+ end (m.names_end ()); begin != end;)
+ dispatch (*--end);
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Create.
+ //
+
+ struct create_column: trav_rel::column,
+ trav_rel::add_column,
+ trav_rel::alter_column, // Override.
+ common
+ {
+ typedef create_column base;
+
+ create_column (common const& c,
+ bool override_null = true,
+ bool* first = 0)
+ : common (c),
+ override_null_ (override_null),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ create_column (create_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ override_null_ (c.override_null_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ create (c);
+ }
+
+ virtual void
+ add_header ()
+ {
+ // By default ADD COLUMN though some databases use just ADD.
+ //
+ os << "ADD COLUMN ";
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ add_header ();
+ create (ac);
+ }
+
+ virtual void
+ create (sema_rel::column& c)
+ {
+ using sema_rel::column;
+
+ // See if this column is (part of) a primary key.
+ //
+ sema_rel::primary_key* pk (0);
+
+ for (column::contained_iterator i (c.contained_begin ());
+ i != c.contained_end ();
+ ++i)
+ {
+ if ((pk = dynamic_cast<sema_rel::primary_key*> (&i->key ())))
+ break;
+ }
+
+ os << quote_id (c.name ()) << " ";
+
+ type (c, pk != 0 && pk->auto_ ());
+ constraints (c, pk);
+
+ if (!c.options ().empty ())
+ os << " " << c.options ();
+ }
+
+ virtual void
+ constraints (sema_rel::column& c, sema_rel::primary_key* pk)
+ {
+ null (c);
+
+ if (!c.default_ ().empty ())
+ os << " DEFAULT " << c.default_ ();
+
+ // If this is a single-column primary key, generate it inline.
+ //
+ if (pk != 0 && pk->contains_size () == 1)
+ primary_key ();
+
+ if (pk != 0 && pk->auto_ ())
+ auto_ (*pk);
+ }
+
+ virtual void
+ type (sema_rel::column& c, bool /*auto*/)
+ {
+ os << c.type ();
+ }
+
+ virtual void
+ null (sema_rel::column& c)
+ {
+ bool n (c.null ());
+
+ // If we are adding a new column that doesn't allow NULL nor has
+ // a default value, add it as NULL. Later, after migration, we
+ // will convert it to NOT NULL.
+ //
+ if (override_null_ && c.is_a<sema_rel::add_column> () &&
+ !n && c.default_ ().empty ())
+ n = true;
+
+ // Specify both cases explicitly for better readability,
+ // especially in ALTER COLUMN clauses.
+ //
+ os << (n ? " NULL" : " NOT NULL");
+ }
+
+ virtual void
+ primary_key ()
+ {
+ os << " PRIMARY KEY";
+ }
+
+ virtual void
+ auto_ (sema_rel::primary_key&)
+ {
+ }
+
+ protected:
+ bool override_null_; // Override NOT NULL in add_column.
+ bool& first_;
+ bool first_data_;
+ bool add_;
+ };
+
+ struct create_primary_key: trav_rel::primary_key, common
+ {
+ typedef create_primary_key base;
+
+ create_primary_key (common const& c): common (c) {}
+
+ virtual void
+ traverse (sema_rel::primary_key& pk)
+ {
+ // Single-column primary keys are generated inline in the
+ // column declaration.
+ //
+ if (pk.contains_size () == 1)
+ return;
+
+ // We will always follow a column.
+ //
+ os << "," << endl;
+
+ create (pk);
+ }
+
+ virtual void
+ create (sema_rel::primary_key& pk)
+ {
+ using sema_rel::primary_key;
+
+ // By default we create unnamed primary key constraint.
+ //
+
+ os << " PRIMARY KEY (";
+
+ for (primary_key::contains_iterator i (pk.contains_begin ());
+ i != pk.contains_end ();
+ ++i)
+ {
+ if (i != pk.contains_begin ())
+ os << "," << endl
+ << " ";
+
+ os << quote_id (i->column ().name ());
+ }
+
+ os << ")";
+ }
+ };
+
+ struct create_foreign_key: trav_rel::foreign_key,
+ trav_rel::add_foreign_key,
+ common
+ {
+ typedef create_foreign_key base;
+
+ // Schema constructor, pass 1.
+ //
+ create_foreign_key (common const& c, table_set& created, bool* first = 0)
+ : common (c),
+ created_ (&created),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Schema constructor, pass 2 and migration constructor.
+ //
+ create_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ created_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ create_foreign_key (create_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ created_ (c.created_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ if (created_ != 0)
+ {
+ // Pass 1.
+ //
+ // If the referenced table has already been defined, do the
+ // foreign key definition in the table definition. Otherwise
+ // postpone it until pass 2 where we do it via ALTER TABLE.
+ //
+ if (created_->find (fk.referenced_table ()) != created_->end ())
+ {
+ traverse_create (fk);
+ fk.set (db.string () + "-fk-defined", true); // Mark it as defined.
+ }
+ }
+ else
+ {
+ // Pass 2.
+ //
+ if (!fk.count (db.string () + "-fk-defined"))
+ traverse_add (fk);
+ }
+ }
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " CONSTRAINT ";
+ create (fk);
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ add (fk);
+ }
+
+ virtual void
+ traverse (sema_rel::add_foreign_key& afk)
+ {
+ traverse_add (afk);
+ }
+
+ virtual void
+ add_header ()
+ {
+ os << "ADD CONSTRAINT ";
+ }
+
+ virtual void
+ add (sema_rel::foreign_key& fk)
+ {
+ os << " ";
+ add_header ();
+ create (fk);
+ }
+
+ virtual void
+ create (sema_rel::foreign_key& fk)
+ {
+ using sema_rel::foreign_key;
+
+ os << name (fk) << endl
+ << " FOREIGN KEY (";
+
+ for (foreign_key::contains_iterator i (fk.contains_begin ());
+ i != fk.contains_end ();
+ ++i)
+ {
+ if (i != fk.contains_begin ())
+ os << "," << endl
+ << " ";
+
+ os << quote_id (i->column ().name ());
+ }
+
+ string tn (table_name (fk));
+ string tn_pad (tn.size (), ' ');
+
+ os << ")" << endl
+ << " REFERENCES " << tn << " (";
+
+ foreign_key::columns const& refs (fk.referenced_columns ());
+ for (foreign_key::columns::const_iterator i (refs.begin ());
+ i != refs.end ();
+ ++i)
+ {
+ if (i != refs.begin ())
+ os << "," << endl
+ << " " << tn_pad;
+
+ os << quote_id (*i);
+ }
+
+ os << ")";
+
+ if (fk.on_delete () != foreign_key::no_action)
+ on_delete (fk.on_delete ());
+
+ if (!fk.not_deferrable ())
+ deferrable (fk.deferrable ());
+ }
+
+ virtual string
+ name (sema_rel::foreign_key& fk)
+ {
+ return quote_id (fk.name ());
+ }
+
+ virtual string
+ table_name (sema_rel::foreign_key& fk)
+ {
+ return quote_id (fk.referenced_table ());
+ }
+
+ virtual void
+ on_delete (sema_rel::foreign_key::action_type a)
+ {
+ using sema_rel::foreign_key;
+
+ switch (a)
+ {
+ case foreign_key::no_action:
+ break;
+ case foreign_key::cascade:
+ {
+ os << endl
+ << " ON DELETE CASCADE";
+ break;
+ }
+ case foreign_key::set_null:
+ {
+ os << endl
+ << " ON DELETE SET NULL";
+ break;
+ }
+ }
+ }
+
+ virtual void
+ deferrable (sema_rel::deferrable d)
+ {
+ os << endl
+ << " DEFERRABLE INITIALLY " << d;
+ }
+
+ protected:
+ table_set* created_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ struct create_index: trav_rel::index, common
+ {
+ typedef create_index base;
+
+ enum index_type {unique, non_unique, all};
+
+ create_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
+
+ virtual void
+ traverse (sema_rel::index& in)
+ {
+ if (type_ == unique &&
+ in.type ().find ("UNIQUE") == string::npos &&
+ in.type ().find ("unique") == string::npos)
+ return;
+
+ if (type_ == non_unique && (
+ in.type ().find ("UNIQUE") != string::npos ||
+ in.type ().find ("unique") != string::npos))
+ return;
+
+ pre_statement ();
+ create (in);
+ post_statement ();
+ }
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ return quote_id (in.name ());
+ }
+
+ virtual string
+ table_name (sema_rel::index& in)
+ {
+ return quote_id (static_cast<sema_rel::table&> (in.scope ()).name ());
+ }
+
+ virtual void
+ columns (sema_rel::index& in)
+ {
+ using sema_rel::index;
+
+ for (index::contains_iterator i (in.contains_begin ());
+ i != in.contains_end ();
+ ++i)
+ {
+ if (in.contains_size () > 1)
+ {
+ if (i != in.contains_begin ())
+ os << ",";
+
+ os << endl
+ << " ";
+ }
+
+ os << quote_id (i->column ().name ());
+
+ if (!i->options ().empty ())
+ os << ' ' << i->options ();
+ }
+ }
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ // Default implementation that ignores the method.
+ //
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ os << in.type () << ' ';
+
+ os << "INDEX " << name (in) << endl
+ << " ON " << table_name (in) << " (";
+
+ columns (in);
+
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+
+ protected:
+ index_type type_;
+ };
+
+ struct create_table: trav_rel::table,
+ trav_rel::alter_table, // Override.
+ common
+ {
+ typedef create_table base;
+
+ using trav_rel::table::names;
+
+ create_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ create_pre (sema_rel::qname const& table)
+ {
+ os << "CREATE TABLE " << quote_id (table) << " (";
+ }
+
+ virtual void
+ create_post (sema_rel::table& t)
+ {
+ os << ")" << endl;
+
+ if (!t.options ().empty ())
+ os << " " << t.options () << endl;
+ }
+
+ virtual void
+ create (sema_rel::table& t)
+ {
+ pre_statement ();
+ create_pre (t.name ());
+
+ instance<create_column> c (*this);
+ instance<create_primary_key> pk (*this);
+
+ // We will always follow a column, so set first to false.
+ //
+ bool f (false); // (Im)perfect forwarding.
+ bool* pf (&f); // (Im)perfect forwarding.
+ instance<create_foreign_key> fk (*this, created_, pf);
+
+ trav_rel::unames n;
+ n >> c;
+ n >> pk;
+ n >> fk;
+
+ names (t, n);
+
+ create_post (t);
+ post_statement ();
+
+ // Create indexes.
+ //
+ {
+ instance<create_index> in (*this);
+ trav_rel::unames n (*in);
+ names (t, n);
+ }
+ }
+
+ // See if there are any undefined foreign keys that we need to
+ // add with ALTER TABLE.
+ //
+ bool
+ check_undefined_fk (sema_rel::table& t)
+ {
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end (); ++i)
+ {
+ if (i->nameable ().is_a<sema_rel::foreign_key> () &&
+ !i->nameable ().count (db.string () + "-fk-defined"))
+ return true;
+ }
+ return false;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ // By default add foreign keys referencing tables that haven't
+ // yet been defined on the second pass.
+ //
+ if (pass_ == 1)
+ {
+ // In migration we always add foreign keys on pass 2.
+ //
+ if (!t.is_a<sema_rel::add_table> ())
+ created_.insert (t.name ()); // Add it before to cover self-refs.
+
+ create (t);
+ }
+ else
+ {
+ // Add undefined foreign keys.
+ //
+ if (check_undefined_fk (t))
+ {
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (t.name ());
+
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (t, n);
+ os << endl;
+
+ post_statement ();
+ }
+ }
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ table_set created_;
+ };
+
+ struct create_model: trav_rel::model, common
+ {
+ typedef create_model base;
+
+ create_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: trav_rel::alter_column,
+ trav_rel::add_column,
+ common
+ {
+ typedef alter_column base;
+
+ alter_column (common const& c, bool pre, bool* first = 0)
+ : common (c),
+ pre_ (pre),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true),
+ fl_ (false),
+ def_ (c, fl_)
+ {
+ }
+
+ alter_column (alter_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ pre_ (c.pre_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_),
+ fl_ (false),
+ def_ (c, fl_)
+ {
+ }
+
+ virtual void
+ alter_header ()
+ {
+ os << "ALTER COLUMN ";
+ }
+
+ virtual void
+ alter (sema_rel::column& c)
+ {
+ // By default use the whole definition.
+ //
+ def_->create (c);
+ }
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Relax (NULL) in pre and tighten (NOT NULL) in post.
+ //
+ if (pre_ != c.null ())
+ return;
+
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ alter_header ();
+ alter (c);
+ }
+
+ virtual void
+ traverse (sema_rel::alter_column& ac)
+ {
+ assert (ac.null_altered ());
+ traverse (static_cast<sema_rel::column&> (ac));
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ // We initially add NOT NULL columns without default values as
+ // NULL. Now, after the migration, we convert them to NOT NULL.
+ //
+ if (!ac.null () && ac.default_ ().empty ())
+ traverse (static_cast<sema_rel::column&> (ac));
+ }
+
+ protected:
+ bool pre_;
+ bool& first_;
+ bool first_data_;
+ bool fl_; // (Im)perfect forwarding.
+ instance<create_column> def_;
+ };
+
+ struct alter_table_common: trav_rel::alter_table, common
+ {
+ alter_table_common (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ template <typename T>
+ T*
+ check (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ if (T* x = dynamic_cast<T*> (&i->nameable ()))
+ return x;
+ }
+ return 0;
+ }
+
+ sema_rel::column*
+ check_alter_column_null (sema_rel::alter_table& at, bool v)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_column;
+ using sema_rel::alter_column;
+
+ if (alter_column* ac = dynamic_cast<alter_column*> (&i->nameable ()))
+ {
+ if (ac->null_altered () && ac->null () == v)
+ return ac;
+ }
+
+ // If we are testing for NOT NULL, also look for new columns that
+ // we initially add as NULL and later convert to NOT NULL.
+ //
+ if (!v)
+ {
+ if (add_column* ac = dynamic_cast<add_column*> (&i->nameable ()))
+ {
+ if (!ac->null () && ac->default_ ().empty ())
+ return ac;
+ }
+ }
+ }
+ return 0;
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ struct alter_table_pre: alter_table_common
+ {
+ typedef alter_table_pre base;
+
+ alter_table_pre (emitter_type& e, ostream& os, schema_format f)
+ : alter_table_common (e, os, f) {}
+
+ // Check if there will be any clauses in ALTER TABLE.
+ //
+ using alter_table_common::check;
+
+ virtual bool
+ check (sema_rel::alter_table& at)
+ {
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::drop_foreign_key> (at) ||
+ check<sema_rel::add_column> (at) ||
+ check_alter_column_null (at, true);
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // By default we generate all the alterations in a single ALTER TABLE
+ // statement. Quite a few databases don't support this.
+ //
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ bool f (true); // Shared first flag.
+ bool* pf (&f); // (Im)perfect forwarding.
+ bool tl (true); // (Im)perfect forwarding.
+ instance<create_column> cc (*this, tl, pf);
+ instance<alter_column> ac (*this, tl, pf);
+ instance<drop_foreign_key> dfk (*this, pf);
+ trav_rel::unames n;
+ n >> cc;
+ n >> ac;
+ n >> dfk;
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (pass_ == 1)
+ {
+ // Drop unique indexes.
+ //
+ {
+ drop_index::index_type it (drop_index::unique);
+ instance<drop_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+
+ if (check (at))
+ alter (at);
+ }
+ else
+ {
+ // Add non-unique indexes.
+ //
+ {
+ create_index::index_type it (create_index::non_unique);
+ instance<create_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ }
+ };
+
+ struct changeset_pre: trav_rel::changeset, common
+ {
+ typedef changeset_pre base;
+
+ changeset_pre (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ struct alter_table_post: alter_table_common
+ {
+ typedef alter_table_post base;
+
+ alter_table_post (emitter_type& e, ostream& os, schema_format f)
+ : alter_table_common (e, os, f) {}
+
+ // Check if there will be any clauses in ALTER TABLE.
+ //
+ using alter_table_common::check;
+
+ virtual bool
+ check (sema_rel::alter_table& at)
+ {
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::add_foreign_key> (at) ||
+ check<sema_rel::drop_column> (at) ||
+ check_alter_column_null (at, false);
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // By default we generate all the alterations in a single ALTER TABLE
+ // statement. Quite a few databases don't support this.
+ //
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ bool f (true); // Shared first flag.
+ bool* pf (&f); // (Im)perfect forwarding.
+ bool fl (false); // (Im)perfect forwarding.
+ instance<drop_column> dc (*this, pf);
+ instance<alter_column> ac (*this, fl, pf);
+ instance<create_foreign_key> fk (*this, pf);
+
+ trav_rel::unames n;
+ n >> dc;
+ n >> ac;
+ n >> fk;
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (pass_ == 1)
+ {
+ // Drop non-unique indexes.
+ //
+ {
+ drop_index::index_type it (drop_index::non_unique);
+ instance<drop_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ else
+ {
+ if (check (at))
+ alter (at);
+
+ // Add unique indexes.
+ //
+ {
+ create_index::index_type it (create_index::unique);
+ instance<create_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ }
+ };
+
+ struct changeset_post: trav_rel::changeset, common
+ {
+ typedef changeset_post base;
+
+ changeset_post (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ traverse (sema_rel::changeset& m)
+ {
+ // Traverse named entities in the reverse order. This way we
+ // drop them in the order opposite to creating.
+ //
+ for (sema_rel::changeset::names_iterator begin (m.names_begin ()),
+ end (m.names_end ()); begin != end;)
+ dispatch (*--end);
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: common
+ {
+ typedef version_table base;
+
+ version_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f),
+ table_ (options.schema_version_table ()[db]),
+ qt_ (quote_id (table_)),
+ qs_ (quote_string (options.schema_name ()[db])),
+ qn_ (quote_id ("name")),
+ qv_ (quote_id ("version")),
+ qm_ (quote_id ("migration"))
+ {
+ }
+
+ // Create the version table if it doesn't exist.
+ //
+ virtual void
+ create_table () {}
+
+ // Remove the version entry. Called after the DROP statements.
+ //
+ virtual void
+ drop ()
+ {
+ pre_statement ();
+
+ os << "DELETE FROM " << qt_ << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ // Set the version. Called after the CREATE statements.
+ //
+ virtual void
+ create (sema_rel::version) {}
+
+ // Set the version and migration state to true. Called after
+ // the pre migration statements.
+ //
+ virtual void
+ migrate_pre (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qv_ << " = " << v << ", " << qm_ << " = 1" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ // Set migration state to false. Called after the post migration
+ // statements.
+ //
+ virtual void
+ migrate_post ()
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qm_ << " = 0" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ protected:
+ sema_rel::qname table_;
+ string qt_; // Quoted table.
+ string qs_; // Quoted schema name string.
+ string qn_; // Quoted name column.
+ string qv_; // Quoted version column.
+ string qm_; // Quoted migration column.
+ };
+
+ //
+ // SQL output.
+ //
+
+ struct sql_emitter: emitter, virtual context
+ {
+ typedef sql_emitter base;
+
+ virtual void
+ pre ()
+ {
+ first_ = true;
+ }
+
+ virtual void
+ line (const std::string& l)
+ {
+ if (first_ && !l.empty ())
+ first_ = false;
+ else
+ os << endl;
+
+ os << l;
+ }
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ os << ';' << endl
+ << endl;
+ }
+
+ protected:
+ bool first_;
+ };
+
+ struct sql_file: virtual context
+ {
+ typedef sql_file base;
+
+ virtual void
+ prologue ()
+ {
+ }
+
+ virtual void
+ epilogue ()
+ {
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SCHEMA_HXX
diff --git a/odb/odb/relational/source.cxx b/odb/odb/relational/source.cxx
new file mode 100644
index 0000000..e00626a
--- /dev/null
+++ b/odb/odb/relational/source.cxx
@@ -0,0 +1,6343 @@
+// file : odb/relational/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+
+#include <odb/gcc.hxx>
+
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/relational/source.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+void relational::source::class_::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ data_member* idb (id ? id->back () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+ bool readonly (context::readonly (c));
+
+ bool grow (false);
+ bool grow_id (false);
+
+ if (generate_grow)
+ {
+ grow = context::grow (c);
+ grow_id = (id ? context::grow (*idb) : false) ||
+ (opt ? context::grow (*opt) : false);
+ }
+
+ column_count_type const& cc (column_count (c));
+ bool versioned (context::versioned (c));
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+ user_sections* buss (poly_base != 0
+ ? &poly_base->get<user_sections> ("user-sections")
+ : 0);
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ object_extra (c);
+
+ //
+ // Query (abstract and concrete).
+ //
+
+ // query_columns
+ //
+ if (options.generate_query ())
+ query_columns_type_->traverse (c);
+
+ // Statement cache (definition).
+ //
+ if (!reuse_abst && id != 0)
+ {
+ bool sections (false);
+ bool containers (has_a (c, test_container));
+
+ os << "struct " << traits << "::extra_statement_cache_type"
+ << "{";
+
+ instance<container_cache_members> cm;
+ cm->traverse (c);
+
+ if (containers)
+ os << endl;
+
+ instance<section_cache_members> sm;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip the special version update section in reuse inheritance (we
+ // always treat it as abstract).
+ //
+ if (i->special == user_section::special_version && !poly)
+ continue;
+
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
+ //
+ if (!i->empty () || (poly && i->optimistic ()))
+ {
+ sm->traverse (*i);
+ sections = true;
+ }
+ }
+
+ if (sections)
+ os << endl;
+
+ os << "extra_statement_cache_type (" << endl
+ << db << "::connection&" << (containers || sections ? " c" : "") <<
+ "," << endl
+ << "image_type&" << (sections ? " im" : "") << "," << endl
+ << "id_image_type&" << (sections ? " idim" : "") << "," << endl
+ << db << "::binding&" << (containers || sections ? " id" : "") <<
+ "," << endl
+ << db << "::binding&" << (sections ? " idv" : "");
+
+ extra_statement_cache_extra_args (containers, sections);
+
+ os << ")";
+
+ instance<container_cache_init_members> cim;
+ cim->traverse (c);
+
+ instance<section_cache_init_members> sim (!containers);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->special == user_section::special_version && !poly)
+ continue;
+
+ if (!i->empty () || (poly && i->optimistic ()))
+ sim->traverse (*i);
+ }
+
+ os << "{"
+ << "}"
+ << "};";
+ }
+
+ //
+ // Containers (abstract and concrete).
+ //
+
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ //
+ // Sections (abstract and concrete).
+ //
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ instance<section_traits> t (c);
+ t->traverse (*i);
+ }
+
+ //
+ // Functions (abstract and concrete).
+ //
+
+ // id(), version()
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ // id (id_image_type)
+ //
+ if (auto_id)
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const id_image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "id_type id;";
+ init_id_value_member_id_image_->traverse (*idb);
+ os << "return id;"
+ << "}";
+ }
+
+ // id (image)
+ //
+ if (options.generate_query ())
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "id_type id;";
+
+ // Handle nested id.
+ //
+ if (id->size () > 1)
+ {
+ string var;
+
+ for (data_member_path::const_iterator i (id->begin ());
+ i != id->end ();
+ ++i)
+ {
+ // The same logic as in member_base.
+ //
+ if (!var.empty ())
+ var += "value."; // Composite.
+
+ string const& name ((*i)->name ());
+ var += name;
+
+ if (name[name.size () - 1] != '_')
+ var += '_';
+ }
+
+ instance<init_value_member> t ("id", var);
+ t->traverse (*idb);
+ }
+ else
+ init_id_value_member_->traverse (*idb);
+
+ os << "return id;"
+ << "}";
+ }
+
+ // version (image)
+ //
+ if (opt != 0)
+ {
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_->traverse (*opt);
+ os << "return v;"
+ << "}";
+ }
+ }
+
+ // discriminator(image)
+ //
+ if (poly && !poly_derived)
+ {
+ os << traits << "::discriminator_type" << endl
+ << traits << "::" << endl
+ << "discriminator (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "discriminator_type d;";
+ init_discriminator_value_member_->traverse (*discriminator);
+ os << "return d;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+
+ if (poly_derived)
+ {
+ // Select column count for this class.
+ //
+ size_t cols (cc.total - cc.id);
+
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)"
+ << "{"
+ << "if (base_traits::grow (*i.base, " <<
+ "t + " << cols << "UL" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << "))" << endl
+ << "i.base->version++;"
+ << "}";
+ }
+ else
+ inherits (c, grow_base_inherits_);
+
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl;
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // we get the external id binding.
+ //
+ if (poly_derived)
+ os << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl;
+
+ os << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly)
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ os << "std::size_t n (0);"
+ << endl;
+
+ if (poly_derived)
+ {
+ // The id reference comes first in the insert statement.
+ //
+ os << "// " << idf->name () << endl
+ << "//" << endl
+ << "if (sk == statement_insert)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << "}";
+ }
+ else
+ inherits (c, bind_base_inherits_);
+
+ names (c, bind_member_names_);
+
+ if (poly_derived)
+ {
+ // The id reference comes last in the update statement.
+ //
+ if (!readonly)
+ os << "// " << idf->name () << endl
+ << "//" << endl
+ << "if (sk == statement_update)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << "}";
+
+ // Bind the image chain for the select statement. Seeing that
+ // this is the last statement in the function, we don't care
+ // about updating n.
+ //
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "base_traits::bind (b + n, ";
+
+ if (poly_base != poly_root)
+ os << "id, id_size, ";
+
+ os << "*i.base, sk" <<
+ (context::versioned (*poly_base) ? ", svm" : "") << ");";
+ }
+
+ os << "}";
+
+ // bind (id_image_type)
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (opt != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "std::size_t n (0);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ bind_id_member_->traverse (*idb);
+
+ if (opt != 0)
+ {
+ os << "if (bv)"
+ << "{"
+ << "n += " << column_count (c).id << ";"
+ << endl;
+
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
+ << "init (image_type& i," << endl
+ << "const object_type& o," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly)
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ init_image_pre (c);
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ if (!poly_derived)
+ inherits (c, init_image_base_inherits_);
+
+ names (c, init_image_member_names_);
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+
+ // init (object, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (object_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (poly_derived)
+ {
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)" << endl
+ << "base_traits::init (o, *i.base, db" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+ }
+ else
+ inherits (c, init_value_base_inherits_);
+
+ names (c, init_value_member_names_);
+
+ os << "}";
+
+ // init (id_image, id)
+ //
+ if (id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (opt != 0 ? ", const version_type* v" : "") << ")"
+ << "{";
+
+ if (grow_id)
+ os << "bool grew (false);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ init_id_image_member_->traverse (*idb);
+
+ if (opt != 0)
+ {
+ // Here we rely on the fact that init_image_member
+ // always wraps the statements in a block.
+ //
+ os << "if (v != 0)";
+ init_version_image_member_->traverse (*opt);
+ }
+
+ if (grow_id)
+ os << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ //
+ // Containers (concrete).
+ //
+
+ //
+ // Sections (concrete).
+ //
+
+ // Polymorphic map.
+ //
+ if (poly)
+ {
+ if (!poly_derived)
+ os << traits << "::map_type*" << endl
+ << traits << "::map;"
+ << endl;
+
+ // Sections. Do we have any new sections or overrides?
+ //
+ bool sections (
+ uss.count (user_sections::count_new |
+ user_sections::count_override |
+ user_sections::count_load |
+ user_sections::count_update |
+ user_sections::count_special_version |
+ (poly ? user_sections::count_optimistic : 0)) != 0);
+ if (sections)
+ {
+ string const& fn (flat_name (type));
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version));
+
+ map<size_t, user_section*> m;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ m[i->index] = &*i;
+
+ os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type::section_functions" << endl
+ << "section_functions_for_" << fn << "[] ="
+ << "{";
+
+ for (size_t i (0); i != n; ++i)
+ {
+ os << (i != 0 ? "," : "") << "{";
+
+ map<size_t, user_section*>::iterator j (m.find (i));
+
+ if (j != m.end ())
+ {
+ user_section& s (*j->second);
+ string n (public_name (*s.member));
+
+ // load
+ //
+ if (s.load_empty ())
+ os << "0";
+ else
+ os << "&odb::section_load_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+
+ os << "," << endl;
+
+ // update
+ //
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
+ //
+ if (s.update_empty () && !(poly && s.optimistic ()))
+ os << "0";
+ else
+ os << "&odb::section_update_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+ }
+ else
+ os << "0," << endl
+ << "0";
+
+ os << "}";
+ }
+
+ os << "};";
+
+ os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type::section_list" << endl
+ << "section_list_for_" << fn << " ="
+ << "{"
+ << n << "UL," << endl
+ << "section_functions_for_" << fn
+ << "};";
+ }
+
+ //
+ //
+ os << "const " << traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type" << endl
+ << traits << "::info (" << endl
+ << "typeid (" << type << ")," << endl;
+
+ if (poly_derived)
+ os << "&object_traits_impl< " << class_fq_name (*poly_base) <<
+ ", id_" << db << " >::info," << endl;
+ else
+ os << "0," << endl;
+
+ // Sections.
+ //
+ if (sections)
+ os << "&section_list_for_" << flat_name (type);
+ else
+ os << "0";
+
+ if (!abst)
+ {
+ os << "," << endl
+ << strlit (string (type, 2, string::npos)) << "," << endl
+ << "&odb::create_impl< " << type << " >," << endl
+ << "&odb::dispatch_impl< " << type << ", id_" << db << " >," << endl;
+
+ if (poly_derived)
+ os << "&statements_type::delayed_loader";
+ else
+ os << "0";
+ }
+
+ os << ");"
+ << endl;
+
+ if (!abst)
+ os << "static const " << traits << "::entry_type" << endl
+ << "polymorphic_entry_for_" << flat_name (type) << ";"
+ << endl;
+ }
+
+ //
+ // Statements.
+ //
+ bool update_columns (
+ cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update);
+
+ qname table (table_name (c));
+ string qtable (quote_id (table));
+
+ // persist_statement
+ //
+ {
+ string sep (versioned ? "\n" : " ");
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> ct (sk, sc);
+ ct->traverse (c);
+ process_statement_columns (sc, statement_insert, versioned);
+ }
+
+ bool dv (sc.empty ()); // The DEFAULT VALUES syntax.
+
+ os << "const char " << traits << "::persist_statement[] =" << endl
+ << strlit ("INSERT INTO " + qtable + sep) << endl;
+
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e;)
+ {
+ string s;
+
+ if (i == b)
+ s += '(';
+ s += i->column;
+ s += (++i != e ? ',' : ')');
+ s += sep;
+
+ os << strlit (s) << endl;
+ }
+
+ instance<query_parameters> qp (statement_insert, table);
+
+ string extra (persist_statement_extra (c, *qp, persist_after_columns));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string values;
+ if (!dv)
+ {
+ os << strlit ("VALUES" + sep) << endl;
+
+ values += '(';
+ instance<persist_statement_params> pt (values, *qp, sep);
+ pt->traverse (c);
+ values += ')';
+ }
+ else
+ values = "DEFAULT VALUES";
+
+ extra = persist_statement_extra (c, *qp, persist_after_values);
+
+ if (!extra.empty ())
+ values += sep;
+
+ os << strlit (values);
+
+ if (!extra.empty ())
+ os << endl
+ << strlit (extra);
+
+ os << ";"
+ << endl;
+ }
+
+ // Index of the first empty SELECT statement in poly-derived
+ // statement list. All subsequent statements are also empty.
+ // The first statement can never be empty (contains id and
+ // type_id).
+ //
+ size_t empty_depth (0);
+
+ if (id != 0)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id);
+
+ std::vector<size_t> find_column_counts (abst ? 1 : poly_depth);
+
+ // find_statement
+ //
+ if (poly_derived)
+ os << "const char* const " << traits << "::find_statements[] ="
+ << "{";
+ else
+ os << "const char " << traits << "::find_statement[] =" << endl;
+
+ for (size_t d (poly_depth); d != 0;)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, d, s);
+ t->traverse (c);
+ process_statement_columns (sc, statement_select, versioned);
+ find_column_counts[poly_depth - d] = sc.size ();
+ }
+
+ if (sc.size () != 0)
+ {
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ if (d != 1)
+ {
+ bool f (false); // @@ (im)perfect forwarding
+ size_t d1 (d - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c, f, d1);
+ j->traverse (polymorphic_base (c));
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ {
+ // For the find statement, don't join section members.
+ //
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* s (&main_section); // @@ (im)perfect forwarding
+ instance<object_joins> j (c, f, d, s);
+ j->traverse (c);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where);
+ }
+ else
+ os << strlit (""); // Empty SELECT statement.
+
+ if (abst)
+ break;
+
+ if (--d != 0)
+ os << "," << endl
+ << endl;
+ }
+
+ if (poly_derived)
+ os << "};";
+ else
+ os << ";"
+ << endl;
+
+ // find_column_counts
+ //
+ if (poly_derived)
+ {
+ os << "const std::size_t " << traits << "::find_column_counts[] ="
+ << "{";
+
+ for (std::vector<size_t>::iterator b (find_column_counts.begin ()),
+ i (b), e (find_column_counts.end ()); i != e;)
+ {
+ os << *i << "UL";
+
+ if (*i == 0 && empty_depth == 0)
+ empty_depth = i - b;
+
+ if (++i != e)
+ os << ',' << endl;
+ }
+
+ os << "};";
+ }
+
+ // find_discriminator_statement
+ //
+ if (poly && !poly_derived)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc);
+ t->traverse (*discriminator);
+
+ if (opt != 0)
+ t->traverse (*opt);
+
+ process_statement_columns (sc, statement_select, false);
+ }
+
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement[] =" << endl
+ << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? ", " : " ")) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (update_columns)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ instance<query_parameters> qp (statement_update, table);
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, s);
+ t->traverse (c);
+ process_statement_columns (sc, statement_update, versioned);
+ }
+
+ os << "const char " << traits << "::update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
+ string extra (update_statement_extra (c));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ // Top-level version column.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // erase_statement
+ //
+ {
+ instance<query_parameters> qp (statement_delete, table);
+ os << "const char " << traits << "::erase_statement[] =" << endl
+ << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ if (opt != 0 && !poly_derived)
+ {
+ instance<query_parameters> qp (statement_delete, table);
+
+ os << "const char " << traits << "::optimistic_erase_statement[] " <<
+ "=" << endl
+ << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ // Top-level version column.
+ //
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+ }
+
+ // query_statement
+ //
+ bool query_optimize (false);
+ if (options.generate_query ())
+ {
+ // query_statement
+ //
+ strings joins;
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c, t, d);
+ j->traverse (polymorphic_base (c));
+ joins = j->joins;
+ }
+
+ if (id != 0)
+ {
+ // For the query statement we also join section members so that they
+ // can be used in the WHERE clause.
+ //
+ bool t (true); //@@ (im)perfect forwarding
+ instance<object_joins> j (c, t, poly_depth);
+ j->traverse (c);
+ joins.insert (joins.end (), j->begin (), j->end ());
+ }
+
+ query_optimize = !joins.empty ();
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); //@@ Imperfect forwarding.
+ object_section* s (&main_section); //@@ Imperfect forwarding.
+ instance<object_columns> oc (qtable, sk, sc, poly_depth, s);
+ oc->traverse (c);
+ process_statement_columns (
+ sc, statement_select, versioned || query_optimize);
+ }
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ os << "const char " << traits << "::query_statement[] =" << endl
+ << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ string prev ("FROM " + qtable);
+
+ for (strings::const_iterator i (joins.begin ()); i != joins.end (); ++i)
+ {
+ os << strlit (prev + sep) << endl;
+ prev = *i;
+ }
+
+ os << strlit (prev) << ";"
+ << endl;
+
+ // erase_query_statement
+ //
+ os << "const char " << traits << "::erase_query_statement[] =" << endl
+ << strlit ("DELETE FROM " + qtable) << ";"
+ << endl;
+
+ // table_name
+ //
+ os << "const char " << traits << "::table_name[] =" << endl
+ << strlit (qtable) << ";" // Use quoted name.
+ << endl;
+ }
+
+ // persist ()
+ //
+ size_t persist_containers (has_a (c, test_straight_container));
+ bool persist_versioned_containers (
+ persist_containers >
+ has_a (c,
+ test_straight_container |
+ exclude_deleted | exclude_added | exclude_versioned));
+
+ os << "void " << traits << "::" << endl
+ << "persist (database& db, " << (auto_id ? "" : "const ") <<
+ "object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_persist, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to persist an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned ||
+ persist_versioned_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Call callback (pre_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::pre_persist);"
+ << endl;
+ }
+
+ // Call our base if we are a derived type in a polymorphic
+ // hierarchy.
+ //
+ if (poly_derived)
+ os << "base_traits::persist (db, obj, false, false);"
+ << endl;
+
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.insert_image_binding ());";
+
+ if (poly_derived)
+ os << "const binding& idb (sts.id_image_binding ());";
+
+ os << endl;
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ if (!poly_derived && auto_id && insert_send_auto_id)
+ {
+ string const& n (idf->name ());
+ string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
+ init_auto_id (*idf, var); // idf == idb, since auto
+ }
+
+ os << "if (";
+
+ if (poly_derived)
+ os << "idb.version != sts.insert_id_binding_version () ||" << endl;
+
+ os << "im.version != sts.insert_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, ";
+
+ if (poly_derived)
+ os << "idb.bind, idb.count, ";
+
+ os << "im, statement_insert" << (versioned ? ", svm" : "") << ");";
+
+ if (poly_derived)
+ os << "sts.insert_id_binding_version (idb.version);";
+
+ os << "sts.insert_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ // Bind id image since that's where the returned id and/or version will
+ // be extracted.
+ //
+ if (!poly_derived)
+ {
+ bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
+ if (bv || auto_id)
+ {
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "binding& b (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || b.version == 0)"
+ << "{"
+ << "bind (b.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "b.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}"
+ << "}";
+ }
+ }
+
+ os << "insert_statement& st (sts.persist_statement ());"
+ << "if (!st.execute ())" << endl
+ << "throw object_already_persistent ();"
+ << endl;
+
+ if (!poly_derived && auto_id) // idf == idb since auto
+ {
+ set_member (*idf, "obj", "id (sts.id_image ())", "db", "id_type");
+ os << endl;
+ }
+
+ // Set the optimistic concurrency version in the object member.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ // If we don't have auto id, then obj is a const reference.
+ //
+ set_member (*opt,
+ (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
+ optimistic_version_init (*opt),
+ "", // No database.
+ "version_type");
+ os << endl;
+ }
+
+ // Initialize id_image and binding if we are a root of a polymorphic
+ // hierarchy or if we have non-inverse containers.
+ //
+ if (!poly_derived && (poly || persist_containers))
+ {
+ // If this is a polymorphic root without containers, then we only
+ // need to do this if we are not a top-level call. If we are poly-
+ // abstract, then top will always be false.
+ //
+ if (poly && !persist_containers && !abst)
+ os << "if (!top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl
+ << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly && !persist_containers && !abst)
+ os << "}";
+ }
+
+ if (persist_containers)
+ {
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ instance<container_calls> t (container_calls::persist_call);
+ t->traverse (c);
+ }
+
+ // Reset sections: loaded, unchanged.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they have been reset by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ // Call callback (post_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::post_persist);";
+ }
+
+ os << "}";
+
+ // persist() bulk
+ //
+ if (c.count ("bulk-persist"))
+ {
+ os << "void " << traits << "::" << endl
+ << "persist (database& db," << endl
+ << (auto_id ? "" : "const ") << "object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ os << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_persist);"
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql) so all we have to do is to increment image
+ // version if it grew.
+ //@@ assumption: insert_send_auto_id is false
+ << "image_type& im (sts.image (i));";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << " && i == 0)" << endl
+ << "im.version++";
+
+ os << ";"
+ << "}";
+
+ //@@ assumption: generate_grow: as above
+ os << "binding& imb (sts.insert_image_binding ());"
+ << "if (imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, sts.image (), statement_insert" <<
+ (versioned ? ", svm" : "") << ");"
+ << "imb.version++;"
+ << "}";
+
+ // Bind id image since that's where the returned id and/or version will
+ // be extracted.
+ //
+ bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
+ if (bv || auto_id)
+ {
+ os << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow: as above
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ os << "insert_statement& st (sts.persist_statement ());"
+ << "n = st.execute (n, mex);" // Actual number of rows attempted.
+ << endl;
+
+ os << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "bool r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception for this position.
+ << "continue;"
+ << endl
+ << "if (!r)"
+ << "{"
+ << "mex.insert (i, object_already_persistent ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << (auto_id ? "" : "const ") << "object_type& obj (*objs[i]);"
+ << endl;
+
+ // Extract auto id.
+ //
+ if (auto_id) // idb == idf, since auto
+ {
+ set_member (*idf, "obj", "id (sts.id_image (i))", "db", "id_type");
+ os << endl;
+ }
+
+ // Set the optimistic concurrency version.
+ //
+ if (opt != 0)
+ {
+ // If we don't have auto id, then obj is a const reference.
+ //
+ set_member (*opt,
+ (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
+ optimistic_version_init (*opt, true),
+ "", // No database.
+ "version_type");
+ os << endl;
+ }
+
+ // Reset sections: loaded, unchanged.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::post_persist);"
+ << "}" // for
+ << "}"; // persist ()
+ }
+
+ // update ()
+ //
+ if (id != 0 && (!readonly || poly))
+ {
+ size_t update_containers (
+ has_a (c, test_readwrite_container, &main_section));
+
+ bool update_versioned_containers (
+ update_containers >
+ has_a (c,
+ test_readwrite_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &main_section));
+
+ // See if we have any sections that we might have to update.
+ //
+ bool sections (false);
+ bool versioned_sections (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // This test will automatically skip the special version update section.
+ //
+ if (i->update_empty () || i->update == user_section::update_manual)
+ continue;
+
+ sections = true;
+
+ if (added (*i->member) || deleted (*i->member))
+ versioned_sections = true;
+
+ // For change-updated sections, also check if we have any
+ // versioned smart containers.
+ //
+ if (i->update != user_section::update_change)
+ continue;
+
+ if (has_a (c, test_smart_container, &*i) !=
+ has_a (c, test_smart_container | exclude_deleted | exclude_added, &*i))
+ versioned_sections = true;
+ }
+
+ os << "void " << traits << "::" << endl
+ << "update (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_update, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to update an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ // If we are readonly, then there is nothing else to do.
+ //
+ if (!readonly)
+ {
+ // Call callback (pre_update).
+ //
+ if (!abst) // If we are poly-abstract then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_update);"
+ << endl;
+ }
+
+ bool sts (false);
+
+ if ((versioned && update_columns) ||
+ update_versioned_containers ||
+ versioned_sections)
+ {
+ sts = true;
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ }
+
+ // If we have change-updated sections that contain change-tracking
+ // containers, then mark such sections as changed if any of the
+ // containers were changed. Do this before calling the base so that
+ // we account for the whole hierarchy before we actually start
+ // updating any sections (important for optimistic concurrency
+ // version).
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ if (s.update != user_section::update_change)
+ continue;
+
+ if (!has_a (c, test_smart_container, &s))
+ continue;
+
+ data_member& m (*s.member);
+
+ os << "// " << m.name () << endl
+ << "//" << endl;
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // VC++ cannot grok the constructor syntax.
+ //
+ os << "const odb::section& s = " << ma.translate ("obj") << ";"
+ << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ if (s.load != user_section::load_eager)
+ os << "s.loaded () && ";
+
+ // And for not already being changed.
+ //
+ os << "!s.changed ())"
+ << "{";
+
+ instance<container_calls> t (container_calls::section_call, &s);
+ t->traverse (c);
+
+ os << "}" // if
+ << "}";
+ }
+
+ if (poly_derived)
+ {
+ bool readonly_base (context::readonly (*poly_base));
+
+ if (!sts && (readonly_base ||
+ update_columns ||
+ update_containers ||
+ sections))
+ {
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+ }
+
+ // Unless our base is readonly, call it first.
+ //
+ if (!readonly_base)
+ {
+ os << "base_traits::update (db, obj, false, false);"
+ << endl;
+ }
+ else
+ {
+ // Otherwise, we have to initialize the id image ourselves. If
+ // we don't have any columns, containers or sections to update,
+ // then we only have to do it if this is not a top-level call.
+ // If we are abstract, then top is always false.
+ //
+ if (!update_columns && !update_containers && !sections && !abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ // Optimistic poly base cannot be readonly.
+ << "}"
+ << "}";
+ }
+
+ if (update_columns)
+ {
+ // Initialize the object image.
+ //
+ os << "image_type& im (sts.image ());";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" <<
+ (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ os << "const binding& idb (sts.id_image_binding ());"
+ << "binding& imb (sts.update_image_binding ());"
+ << "if (idb.version != sts.update_id_binding_version () ||" << endl
+ << "im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, idb.bind, idb.count, im, statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_id_binding_version (idb.version);"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+ }
+
+ // Otherwise, nothing else to do here if we don't have any columns
+ // to update.
+ }
+ else if (update_columns)
+ {
+ if (!sts)
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Initialize id image.
+ //
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "id_image_type& idi (sts.id_image ());"
+ << "init (idi, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
+ << endl;
+
+ // Initialize object image.
+ //
+ os << "image_type& im (sts.image ());";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" <<
+ (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ // The update binding is bound to two images (object and id)
+ // so we have to track both versions.
+ //
+ os << "bool u (false);" // Avoid incrementing version twice.
+ << "binding& imb (sts.update_image_binding ());"
+ << "if (im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "u = true;"
+ << "}";
+
+ // To update the id part of the update binding we have to do
+ // it indirectly via the id binding, which just points to the
+ // suffix of the update bind array (see object_statements).
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (idi.version != sts.update_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ // If the id binding is up-to-date, then that means update
+ // binding is too and we just need to update the versions.
+ //
+ << "if (idi.version != sts.id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, idi);"
+ // Update the id binding versions since we may use them later
+ // to update containers.
+ //
+ << "sts.id_image_version (idi.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}"
+ << "sts.update_id_image_version (idi.version);"
+ << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl;
+
+ if (opt == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+ else if (poly || update_containers || sections)
+ {
+ // We don't have any columns to update but we may still need
+ // to initialize the id image if we are polymorphic or have
+ // any containers or sections.
+ //
+ if (!sts)
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // The same rationale as a couple of screens up.
+ //
+ if (poly && !update_containers && !sections && !abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ // Cannot be optimistic.
+ << "}"
+ << "}";
+ }
+
+ if (update_containers || sections)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (update_containers)
+ {
+ instance<container_calls> t (container_calls::update_call,
+ &main_section);
+ t->traverse (c);
+ }
+
+ // Update the optimistic concurrency version in the object member.
+ // Do it before updating sections since they will update it as well.
+ // The same code as in section_traits.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+
+ os << endl;
+ }
+
+ // Update sections that are loaded and need updating.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->update_empty () || i->update == user_section::update_manual)
+ continue;
+
+ // Here is the deal: for polymorphic section overrides, we could
+ // have used the same approach as in load_() where the class that
+ // defines the section data member initiates the section update.
+ // However, if we used this approach, then we would get what can
+ // be called "interleaved" update statements. That is, a section
+ // update would update the section data in "derived" tables before
+ // updating the main section in these derived tables. While this
+ // does not feel right, it can also cause more real problems if,
+ // for example, there are constraints or some such. Generally,
+ // we always want to update the main section data first for each
+ // table in the hierarchy.
+ //
+ // So what we are going to do instead is call section traits
+ // update at each override point (and indicate to it not to
+ // call base). One tricky aspect is who is going to reset the
+ // changed state. We have to do it at the top since otherwise
+ // overrides won't know the section has changed. Finding out
+ // whether we are the final override (in section terms, not
+ // object terms) is tricky.
+ //
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ string sep;
+ if (i->load != user_section::load_eager)
+ {
+ os << ma.translate ("obj") << ".loaded ()";
+ sep = " && ";
+ }
+
+ // If change-updated section, check that it has changed.
+ //
+ if (i->update == user_section::update_change)
+ os << sep << ma.translate ("obj") << ".changed ()";
+
+ os << ")"
+ << "{";
+
+ // Re-initialize the id+ver image with new version which may
+ // have changed. Here we take a bit of a shortcut and not
+ // re-bind the image since we know only version may have
+ // changed and that is always an integer (cannot grow).
+ //
+ if (opt != 0)
+ {
+ os << "const version_type& v (version (obj));"
+ << "init (sts.id_image (), id (obj), &v);"
+ << endl;
+ }
+
+ os << public_name (m) << "_traits::update (esc, obj";
+
+ if (poly_derived && i->base != 0)
+ {
+ // Normally we don't want to call base (base object's update()
+ // would have called it; see comment above). There is one
+ // exception, however, and that is a readonly base section
+ // in optimistic class. In this case, base object's update()
+ // won't call it (it is readonly) but it needs to be called
+ // in order to increment the version.
+ //
+ bool base (false);
+ for (user_section* b (i->base); !base && b != 0; b = b->base)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers)
+ break; // We have a readwrite base.
+ else if (b->optimistic ())
+ base = true; // We have a readonly optimistic root.
+ }
+
+ os << ", " << base;
+ }
+
+ os << ");";
+
+ // Clear the change flag and arm the transaction rollback callback.
+ //
+ if (i->update == user_section::update_change)
+ {
+ // If polymorphic, do it only if we are the final override.
+ //
+ if (poly)
+ os << "if (root_traits::map->find (typeid (obj))." <<
+ "final_section_update (info, " << i->index << "UL))" << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false, &tr);";
+ }
+
+ os << "}";
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+
+ // Call callback (post_update).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "callback (db, obj, callback_event::post_update);"
+ << "pointer_cache_traits::update (db, obj);";
+
+ if (poly)
+ os << "}";
+ }
+ } // readonly
+
+ os << "}";
+ }
+
+ // update () bulk
+ //
+ if (id != 0 && !readonly && c.count ("bulk-update"))
+ {
+ os << "void " << traits << "::" << endl
+ << "update (database& db," << endl
+ << "const object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_update);";
+
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "init (sts.id_image (i), id (obj)" << (opt != 0 ? ", &v" : "") << ");";
+
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql) so all we have to do is to increment image
+ // version if it grew.
+
+ os << "image_type& im (sts.image (i));";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << " && i == 0)" << endl
+ << "im.version++";
+
+ os << ";"
+ << "}";
+
+ // Update bindings.
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "binding& imb (sts.update_image_binding ());"
+ << endl;
+
+ //@@ assumption: generate_grow: as above
+ //
+ os << "bool u (false);" // Avoid incrementing version twice.
+ << "if (imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, sts.image (), statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "imb.version++;"
+ << "u = true;"
+ << "}";
+
+ //@@ assumption: generate_grow: as above
+ //
+ os << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
+ << "}";
+
+ // We don't need the versioned check here since the statement cannot
+ // be empty; otherwise it would have been an empty object which is
+ // illegal.
+ //
+ os << "update_statement& st (sts.update_statement ());"
+ << "n = st.execute (n, mex);"; // Actual number of rows attempted.
+
+ os << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Also sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == update_statement::result_unknown)," << endl
+ << (opt == 0 ? "object_not_persistent" : "object_changed") << " ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << "const object_type& obj (*objs[i]);";
+
+ // Update the optimistic concurrency version in the object member.
+ //
+ if (opt != 0)
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+
+ os << endl;
+ }
+
+ os << "callback (db, obj, callback_event::post_update);"
+ << "pointer_cache_traits::update (db, obj);"
+ << "}" // for
+ << "}"; // update()
+ }
+
+ // erase (id)
+ //
+ size_t erase_containers (has_a (c, test_straight_container));
+ bool erase_versioned_containers (
+ erase_containers >
+ has_a (c, test_straight_container | exclude_deleted | exclude_added));
+
+ if (id != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const id_type& id";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";";
+
+ if (poly)
+ os << endl
+ << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Get the discriminator and determine the dynamic type of
+ // this object.
+ //
+ if (poly)
+ {
+ os << "if (dyn)" << endl
+ << "{"
+ << "discriminator_type d;"
+ << "root_traits::discriminator_ (sts.root_statements (), id, &d);";
+
+ if (!abst)
+ os << endl
+ << "if (d != info.discriminator)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl
+ // Check that the dynamic type is derived from the static type.
+ //
+ << "if (!pi.derived (info))" << endl
+ << "throw object_not_persistent ();"
+ << endl
+ << "pi.dispatch (info_type::call_erase, db, 0, &id);"
+ << "return;";
+
+ if (!abst)
+ os << "}";
+
+ os << "}";
+ }
+
+ // Initialize id image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly)
+ os << "}";
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_id_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "base_traits::erase (db, id, false, false);"
+ << endl;
+ }
+
+ // Remove from the object cache.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "pointer_cache_traits::erase (db, id);";
+ }
+
+ os << "}";
+ }
+
+ // erase (id) bulk
+ //
+ if (id != 0 && c.count ("bulk-erase"))
+ {
+ os << "std::size_t " << traits << "::" << endl
+ << "erase (database& db," << endl
+ << "const id_type** ids," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)" << endl
+ << "init (sts.id_image (i), *ids[i]);"
+ << endl
+ << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql).
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;"
+ << (opt != 0 ? "sts.optimistic_id_image_binding ().version++;" : "")
+ << "}"
+ << "delete_statement& st (sts.erase_statement ());"
+ << "n = st.execute (n, mex);" // Set to actual number of rows attempted.
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == delete_statement::result_unknown)," << endl
+ << "object_not_persistent ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << "pointer_cache_traits::erase (db, *ids[i]);"
+ << "}" // for
+ << "return n;"
+ << "}"; // erase()
+ }
+
+ // erase (object)
+ //
+ bool erase_smart_containers (has_a (c, test_smart_container));
+
+ if (id != 0 && (poly || opt != 0 || erase_smart_containers))
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_erase, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to erase an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ if (opt != 0 || erase_smart_containers)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (poly_derived)
+ os << endl
+ << "root_statements_type& rsts (sts.root_statements ());"
+ << "ODB_POTENTIALLY_UNUSED (rsts);";
+
+ os << endl;
+
+ // Call callback (pre_erase).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << endl;
+ }
+
+ if (!abst || erase_containers)
+ {
+ os << "const id_type& id (object_traits_impl::id (obj));"
+ << endl;
+ }
+
+ // Smart containers case.
+ //
+ if (opt == 0)
+ {
+ // Initialize id image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
+ << "if (i.version != " << rsts << ".id_image_version () || " <<
+ "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << rsts << ".id_image_version (i.version);"
+ << "idb.version++;"
+ // Cannot be optimistic.
+ << "}";
+
+ if (poly)
+ os << "}";
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_obj_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+ }
+ // Optimistic case.
+ //
+ else
+ {
+ // Initialize id + managed column image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "const version_type& v (version (obj));"
+ << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, id, &v);"
+ << endl;
+
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
+ << "if (i.version != " << rsts << ".id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << rsts << ".id_image_version (i.version);"
+ << "idb.version++;"
+ << rsts << ".optimistic_id_image_binding ().version++;"
+ << "}";
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to check the version (stored in root) before we go ahead
+ // and start deleting things. Also use the same code for root with
+ // containers since it is more efficient than the find_() method
+ // below.
+ //
+ bool svm (false);
+ if (poly_derived || (poly && erase_containers))
+ {
+ // Only do the check in the top-level call.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ os << "if (top)"
+ << "{"
+ << "version_type v;"
+ << "root_traits::discriminator_ (" << rsts << ", id, 0, &v);"
+ << endl;
+
+ os << "if (v != version (obj))" << endl
+ << "throw object_changed ();"
+ << "}";
+ }
+ }
+ else if (erase_containers)
+ {
+ // Things get complicated here: we don't want to trash the
+ // containers and then find out that the versions don't match
+ // and we therefore cannot delete the object. After all, there
+ // is no guarantee that the user will abort the transaction.
+ // In fact, a perfectly reasonable scenario is to reload the
+ // object, re-check the condition, decide not to delete the
+ // object, and then commit the transaction.
+ //
+ // There doesn't seem to be anything better than first making
+ // sure we can delete the object, then deleting the container
+ // data, and then deleting the object. To check that we can
+ // delete the object we are going to use find_() and then
+ // compare the versions. A special-purpose SELECT query would
+ // have been more efficient but it would complicated and bloat
+ // things significantly.
+ //
+
+ if (versioned)
+ {
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ svm = true;
+ }
+
+ os << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "throw object_changed ();"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "sts.find_statement ().free_result ();"
+ << endl;
+
+ os << "if (version (sts.image ()) != version (obj))" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers && !svm)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_obj_call);
+ t->traverse (c);
+ }
+
+ const char* st (
+ poly_derived ? "erase_statement" : "optimistic_erase_statement");
+
+ os << "if (sts." << st << " ().execute () != 1)" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "base_traits::erase (db, obj, false, false);"
+ << endl;
+ }
+
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
+ // Remove from the object cache.
+ //
+ os << "pointer_cache_traits::erase (db, id);";
+
+ // Call callback (post_erase).
+ //
+ os << "callback (db, obj, callback_event::post_erase);";
+
+ if (poly)
+ os << "}";
+ }
+ }
+ else
+ {
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << "erase (db, id (obj), true, false);"
+ << "callback (db, obj, callback_event::post_erase);";
+ }
+
+ os << "}";
+ }
+
+ // erase (object) bulk
+ //
+ if (id != 0 && c.count ("bulk-erase"))
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db," << endl
+ << "const object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{";
+
+ // In non-optimistic case delegate to erase(id).
+ //
+ if (opt == 0)
+ {
+ os << "id_type a[batch];"
+ << "const id_type* p[batch];"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "a[i] = id (obj);"
+ << "p[i] = a + i;"
+ << "}"
+ << "n = erase (db, p, n, mex);"
+ << endl
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "return;"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "if (mex[i] == 0)" << endl // No pending exception.
+ << "callback (db, *objs[i], callback_event::post_erase);"
+ << "}"; // for
+ }
+ else
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "const version_type& v (version (obj));"
+ << "init (sts.id_image (i), id (obj), &v);"
+ << "}";
+
+ os << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow is false or it only affects select
+ // (like in pgsql).
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;"
+ << "sts.optimistic_id_image_binding ().version++;"
+ << "}"
+ << "delete_statement& st (sts.optimistic_erase_statement ());"
+ << "n = st.execute (n, mex);" // Set to actual number of attempted.
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == delete_statement::result_unknown)," << endl
+ << "object_changed ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << "const object_type& obj (*objs[i]);"
+ << "pointer_cache_traits::erase (db, id (obj));"
+ << "callback (db, obj, callback_event::post_erase);"
+ << "}"; // for
+ }
+
+ os << "}"; // erase()
+ }
+
+ // find (id)
+ //
+ if (id != 0 && c.default_ctor ())
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << traits << "::pointer_type" << endl
+ << traits << "::" << endl
+ << "find (database& db, const id_type& id)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // First check the session.
+ //
+ os << "{";
+
+ if (poly_derived)
+ os << "root_traits::pointer_type rp (pointer_cache_traits::find (" <<
+ "db, id));"
+ << endl
+ << "if (!root_traits::pointer_traits::null_ptr (rp))" << endl
+ << "return" << endl
+ << " root_traits::pointer_traits::dynamic_pointer_cast<" <<
+ "object_type> (rp);";
+ else
+ os << "pointer_type p (pointer_cache_traits::find (db, id));"
+ << endl
+ << "if (!pointer_traits::null_ptr (p))" << endl
+ << "return p;";
+
+ os << "}";
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");";
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar;";
+
+ if (poly)
+ os << "root_traits::discriminator_type d;";
+
+ os << endl
+ << "if (l.locked ())"
+ << "{"
+ << "if (!find_ (sts, &id" << (versioned ? ", svm" : "") << "))" << endl
+ << "return pointer_type ();";
+
+ if (delay_freeing_statement_result)
+ os << endl
+ << "ar.set (sts.find_statement (" << (poly_derived ? "depth" : "") <<
+ "));";
+
+ if (poly)
+ os << "d = root_traits::discriminator (" << rsts << ".image ());";
+
+ os << "}";
+
+ if (poly)
+ {
+ // If statements are locked, then get the discriminator by
+ // executing a special SELECT statement. We need it to be
+ // able to create an object of the correct dynamic type
+ // which will be loaded later.
+ //
+ os << "else" << endl
+ << "root_traits::discriminator_ (" << rsts << ", id, &d);"
+ << endl;
+
+ if (abst)
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl;
+ else
+ os << "const info_type& pi (" << endl
+ << "d == info.discriminator ? info : root_traits::map->find (d));"
+ << endl;
+ }
+
+ // Create the object.
+ //
+ if (poly_derived)
+ {
+ os << "root_traits::pointer_type rp (pi.create ());"
+ << "pointer_type p (" << endl
+ << "root_traits::pointer_traits::static_pointer_cast<object_type> " <<
+ "(rp));"
+ << "pointer_traits::guard pg (p);"
+ << endl;
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, rp));"
+ << endl;
+ }
+ else
+ {
+ if (poly)
+ os << "pointer_type p (pi.create ());";
+ else
+ os << "pointer_type p (" << endl
+ << "access::object_factory<object_type, pointer_type>::create ());";
+
+ os << "pointer_traits::guard pg (p);"
+ << endl;
+
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, p));"
+ << endl;
+ }
+
+ os << "object_type& obj (pointer_traits::get_ref (p));"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (poly)
+ os << "callback_event ce (callback_event::pre_load);"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::pre_load);";
+
+ os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");";
+
+ if (poly)
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ os << endl
+ << "if (&pi != &info)"
+ << "{"
+ << "std::size_t d (depth);"
+ << "pi.dispatch (info_type::call_load, db, &obj, &d);"
+ << "}";
+
+ os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+
+ if (poly)
+ os << "ce = callback_event::post_load;"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::post_load);";
+
+ os << "pointer_cache_traits::load (ig.position ());"
+ << "}"
+ << "else" << endl
+ << rsts << ".delay_load (id, obj, ig.position ()" <<
+ (poly ? ", pi.delayed_loader" : "") << ");"
+ << endl;
+
+ os << "ig.release ();"
+ << "pg.release ();"
+ << "return p;"
+ << "}";
+ }
+
+ // find (id, obj)
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "bool " << traits << "::" << endl
+ << "find (database& db, const id_type& id, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_find, db, &obj, &id);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "reference_cache_traits::position_type pos (" << endl
+ << "reference_cache_traits::insert (db, id, obj));"
+ << "reference_cache_traits::insert_guard ig (pos);"
+ << endl
+ << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");"
+ << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "reference_cache_traits::load (pos);"
+ << "ig.release ();"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // reload ()
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ // This implementation is almost exactly the same as find(id, obj)
+ // above except that it doesn't interract with the object cache and,
+ // in case of optimistic concurrency, checks if the object actually
+ // needs to be reloaded.
+ //
+ os << "bool " << traits << "::" << endl
+ << "reload (database& db, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_reload, db, &obj, 0);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "const id_type& id (object_traits_impl::id (obj));"
+ << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);"
+ << endl;
+
+ if (opt != 0)
+ {
+ const char* tr (poly_derived ? "root_traits::" : "");
+
+ os << "if (" << tr << "version (" << rsts << ".image ()) == " <<
+ tr << "version (obj))" << endl
+ << "return true;"
+ << endl;
+ }
+
+ os << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, true" << (versioned ? ", svm" : "") << ");"
+ << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // load (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s" <<
+ (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "bool top (pi == 0);" // Top-level call.
+ << "if (top)"
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // Lock the statements for the object itself. We need to do this since we
+ // are using the id image and loading of object pointers can overwrite it.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");";
+
+ // It this is a top-level call, then auto_lock must succeed.
+ //
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "bool r (false);"
+ << endl;
+
+ // If our poly-base has load sections, then call the base version
+ // first. Besides checking for sections, it will also initialize
+ // the id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_load |
+ user_sections::count_load_empty) != 0)
+ {
+ os << "if (base_traits::load (conn, obj, s, pi))" << endl
+ << "r = true;"
+ << endl;
+ }
+ else
+ {
+ // Initialize id image (all this is equivalent to using rsts).
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Resolve extra statements if we are doing direct calls.
+ //
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ // Dispatch.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ if (i->load == user_section::load_eager)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (!r && &s == &" << ma.translate ("obj") << ")"
+ << "{";
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ // If this is an empty section, then there may not be any
+ // overrides.
+ //
+ os << "info_type::section_load sl (" <<
+ "pi->find_section_load (" << i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (conn, obj, true);";
+ }
+
+ os << "r = true;"
+ << "}";
+ }
+
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+
+ if (poly)
+ os << "}";
+
+ os << "return r;"
+ << "}";
+ }
+
+ // update (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s" << (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "if (pi == 0)" // Top-level call.
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // If our poly-base has update sections, then call the base version
+ // first. Besides checking for sections, it will also initialize the
+ // id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_update |
+ user_sections::count_update_empty) != 0)
+ {
+ os << "if (base_traits::update (conn, obj, s, pi))" << endl
+ << "return true;"
+ << endl;
+ }
+ else
+ {
+ // Resolve extra statements if we are doing direct calls.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ os << endl;
+
+ // Initialize id image. This is not necessarily the root of the
+ // polymorphic hierarchy.
+ //
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "id_image_type& i (sts.id_image ());";
+
+ os << "init (i, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Dispatch.
+ //
+ bool e (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ // Skip read-only sections. Polymorphic sections are always
+ // (potentially) read-write.
+ //
+ if (!poly && i->update_empty ())
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (e)
+ os << "else ";
+ else
+ e = true;
+
+ os << "if (&s == &" << ma.translate ("obj") << ")";
+
+ if (!poly)
+ os << public_name (m) << "_traits::update (esc, obj);";
+ else
+ {
+ // If this is a readonly section, then there may not be any
+ // overrides.
+ //
+ os << "{"
+ << "info_type::section_update su (" <<
+ "pi->find_section_update (" << i->index << "UL));";
+
+ if (i->update_empty ())
+ {
+ // For optimistic section, also check that we are not the
+ // final override, since otherwise we will increment the
+ // version without updating anything.
+ //
+ if (i->optimistic ())
+ os << "if (su != 0 && su != info.sections->functions[" <<
+ i->index << "UL].update)" << endl;
+ else
+ os << "if (su != 0)" << endl;
+ }
+
+ os << "su (conn, obj);"
+ << "}";
+ }
+ }
+
+ os << "else" << endl
+ << "return false;"
+ << endl
+ << "return true;"
+ << "}";
+ }
+
+ // find_ ()
+ //
+ if (id != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "find_ (statements_type& sts," << endl
+ << "const id_type* id";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived && !abst)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ if (poly_derived && !abst)
+ os << "if (d == depth)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, *id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly_derived && !abst)
+ os << "}";
+
+ // Rebind data image.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << endl;
+
+ // The dynamic loader SELECT statement can be dynamically empty.
+ //
+ if (versioned && poly_derived && !abst)
+ os << "if (st.empty ())" << endl
+ << "return true;"
+ << endl;
+
+ os << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ if (grow)
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") <<
+ (poly_derived ? (abst ? ", depth" : ", d") : "") << "))" << endl
+ << "im.version++;"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "st.refetch ();"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}";
+ }
+
+ os << "}";
+ }
+
+ // If we are delaying, only free the result if it is empty.
+ //
+ if (delay_freeing_statement_result)
+ os << "if (r != select_statement::no_data)"
+ << "{"
+ << "ar.release ();"
+ << "return true;"
+ << "}"
+ << "else" << endl
+ << "return false;";
+ else
+ os << "return r != select_statement::no_data;";
+
+ os << "}";
+ }
+
+ // load_()
+ //
+ // Load containers, reset/reload sections.
+ //
+ size_t load_containers (
+ has_a (c, test_container | include_eager_load, &main_section));
+
+ if (poly_derived ||
+ load_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ {
+ bool load_versioned_containers (
+ load_containers >
+ has_a (c,
+ test_container | include_eager_load |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &main_section));
+
+ os << "void " << traits << "::" << endl
+ << "load_ (statements_type& sts," << endl
+ << "object_type& obj," << endl
+ << "bool reload";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (reload);";
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+ os << endl;
+
+ if (poly_derived)
+ os << "if (--d != 0)" << endl
+ << "base_traits::load_ (sts.base_statements (), obj, reload" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+
+ if (load_containers ||
+ (!poly && uss.count (user_sections::count_new |
+ user_sections::count_load) != 0))
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (!versioned && (
+ load_versioned_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0))
+ {
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ }
+
+ if (load_containers)
+ {
+ instance<container_calls> t (container_calls::load_call, &main_section);
+ t->traverse (c);
+ }
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they are handled by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (i->load == user_section::load_eager)
+ {
+ // Mark an eager section as loaded.
+ //
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ continue;
+ }
+
+ os << "if (reload)"
+ << "{"
+ // Reload sections that have been loaded, clear change state.
+ //
+ << "if (" << ma.translate ("obj") << ".loaded ())"
+ << "{";
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ os << "info_type::section_load sl (" << endl
+ << "root_traits::map->find (typeid (obj)).find_section_load (" <<
+ i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (sts.connection (), obj, true);";
+ }
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << "}"
+ << "}"
+ << "else" << endl
+ // Reset to unloaded, unchanged state.
+ << ma.translate ("obj") << ".reset ();";
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ else
+ os << endl;
+ }
+
+ os << "}";
+ }
+
+ // load_()
+ //
+ // Load the dynamic part of the object. We don't need it if we are
+ // poly-abstract.
+ //
+ if (poly_derived && !abst)
+ {
+ os << "void " << traits << "::" << endl
+ << "load_ (database& db, root_type& r, std::size_t d)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "object_type& obj (static_cast<object_type&> (r));"
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "d = depth - d;" // Convert to distance from derived.
+ << endl;
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+
+ // Avoid trying to execute an empty SELECT statement.
+ //
+ if (empty_depth != 0)
+ os << "if (d > " << (poly_depth - empty_depth) << "UL)"
+ << "{";
+
+ os << "if (!find_ (sts, 0" << (versioned ? ", svm" : "") << ", d))" << endl
+ << "throw object_not_persistent ();" // Database inconsistency.
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (d));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ // The resulting SELECT statement may end up being dynamically empty.
+ //
+ if (versioned)
+ os << "if (!st.empty ())"
+ << "{";
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") <<
+ ", d);";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ if (versioned)
+ os << "}";
+
+ if (empty_depth != 0)
+ os << "}";
+
+ os << "load_ (sts, obj, false, " << (versioned ? "svm, " : "") << "d);"
+ << "}";
+ }
+
+ // discriminator_ ()
+ //
+ if (poly && !poly_derived)
+ {
+ os << "void " << traits << "::" << endl
+ << "discriminator_ (statements_type& sts," << endl
+ << "const id_type& id," << endl
+ << "discriminator_type* pd";
+
+ if (opt != 0)
+ os << "," << endl
+ << "version_type* pv";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ os << "id_image_type& idi (sts.discriminator_id_image ());"
+ << "init (idi, id);"
+ << endl;
+
+ os << "binding& idb (sts.discriminator_id_image_binding ());"
+ << "if (idi.version != sts.discriminator_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, idi" << (opt != 0 ? ", false" : "") << ");"
+ << "sts.discriminator_id_image_version (idi.version);"
+ << "idb.version++;"
+ << "}";
+
+ // Rebind data image.
+ //
+ os << "discriminator_image_type& i (sts.discriminator_image ());"
+ << "binding& imb (sts.discriminator_image_binding ());"
+ << endl
+ << "if (i.version != sts.discriminator_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ // Generate bind code inline. For now discriminator is simple
+ // value so we don't need statement kind (sk).
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "{"
+ << "select_statement& st (sts.find_discriminator_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl
+ << "if (r == select_statement::no_data)"
+ << "{";
+
+ if (opt != 0)
+ os << "if (pv != 0)" << endl
+ << "throw object_changed ();"
+ << "else" << endl;
+
+ os << "throw object_not_persistent ();"
+ << "}";
+
+ if (generate_grow &&
+ (context::grow (*discriminator) ||
+ (opt != 0 && context::grow (*opt))))
+ {
+ os << "else if (r == select_statement::truncated)"
+ << "{";
+
+ // Generate grow code inline.
+ //
+ os << "bool grew (false);"
+ << truncated_vector << " t (sts.discriminator_image_truncated ());"
+ << endl;
+
+ index_ = 0;
+ grow_discriminator_member_->traverse (*discriminator);
+
+ if (opt != 0)
+ grow_version_member_->traverse (*opt);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << endl;
+
+ os << "if (i.version != sts.discriminator_image_version ())"
+ << "{"
+ // Generate bind code inline. The same code as above.
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ // Discriminator cannot be long data (no streaming).
+ //
+ os << "}";
+
+ // Initialize value inline instead of generating a separate
+ // init() function. For now discriminator is simple value so
+ // we don't need the database (db).
+ //
+ os << "if (pd != 0)"
+ << "{"
+ << "discriminator_type& d (*pd);";
+ init_named_discriminator_value_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "if (pv != 0)"
+ << "{"
+ << "version_type& v (*pv);";
+ init_named_version_value_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ if (options.generate_query ())
+ {
+ char const* result_type;
+ if (poly)
+ result_type = "polymorphic_object_result_impl<object_type>";
+ else if (id != 0)
+ result_type = "object_result_impl<object_type>";
+ else
+ result_type = "no_id_object_result_impl<object_type>";
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ // Unprepared.
+ //
+ if (!options.omit_unprepared ())
+ {
+ // query ()
+ //
+ os << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "std::string text (query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += " << strlit (sep) << ";"
+ << "text += q.clause ();"
+ << "}";
+
+ os << "q.init_parameters ();"
+ << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+ object_query_statement_ctor_args (
+ c, "q", versioned || query_optimize, false);
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c, true);
+
+ os << endl
+ << "shared_ptr< odb::" << result_type << " > r (" << endl
+ << "new (shared) " << db << "::" << result_type << " (" << endl
+ << "q, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << endl
+ << "return result<object_type> (r);"
+ << "}";
+
+ // query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return query (db, query_base_type (q));"
+ << "}";
+ }
+
+ // erase_query
+ //
+ os << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << endl
+ << "std::string text (erase_query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += ' ';"
+ << "text += q.clause ();"
+ << "}"
+ << "q.init_parameters ();"
+ << "delete_statement st (" << endl;
+ object_erase_query_statement_ctor_args (c);
+ os << ");"
+ << endl
+ << "return st.execute ();"
+ << "}";
+
+ // erase_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return erase_query (db, query_base_type (q));"
+ << "}";
+
+ // Prepared. Very similar to unprepared but has some annoying variations
+ // that make it difficult to factor out something common.
+ //
+ if (options.generate_prepared ())
+ {
+ // prepare_query
+ //
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << "static_cast<" << db << "::connection&> (c));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "std::string text (query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += " << strlit (sep) << ";"
+ << "text += q.clause ();"
+ << "}";
+
+ os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
+ << "new (shared) " << db << "::prepared_query_impl (conn));"
+ << "r->name = n;"
+ << "r->execute = &execute_query;"
+ << "r->query = q;"
+ << "r->stmt.reset (" << endl
+ << "new (shared) select_statement (" << endl;
+ object_query_statement_ctor_args (
+ c, "r->query", versioned || query_optimize, true);
+ os << "));"
+ << endl
+ << "return r;"
+ << "}";
+
+ // prepare_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const odb::query_base& q)"
+ << "{"
+ << "return prepare_query (c, n, query_base_type (q));"
+ << "}";
+
+ // execute_query
+ //
+ os << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::prepared_query_impl& pq (" << endl
+ << "static_cast<" << db << "::prepared_query_impl&> (q));"
+ << "shared_ptr<select_statement> st (" << endl
+ << "odb::details::inc_ref (" << endl
+ << "static_cast<select_statement*> (pq.stmt.get ())));"
+ << endl;
+
+ os << db << "::transaction& tr (" << db << "::transaction::current ());"
+ << endl
+ << "// The connection used by the current transaction and the" << endl
+ << "// one used to prepare this statement must be the same." << endl
+ << "//" << endl
+ << "assert (q.verify_connection (tr));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "st->connection ().statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "pq.query.init_parameters ();"
+ << "st->execute ();";
+ post_query_ (c, false);
+
+ os << endl
+ << "return shared_ptr<result_impl> (" << endl
+ << "new (shared) " << db << "::" << result_type << " (" << endl
+ << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << "}";
+ }
+ }
+
+ // Generate function table registration for dynamic multi-database
+ // support.
+ //
+ if (multi_dynamic)
+ {
+ string fn (flat_name (type));
+ string dt ("access::object_traits_impl< " + type + ", id_common >");
+
+ os << "static const" << endl
+ << dt << "::" << endl
+ << "function_table_type function_table_" << fn << "_ ="
+ << "{";
+
+ // persist ()
+ //
+ os << "&" << traits << "::persist";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "," << endl
+ << "&" << traits << "::find";
+
+ // find (id, obj)
+ //
+ os << "," << endl
+ << "&" << traits << "::find";
+
+ // reload ()
+ //
+ os << "," << endl
+ << "&" << traits << "::reload";
+
+ // update ()
+ //
+ if (!readonly || poly)
+ os << "," << endl
+ << "&" << traits << "::update";
+
+ // erase ()
+ //
+ os << "," << endl
+ << "&" << traits << "::erase";
+
+ os << "," << endl
+ << "&" << traits << "::erase";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::load";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::update";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ os << "," << endl
+ << "&" << traits << "::query";
+
+ os << "," << endl
+ << "&" << traits << "::erase_query";
+
+ if (options.generate_prepared ())
+ {
+ os << "," << endl
+ << "&" << traits << "::prepare_query";
+
+ os << "," << endl
+ << "&" << traits << "::execute_query";
+ }
+ }
+
+ os << "};";
+
+ os << "static const object_function_table_entry< " << type << ", " <<
+ "id_" << db << " >" << endl
+ << "function_table_entry_" << fn << "_ (" << endl
+ << "&function_table_" << fn << "_);"
+ << endl;
+ }
+}
+
+void relational::source::class_::
+traverse_view (type& c)
+{
+ view_query& vq (c.get<view_query> ("query"));
+
+ // Only process the view query if it is versioned since the query text
+ // (e.g., in the native view) must be structured. We also shouldn't try
+ // to optimize JOINs (since the objects are JOINed explicitly by the
+ // user), unless we are adding poly-base/derived JOINs.
+ //
+ bool versioned (context::versioned (c));
+ bool query_optimize (false);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ size_t columns (column_count (c).total);
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ // Generate the from-list. Also collect relationships via which
+ // the objects are joined.
+ //
+ strings from;
+ view_relationship_map rel_map;
+
+ if (vq.kind == view_query::condition)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ bool first (i == objs.begin ());
+ string l;
+
+ //
+ // Tables.
+ //
+
+ if (i->kind == view_object::table)
+ {
+ if (first)
+ {
+ l = "FROM ";
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ l += from_trailer (c);
+
+ from.push_back (l);
+ continue;
+ }
+
+ l = join_syntax (*i);
+ l += ' ';
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
+
+ expression e (
+ translate_expression (
+ c, i->cond, scope, i->loc, "table"));
+
+ if (e.kind != expression::literal)
+ {
+ error (i->loc) << "invalid join condition in db pragma " <<
+ "table" << endl;
+ throw operation_failed ();
+ }
+
+ l += " ON";
+
+ // Add the pragma location for easier error tracking.
+ //
+ from.push_back (l);
+ from.push_back ("// From " + location_string (i->loc, true));
+ from.push_back (e.value);
+ continue;
+ }
+
+ //
+ // Objects.
+ //
+ semantics::class_& o (*i->obj);
+
+ bool poly (polymorphic (o));
+ size_t poly_depth (poly ? polymorphic_depth (o) : 1);
+
+ string alias (i->alias);
+
+ // For polymorphic objects, alias is just a prefix.
+ //
+ if (poly && !alias.empty ())
+ alias += "_" + table_name (o).uname ();
+
+ // First object.
+ //
+ if (first)
+ {
+ l = "FROM ";
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += from_trailer (c);
+
+ from.push_back (l);
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ continue;
+ }
+
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
+
+ expression e (i->join == view_object::cross
+ ? expression ("") // Dummy literal expression.
+ : translate_expression (
+ c, i->cond, scope, i->loc, "object"));
+
+ // Literal expression.
+ //
+ if (e.kind == expression::literal)
+ {
+ l = join_syntax (*i);
+ l += ' ';
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
+ l += " ON";
+
+ // Add the pragma location for easier error tracking.
+ //
+ from.push_back (l);
+ from.push_back ("// From " + location_string (i->loc, true));
+ from.push_back (e.value);
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ continue;
+ }
+
+ // We have an object relationship (pointer) for which we need
+ // to come up with the corresponding JOIN condition. If this
+ // is a to-many relationship, then we first need to JOIN the
+ // container table. This code is similar to object_joins.
+ //
+ // Note that this cannot be CROSS JOIN; we've handled that case
+ // above.
+ //
+ using semantics::data_member;
+
+ data_member& m (*e.member_path.back ());
+ data_member_path* imp (inverse (m));
+
+ // Resolve the pointed-to object to view_object and do
+ // some sanity checks while at it.
+ //
+ semantics::class_* c (0);
+
+ if (container (m))
+ c = object_pointer (container_vt (m));
+ else
+ c = object_pointer (utype (m));
+
+ view_object* vo (0);
+
+ // Check if the pointed-to object has been previously associated
+ // with this view and is unambiguous. A pointer to ourselves is
+ // always assumed to point to this association.
+ //
+ if (&o == c)
+ vo = &*i;
+ else
+ {
+ bool ambig (false);
+
+ for (view_objects::iterator j (objs.begin ()); j != i; ++j)
+ {
+ if (j->obj != c)
+ continue;
+
+ if (vo == 0)
+ {
+ vo = &*j;
+ continue;
+ }
+
+ // If it is the first ambiguous object, issue the
+ // error.
+ //
+ if (!ambig)
+ {
+ error (i->loc) << "pointed-to object '" << class_name (*c) <<
+ "' is ambiguous" << endl;
+ info (i->loc) << "candidates are:" << endl;
+ info (vo->loc) << " '" << vo->name () << "'" << endl;
+ ambig = true;
+ }
+
+ info (j->loc) << " '" << j->name () << "'" << endl;
+ }
+
+ if (ambig)
+ {
+ info (i->loc) << "use the other side of the relationship " <<
+ "or full join condition clause in db pragma object to " <<
+ "resolve this ambiguity" << endl;
+ throw operation_failed ();
+ }
+
+ if (vo == 0)
+ {
+ error (i->loc) << "pointed-to object '" << class_name (*c) <<
+ "' specified in the join condition has not been " <<
+ "previously associated with this view" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // JOIN relationship points to us:
+ // vo - us
+ // e.vo - other side
+ // e.member_path - in other side
+ //
+ // JOIN relationship points to other side:
+ // vo - other side
+ // e.vo - us
+ // e.member_path - in us
+ //
+ if (imp == 0)
+ rel_map.insert (make_pair (e.member_path, make_pair (e.vo, vo)));
+ else
+ rel_map.insert (make_pair (*imp, make_pair (vo, e.vo)));
+
+ // Left and right-hand side table names.
+ //
+ qname lt;
+ {
+ using semantics::class_;
+
+ class_& o (*e.vo->obj);
+ string const& a (e.vo->alias);
+
+ if (class_* root = polymorphic (o))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (
+ &static_cast<class_&> (
+ e.member_path.front ()->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ qname const& t (table_name (*c));
+
+ if (a.empty ())
+ lt = t;
+ else
+ lt = qname (a + "_" + t.uname ());
+ }
+ else
+ lt = a.empty () ? table_name (o) : qname (a);
+ }
+
+ qname rt;
+ {
+ qname t (table_name (*vo->obj));
+ string const& a (vo->alias);
+ rt = a.empty ()
+ ? t
+ : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a);
+ }
+
+ // First join the container table if necessary.
+ //
+ semantics::type* cont (container (imp != 0 ? *imp->back () : m));
+
+ string ct; // Container table.
+ if (cont != 0)
+ {
+ l = join_syntax (*i);
+ l += ' ';
+
+ // The same relationship can be used by multiple associated
+ // objects. So if the object that contains this container has
+ // an alias, then also construct one for the table that we
+ // are joining.
+ //
+ {
+ using semantics::class_;
+
+ qname t;
+
+ // In a polymorphic hierarchy the member can be in a base (see
+ // above).
+ //
+ if (class_* root = polymorphic (imp != 0 ? *vo->obj : *e.vo->obj))
+ {
+ class_* c;
+ if (imp == 0)
+ {
+ c = &static_cast<class_&> (e.member_path.front ()->scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+
+ t = table_name (*c, e.member_path);
+ }
+ else
+ {
+ c = &static_cast<class_&> (imp->front ()->scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+
+ t = table_name (*c, *imp);
+ }
+ }
+ else
+ t = imp != 0
+ ? table_name (*vo->obj, *imp)
+ : table_name (*e.vo->obj, e.member_path);
+
+ // The tricky part is to figure out which view_object, vo
+ // or e.vo we should use. We want to use the alias from the
+ // object that actually contains this container. The following
+ // might not make intuitive sense, but it has been verified
+ // with the truth table.
+ //
+ string const& a (imp != 0 ? vo->alias : e.vo->alias);
+
+ if (a.empty ())
+ {
+ ct = quote_id (t);
+ l += ct;
+ }
+ else
+ {
+ ct = quote_id (a + '_' + t.uname ());
+ l += quote_id (t);
+ l += (need_alias_as ? " AS " : " ") + ct;
+ }
+ }
+
+ l += " ON";
+ from.push_back (l);
+
+ // If we are the pointed-to object, then we have to turn
+ // things around. This is necessary to have the proper
+ // JOIN order. There seems to be a pattern there but it
+ // is not yet intuitively clear what it means.
+ //
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (imp != 0)
+ {
+ semantics::data_member& imb (*imp->back ());
+
+ if (&o == c)
+ {
+ // container.value = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (imb, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.id = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.id = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (
+ m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.value = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+
+ // If we have already joined the container with the desired
+ // join type, then use LEFT JOIN to join the object to the
+ // container. This is the right thing to do since an entry
+ // in the container can only point (either via id or value)
+ // to a single object.
+ //
+ l = (cont == 0 ? join_syntax (*i) : "LEFT JOIN");
+ l += ' ';
+
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += " ON";
+ from.push_back (l);
+
+ if (cont != 0)
+ {
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (imp != 0)
+ {
+ semantics::data_member& imb (*imp->back ());
+
+ if (&o == c)
+ {
+ // container.id = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.value = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (imb, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.value = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.id = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+ else
+ {
+ instance<object_columns_list> l_cols;
+ instance<object_columns_list> r_cols;
+
+ if (imp != 0)
+ {
+ // our.id = pointed-to.pointer
+ //
+ l_cols->traverse (*id_member (*e.vo->obj));
+ r_cols->traverse (*imp->back (), column_prefix (*imp));
+ }
+ else
+ {
+ // our.pointer = pointed-to.id
+ //
+ l_cols->traverse (*e.member_path.back (),
+ column_prefix (e.member_path));
+ r_cols->traverse (*id_member (*vo->obj));
+ }
+
+ for (object_columns_list::iterator b (l_cols->begin ()), i (b),
+ j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += quote_id (lt);
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (rt);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ } // End JOIN-generating for-loop.
+ }
+
+ // Check that pointed-to objects inside objects that we are loading
+ // have session support enabled (required to have a shared copy).
+ // Also see if we need to throw if there is no session.
+ //
+ bool need_session (false);
+ if (vq.kind == view_query::condition)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind != view_object::object || i->ptr == 0)
+ continue; // Not an object or not loaded.
+
+ instance<view_object_check> t (*i, rel_map);
+ t->traverse (*i->obj);
+ need_session = need_session || t->session_;
+ }
+ }
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ view_extra (c);
+
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ view_query_columns_type_->traverse (c);
+
+ //
+ // Functions.
+ //
+
+ // grow ()
+ //
+ if (generate_grow && columns != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ if (columns != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "image_type& i";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ names (c, bind_member_names_);
+
+ os << "}";
+ }
+
+ // init (view, image)
+ //
+ if (columns != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "init (view_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (need_session)
+ os << "if (!" << options.session_type () << "::_has_cache ())" << endl
+ << "throw session_required ();"
+ << endl;
+
+ // Note: db must be not NULL in order to load pointers.
+ //
+ if (has_a (c, test_pointer))
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (*db));"
+ << endl;
+
+ names (c, init_view_pointer_member_pre_names_);
+ names (c, init_value_member_names_);
+ names (c, init_view_pointer_member_post_names_);
+
+ os << "}";
+ }
+
+ // query_statement()
+ //
+ if (vq.kind != view_query::runtime)
+ {
+ os << traits << "::query_base_type" << endl
+ << traits << "::" << endl
+ << "query_statement (const query_base_type& q)"
+ << "{";
+
+ if (vq.kind == view_query::complete_select ||
+ vq.kind == view_query::complete_execute)
+ {
+ os << "query_base_type r (" << endl;
+
+ bool ph (false);
+ bool pred (vq.kind == view_query::complete_select);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ // For the SELECT query we keep the parenthesis in (?) and
+ // also handle the case where the query expression is empty.
+ //
+ if (pred)
+ os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ else
+ {
+ os << strlit (string (vq.literal, 0, p)) << " + q";
+
+ p += 3;
+ if (p != vq.literal.size ())
+ os << " + " << strlit (string (vq.literal, p));
+ }
+ }
+ else
+ os << strlit (vq.literal);
+ }
+ else
+ {
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
+
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " << location_string (vq.loc, true) << endl
+ << translate_expression (
+ c, vq.expr, scope, vq.loc, "query", &ph, pred).value;
+ }
+
+ os << ");";
+
+ // If there was no placeholder, add the query condition
+ // at the end.
+ //
+ if (!ph)
+ os << endl
+ << "if (!q.empty ())"
+ << "{"
+ << "r += " << strlit (versioned ? "\n" : " ") << ";"
+ << "r += q.clause_prefix ();"
+ << "r += q;"
+ << "}";
+ }
+ else // vq.kind == view_query::condition
+ {
+ // Use the from-list generated above.
+ //
+ statement_columns sc;
+ {
+ instance<view_columns> t (sc, from, rel_map);
+ t->traverse (c);
+ process_statement_columns (
+ sc, statement_select, versioned || query_optimize);
+ }
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ os << "query_base_type r (" << endl
+ << strlit ((vq.distinct ? "SELECT DISTINCT" : "SELECT") + sep);
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << endl
+ << strlit (c + (++i != e ? "," : "") + sep);
+ }
+
+ os << ");"
+ << endl;
+
+ // It is much easier to add the separator at the beginning of the
+ // next line since the JOIN condition may not be a string literal.
+ //
+ for (strings::const_iterator i (from.begin ()); i != from.end (); ++i)
+ {
+ if (i->compare (0, 5, "FROM ") == 0)
+ os << "r += " << strlit (*i) << ";";
+ else if (i->compare (0, 3, "// ") == 0)
+ os << *i << endl;
+ else
+ {
+ // See if this is a JOIN. The exact spelling is database-dependent,
+ // but we know there is the JOIN word in there somewhere and before
+ // it we should only have keywords and spaces.
+ //
+ size_t p (i->find ("JOIN "));
+ if (p != string::npos)
+ {
+ // Make sure before it we only have A-Z and spaces.
+ //
+ for (char c; p != 0; --p)
+ {
+ c = (*i)[p - 1];
+ if ((c < 'A' || c > 'Z') && c != ' ')
+ break;
+ }
+
+ if (p == 0)
+ os << endl
+ << "r += " << strlit (sep + *i) << ";";
+ }
+
+ // Something else, assume already a string literal.
+ //
+ if (p != 0)
+ os << "r += " << *i << ";";
+ }
+ }
+
+ // Generate the query condition.
+ //
+ if (!vq.literal.empty () || !vq.expr.empty ())
+ {
+ os << endl
+ << "query_base_type c (" << endl;
+
+ bool ph (false);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ }
+ else
+ os << strlit (vq.literal);
+
+ os << ");";
+ }
+ else
+ {
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
+
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " << location_string (vq.loc, true) << endl
+ << translate_expression (
+ c, vq.expr, scope, vq.loc, "query", &ph).value;
+
+ os << ");";
+
+ // Optimize the query if it had a placeholder. This gets
+ // rid of useless clauses like WHERE TRUE.
+ //
+ if (ph)
+ os << endl
+ << "c.optimize ();";
+ }
+
+ if (!ph)
+ os << endl
+ << "c += q;";
+
+ os << endl
+ << "if (!c.empty ())"
+ << "{"
+ << "r += " << strlit (sep) << ";"
+ << "r += c.clause_prefix ();"
+ << "r += c;"
+ << "}";
+
+ string st (select_trailer (c));
+ if (!st.empty ())
+ {
+ os << "r += " << strlit (sep) << ";"
+ << "r += " << strlit (st) << ";";
+ }
+ }
+ else
+ {
+ os << endl
+ << "if (!q.empty ())"
+ << "{"
+ << "r += " << strlit (sep) << ";"
+ << "r += q.clause_prefix ();"
+ << "r += q;"
+ << "}";
+ }
+ }
+
+ os << "return r;"
+ << "}";
+ }
+
+ // Unprepared.
+ //
+ if (!options.omit_unprepared ())
+ {
+ os << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ if (vq.kind == view_query::runtime)
+ os << "const query_base_type& qs (q);";
+ else
+ os << "const query_base_type& qs (query_statement (q));";
+
+ os << "qs.init_parameters ();"
+ << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+ view_query_statement_ctor_args (
+ c, "qs", versioned || query_optimize, false);
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c, true);
+
+ os << endl
+ << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl
+ << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
+ << "qs, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << endl
+ << "return result<view_type> (r);"
+ << "}";
+
+ // query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return query (db, query_base_type (q));"
+ << "}";
+ }
+
+ // Prepared. Very similar to unprepared but has some annoying variations
+ // that make it difficult to factor out something common.
+ //
+ if (options.generate_prepared ())
+ {
+ // prepare_query
+ //
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << "static_cast<" << db << "::connection&> (c));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
+ << "new (shared) " << db << "::prepared_query_impl (conn));"
+ << "r->name = n;"
+ << "r->execute = &execute_query;";
+
+ if (vq.kind == view_query::runtime)
+ os << "r->query = q;";
+ else
+ os << "r->query = query_statement (q);";
+
+ os << "r->stmt.reset (" << endl
+ << "new (shared) select_statement (" << endl;
+ view_query_statement_ctor_args (
+ c, "r->query", versioned || query_optimize, true);
+ os << "));"
+ << endl
+ << "return r;"
+ << "}";
+
+ // prepare_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const odb::query_base& q)"
+ << "{"
+ << "return prepare_query (c, n, query_base_type (q));"
+ << "}";
+
+ // execute_query
+ //
+ os << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::prepared_query_impl& pq (" << endl
+ << "static_cast<" << db << "::prepared_query_impl&> (q));"
+ << "shared_ptr<select_statement> st (" << endl
+ << "odb::details::inc_ref (" << endl
+ << "static_cast<select_statement*> (pq.stmt.get ())));"
+ << endl;
+
+ os << db << "::transaction& tr (" << db << "::transaction::current ());"
+ << endl
+ << "// The connection used by the current transaction and the" << endl
+ << "// one used to prepare this statement must be the same." << endl
+ << "//" << endl
+ << "assert (q.verify_connection (tr));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "st->connection ().statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "pq.query.init_parameters ();"
+ << "st->execute ();";
+
+ post_query_ (c, false);
+
+ os << endl
+ << "return shared_ptr<result_impl> (" << endl
+ << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
+ << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << "}";
+ }
+
+ // Generate function table registration for dynamic multi-database
+ // support.
+ //
+ if (multi_dynamic)
+ {
+ string fn (flat_name (type));
+ string dt ("access::view_traits_impl< " + type + ", id_common >");
+
+ os << "static const" << endl
+ << dt << "::" << endl
+ << "function_table_type function_table_" << fn << "_ ="
+ << "{";
+
+ if (!options.omit_unprepared ())
+ os << "&" << traits << "::query";
+
+ if (options.generate_prepared ())
+ {
+ if (!options.omit_unprepared ())
+ os << "," << endl;
+
+ os << "&" << traits << "::prepare_query" << "," << endl
+ << "&" << traits << "::execute_query";
+ }
+
+ os << "};";
+
+ os << "static const view_function_table_entry< " << type << ", " <<
+ "id_" << db << " >" << endl
+ << "function_table_entry_" << fn << "_ (" << endl
+ << "&function_table_" << fn << "_);"
+ << endl;
+ }
+}
+
+namespace relational
+{
+ namespace source
+ {
+ static inline void
+ add_space (string& s)
+ {
+ string::size_type n (s.size ());
+ if (n != 0 && s[n - 1] != ' ')
+ s += ' ';
+ }
+
+ static string
+ translate_name_trailer (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt)
+ {
+ string r;
+
+ for (; tt != CPP_EOF; ptt = tt, tt = l.next (tl, &tn))
+ {
+ bool done (false);
+
+ switch (tt)
+ {
+ case CPP_SCOPE:
+ case CPP_DOT:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ // For names like 'foo::template bar'.
+ //
+ if (ptt == CPP_NAME || ptt == CPP_KEYWORD)
+ r += ' ';
+
+ r += tl;
+ }
+ else
+ done = true;
+
+ break;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ return r;
+ }
+
+ static class_::expression
+ translate_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ semantics::scope& start_scope,
+ location_t loc,
+ string const& prag,
+ bool check_ptr,
+ view_alias_map const& amap,
+ view_object_map const& omap)
+ {
+ using semantics::scope;
+ using semantics::data_member;
+ typedef class_::expression expression;
+
+ bool multi_obj ((amap.size () + omap.size ()) > 1);
+
+ bool fail (false);
+ string name;
+ string r ("query_columns");
+ context& ctx (context::current ());
+
+ // This code is quite similar to view_data_members in the type
+ // processor.
+ //
+ try
+ {
+ data_member* m (0);
+ view_object* vo (0);
+
+ // Check if this is an alias.
+ //
+ if (tt == CPP_NAME)
+ {
+ view_alias_map::const_iterator i (amap.find (tl));
+
+ if (i != amap.end ())
+ {
+ if (multi_obj)
+ {
+ r += "::";
+ r += i->first;
+ }
+
+ vo = i->second;
+ fail = true; // This must be a data member.
+
+ // Skip '::'.
+ //
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_SCOPE)
+ {
+ error (loc) << "member name expected after an alias in db " <<
+ "pragma " << prag << endl;
+ throw operation_failed ();
+ }
+
+ ptt = tt;
+ if (l.next (tl, &tn) != CPP_NAME)
+ throw lookup::invalid_name ();
+
+ m = &vo->obj->lookup<data_member> (tl, scope::include_hidden);
+
+ tt = l.next (tl, &tn);
+ }
+ }
+
+ // If it is not an alias, do the normal lookup.
+ //
+ if (vo == 0)
+ {
+ // Also get the object type. We need to do it so that
+ // we can get the correct (derived) object name (the
+ // member itself can come from a base class).
+ //
+ scope* s;
+ cpp_ttype ptt; // Not used.
+ m = &lookup::resolve_scoped_name<data_member> (
+ l, tt, tl, tn, ptt,
+ start_scope,
+ name,
+ false,
+ &s);
+
+ view_object_map::const_iterator i (
+ omap.find (dynamic_cast<semantics::class_*> (s)));
+
+ if (i == omap.end ())
+ {
+ // Not an object associated with this view. Assume it
+ // is some other valid name.
+ //
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+ }
+
+ vo = i->second;
+
+ if (multi_obj)
+ {
+ r += "::";
+ r += context::class_name (*vo->obj);
+ }
+ }
+
+ expression e (vo);
+ r += "::";
+ r += ctx.public_name (*m);
+
+ // Assemble the member path if we may need to return a pointer
+ // expression.
+ //
+ if (check_ptr)
+ e.member_path.push_back (m);
+
+ fail = true; // Now we definitely fail if anything goes wrong.
+
+ // Finally, resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; ptt = tt, tt = l.next (tl, &tn))
+ {
+ // Check if this member is actually of a composite value type.
+ // This is to handle expressions like "object::member.is_null ()"
+ // correctly. The remaining issue here is that in the future
+ // is_null()/is_not_null() will be valid for composite values
+ // as well.
+ //
+ semantics::class_* comp (
+ context::composite_wrapper (context::utype (*m)));
+ if (comp == 0)
+ break;
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (loc) << "name expected after '.' in db pragma " <<
+ prag << endl;
+ throw operation_failed ();
+ }
+
+ m = &comp->lookup<data_member> (tl, scope::include_hidden);
+
+ r += '.';
+ r += ctx.public_name (*m);
+
+ if (check_ptr)
+ e.member_path.push_back (m);
+ }
+
+ // If requested, check if this member is a pointer. We only do this
+ // if there is nothing after this name.
+ //
+ if (check_ptr && tt == CPP_EOF)
+ {
+ using semantics::type;
+
+ type* t;
+
+ if (context::container (*m))
+ t = &context::container_vt (*m);
+ else
+ t = &context::utype (*m);
+
+ if (context::object_pointer (*t))
+ return e;
+ }
+
+ // Read the remainder of the expression (e.g., '.is_null ()') if
+ // the member is not composite and we bailed out from the above
+ // loop.
+ //
+ if (tt == CPP_DOT)
+ r += translate_name_trailer (l, tt, tl, tn, ptt);
+
+ return expression (r);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ if (!fail)
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+
+ error (loc) << "invalid name in db pragma " << prag << endl;
+ throw operation_failed ();
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (!fail)
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+
+ if (e.type_mismatch)
+ error (loc) << "name '" << e.name << "' in db pragma " << prag <<
+ " does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << e.name <<
+ "' specified with db pragma " << prag << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << e.first.name () << "' " <<
+ "specified with db pragma " << prag << " is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to this " <<
+ "data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ class_::expression class_::
+ translate_expression (type& c,
+ cxx_tokens const& ts,
+ semantics::scope& scope,
+ location_t loc,
+ string const& prag,
+ bool* placeholder,
+ bool predicate)
+ {
+ // This code is similar to translate() from context.cxx.
+ //
+
+ // The overall idea is as folows: read in tokens and add them
+ // to the string. If a token starts a name, try to resolve it
+ // to an object member (taking into account aliases). If this
+ // was successful, translate it to the query column reference.
+ // Otherwise, output it as is.
+ //
+ // If the placeholder argument is not NULL, then we need to
+ // detect the special '(?)' token sequence and replace it
+ // with the query variable ('q').
+ //
+ expression e ("");
+ string& r (e.value);
+
+ view_alias_map const& amap (c.get<view_alias_map> ("alias-map"));
+ view_object_map const& omap (c.get<view_object_map> ("object-map"));
+
+ cxx_tokens_lexer l;
+ l.start (ts);
+
+ tree tn;
+ string tl;
+ for (cpp_ttype tt (l.next (tl, &tn)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_NOT:
+ {
+ add_space (r);
+ r += '!';
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ // Check if this is a pointer expression.
+ //
+ // If r is not empty, then it means this is not just the
+ // name. If placeholder is not 0, then we are translating
+ // a query expression, not a join condition.
+ //
+ expression e (
+ translate_name (
+ l, tt, tl, tn, ptt,
+ scope, loc, prag,
+ r.empty () && placeholder == 0, amap, omap));
+
+ if (e.kind == expression::literal)
+ r += e.value;
+ else
+ return e;
+
+ continue; // We have already extracted the next token.
+ }
+ case CPP_QUERY:
+ {
+ if (placeholder != 0 && !*placeholder)
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ *placeholder = true;
+
+ // Predicate is true if this is a SELECT statement clause.
+ // Otherwise it is a stored procedure parameters.
+ //
+ if (predicate)
+ r += "q.empty () ? query_base_type::true_expr : q";
+ else
+ {
+ r.resize (r.size () - 1); // Remove opening paren.
+ r += "q";
+ break; // Skip the closing paren as well.
+ }
+ }
+ else
+ {
+ // The same as in the default case.
+ //
+ add_space (r);
+ r += "? ";
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+
+ return e;
+ }
+
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class_> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ instance<include> i;
+ i->generate ();
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/odb/relational/source.hxx b/odb/odb/relational/source.hxx
new file mode 100644
index 0000000..3c6f5da
--- /dev/null
+++ b/odb/odb/relational/source.hxx
@@ -0,0 +1,7154 @@
+// file : odb/relational/source.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SOURCE_HXX
+#define ODB_RELATIONAL_SOURCE_HXX
+
+#include <map>
+#include <set>
+#include <list>
+#include <vector>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/schema.hxx>
+
+namespace relational
+{
+ namespace source
+ {
+ // Column literal in a statement (e.g., in select-list, etc).
+ //
+ struct statement_column
+ {
+ statement_column (): member (0) {}
+ statement_column (std::string const& tbl,
+ std::string const& col,
+ std::string const& t,
+ semantics::data_member& m,
+ std::string const& kp = "")
+ : table (tbl), column (col), type (t), member (&m), key_prefix (kp)
+ {
+ }
+
+ std::string table; // Schema-qualifed and quoted table name.
+ std::string column; // Table-qualifed and quoted column expr.
+ std::string type; // Column SQL type.
+ semantics::data_member* member;
+ std::string key_prefix;
+ };
+
+ typedef std::list<statement_column> statement_columns;
+
+ // Query parameter generator. A new instance is created for each
+ // query, so the customized version can have a counter to implement,
+ // for example, numbered parameters (e.g., $1, $2, etc). The auto_id()
+ // function is called instead of next() for the automatically-assigned
+ // object id member when generating the persist statement. If empty
+ // string is returned, then parameter is ignored.
+ //
+ struct query_parameters: virtual context
+ {
+ typedef query_parameters base;
+
+ query_parameters (statement_kind sk, qname const& table)
+ : sk_ (sk), table_ (table) {}
+
+ virtual string
+ next (semantics::data_member&,
+ const std::string& /*column*/, // Table qualified and quoted.
+ const std::string& /*sqlt*/)
+ {
+ return "?";
+ }
+
+ virtual string
+ auto_id (semantics::data_member& m,
+ const std::string& column,
+ const std::string& sqlt)
+ {
+ return next (m, column, sqlt);
+ }
+
+ string
+ next (const object_columns_list::column& c)
+ {
+ return next (*c.member, quote_id (c.name), c.type);
+ }
+
+ string
+ next (const statement_column& c)
+ {
+ return next (*c.member, c.column, c.type);
+ }
+
+ protected:
+ statement_kind sk_;
+ qname table_;
+ };
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ // If provided, used to resolve table/alias names for inverse
+ // pointed-to and base objects. Returns qualified name.
+ //
+ struct table_name_resolver
+ {
+ virtual string
+ resolve_pointer (semantics::data_member&) const = 0;
+
+ virtual string
+ resolve_base (semantics::class_&) const = 0;
+ };
+
+ object_columns (statement_kind sk,
+ statement_columns& sc,
+ query_parameters* param = 0,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
+ sk_ (sk),
+ ro_ (true),
+ sc_ (sc),
+ param_ (param),
+ table_name_resolver_ (0),
+ depth_ (1)
+ {
+ }
+
+ object_columns (statement_kind sk,
+ bool ignore_ro,
+ statement_columns& sc,
+ query_parameters* param)
+ : object_columns_base (true, true, 0),
+ sk_ (sk),
+ ro_ (ignore_ro),
+ sc_ (sc),
+ param_ (param),
+ table_name_resolver_ (0),
+ depth_ (1)
+ {
+ }
+
+ object_columns (std::string const& table_qname,
+ statement_kind sk,
+ statement_columns& sc,
+ size_t depth = 1,
+ object_section* section = 0,
+ table_name_resolver* tnr = 0)
+ : object_columns_base (true, true, section),
+ sk_ (sk),
+ ro_ (true),
+ sc_ (sc),
+ param_ (0),
+ table_name_ (table_qname),
+ table_name_resolver_ (tnr),
+ depth_ (depth)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements. Also include optimistic version into
+ // section's SELECT and UPDATE statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ()) ||
+ (version (mp) &&
+ (sk_ == statement_update || sk_ == statement_select));
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If we are generating a select statement and this is a derived
+ // type in a polymorphic hierarchy, then we need to include base
+ // columns, but do it in reverse order as well as switch the table
+ // name (base columns come from different tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
+ {
+ names (c);
+
+ if (sk_ == statement_select && --depth_ != 0)
+ {
+ semantics::class_& b (polymorphic_base (c));
+
+ table_name_ = table_name_resolver_ != 0
+ ? table_name_resolver_->resolve_base (b)
+ : table_qname (b);
+
+ inherits (c);
+ }
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references for select statements.
+ //
+ if (sk_ == statement_select && m.count ("polymorphic-ref"))
+ return;
+
+ data_member_path* imp (inverse (m, key_prefix_));
+
+ // Ignore certain columns depending on what kind of statement we are
+ // generating. Columns corresponding to the inverse members are
+ // only present in the select statements.
+ //
+ if (imp != 0 && sk_ != statement_select)
+ return;
+
+ // Inverse object pointers come from a joined table.
+ //
+ if (imp != 0)
+ {
+ bool poly (polymorphic (c) != 0);
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that table.
+ //
+ semantics::class_& imc (
+ poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
+
+ data_member_path& id (*id_member (imc));
+ semantics::type& idt (utype (id));
+
+ if (container (imb))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name. We don't assign join
+ // aliases for container tables so use the actual table name.
+ // Note that the if(!table_name_.empty ()) test may look wrong
+ // at first but it is not; if table_name_ is empty then we are
+ // generating a container table where we don't qualify columns
+ // with tables.
+ //
+ string table;
+
+ if (!table_name_.empty ())
+ {
+ if (table_name_resolver_ != 0)
+ table = table_name_resolver_->resolve_pointer (m);
+ else
+ table = table_qname (imc, *imp);
+ }
+
+ instance<object_columns> oc (table, sk_, sc_);
+ oc->traverse (imb, idt, "id", "object_id", &imc);
+ }
+ else
+ {
+ // Use the join alias instead of the actual table name unless we
+ // are handling a container. Generally, we want the join alias
+ // to be based on the column name. This is straightforward for
+ // single-column references. In case of a composite id, we will
+ // need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name. Note that the
+ // if(!table_name_.empty ()) test may look wrong at first but
+ // it is not; if table_name_ is empty then we are generating a
+ // container table where we don't qualify columns with tables.
+ //
+ string alias;
+
+ if (!table_name_.empty ())
+ {
+ if (table_name_resolver_ != 0)
+ alias = table_name_resolver_->resolve_pointer (m);
+ else
+ {
+ string n;
+
+ if (composite_wrapper (idt))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+
+ if (poly)
+ {
+ qname const& table (table_name (imc));
+ alias = quote_id (alias + "_" + table.uname ());
+ }
+ else
+ alias = quote_id (alias);
+ }
+ }
+
+ instance<object_columns> oc (alias, sk_, sc_);
+ oc->traverse (id);
+ }
+ }
+ else
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. Id and readonly columns are not present in the update
+ // statements.
+ //
+ if ((id () || readonly (member_path_, member_scope_)) &&
+ sk_ == statement_update && ro_)
+ return false;
+
+ return column (m, table_name_, quote_id (name));
+ }
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ string r;
+
+ if (!table.empty ())
+ {
+ r += table; // Already quoted.
+ r += '.';
+ }
+
+ r += column; // Already quoted.
+
+ string const& sqlt (column_type ());
+
+ // Version column (optimistic concurrency) requires special
+ // handling in the UPDATE statement.
+ //
+ //
+ if (sk_ == statement_update && version (m))
+ {
+ r += "=" + r + "+1";
+ }
+ else if (param_ != 0)
+ {
+ r += '=';
+ r += convert_to (param_->next (m, column, sqlt), sqlt, m);
+ }
+ else if (sk_ == statement_select)
+ r = convert_from (r, sqlt, m);
+
+ sc_.push_back (statement_column (table, r, sqlt, m, key_prefix_));
+ return true;
+ }
+
+ protected:
+ statement_kind sk_;
+ bool ro_;
+ statement_columns& sc_;
+ query_parameters* param_;
+ string table_name_;
+ table_name_resolver* table_name_resolver_;
+ size_t depth_;
+ };
+
+ struct view_columns: object_columns_base,
+ object_columns::table_name_resolver,
+ virtual context
+ {
+ typedef view_columns base;
+
+ view_columns (statement_columns& sc,
+ strings& from,
+ const view_relationship_map& rm)
+ : sc_ (sc), from_ (from), rel_map_ (rm), in_composite_ (false) {}
+
+ // Implementation of table_name_resolver for object_columns.
+ //
+ virtual string
+ resolve_pointer (semantics::data_member& m) const
+ {
+ view_object& us (*ptr_->get<view_object*> ("view-object"));
+
+ data_member_path& imp (*inverse (m));
+ semantics::data_member& imf (*imp.front ());
+ semantics::data_member& imb (*imp.back ());
+
+ using semantics::class_;
+ typedef view_relationship_map::const_iterator iterator;
+
+ std::pair<iterator, iterator> r (rel_map_.equal_range (imp));
+
+ for (; r.first != r.second; ++r.first)
+ {
+ if (r.first->second.second != &us) // Not our associated.
+ continue;
+
+ view_object& vo (*r.first->second.first); // First because inverse.
+
+ // Derive the table name the same way as the JOIN code.
+ //
+ class_* c (vo.obj);
+ if (class_* root = polymorphic (*c))
+ {
+ // Can be in base.
+ //
+ c = &static_cast<class_&> (imf.scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+ }
+
+ string const& a (vo.alias);
+
+ if (container (imb))
+ {
+ // If this is a container, then object_columns will use the
+ // column from the container table, not from the object table
+ // (which, strictly speaking, might not have been JOIN'ed).
+ //
+ qname t (table_name (*c, imp));
+ return a.empty ()
+ ? quote_id (t)
+ : quote_id (a + '_' + t.uname ());
+ }
+ else
+ {
+ qname t;
+ if (a.empty ())
+ t = table_name (*c);
+ else
+ {
+ if (polymorphic (*c))
+ t = qname (a + "_" + table_name (*c).uname ());
+ else
+ t = qname (a);
+ }
+ return quote_id (t);
+ }
+ }
+
+ // So there is no associated object for this column. The initial
+ // plan was to complain and ask the user to explicitly associate
+ // the object. This is not a bad plan except for one thing: if
+ // the direct side of the relationship is a container, then
+ // associating that object explicitly will result in both the
+ // container table and the object table being JOIN'ed. But we
+ // only need the container table (for the object id) So we will
+ // be joining a table for nothing, which is not very clean. So
+ // the alternative, and more difficult, plan is to go ahead and
+ // add the necessary JOIN's automatically.
+ //
+ // This code follows the normal JOIN generation code.
+ //
+ class_* o (object_pointer (utype (m)));
+ if (class_* root = polymorphic (*o))
+ {
+ o = &static_cast<class_&> (imf.scope ());
+
+ if (!polymorphic (*o))
+ o = root;
+ }
+
+ string const& a (us.alias);
+ string lt (a.empty () ? table_qname (*us.obj) : quote_id (a));
+ string rt;
+ qname ct (container (imb) ? table_name (*o, imp) : table_name (*o));
+
+ string l ("LEFT JOIN ");
+
+ if (a.empty ())
+ {
+ rt = quote_id (ct);
+ l += rt;
+ }
+ else
+ {
+ // The same relationship can be used by multiple associated
+ // objects. So if we have an alias, then also construct one
+ // for the table that we are joining.
+ //
+ rt = quote_id (a + '_' + ct.uname ());
+ l += quote_id (ct);
+ l += (need_alias_as ? " AS " : " ") + rt;
+ }
+
+ l += " ON";
+ from_.push_back (l);
+
+ instance<object_columns_list> l_cols; // Our id columns.
+ instance<object_columns_list> r_cols; // Other side id columns.
+
+ data_member_path& id (*id_member (*us.obj));
+
+ l_cols->traverse (id);
+
+ if (container (imb))
+ r_cols->traverse (imb, utype (id), "value", "value");
+ else
+ r_cols->traverse (imb, column_prefix (imp));
+
+ for (object_columns_list::iterator b (l_cols->begin ()), i (b),
+ j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += lt;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += rt;
+ l += '.';
+ l += quote_id (j->name);
+
+ from_.push_back (strlit (l));
+ }
+
+ return rt;
+
+ /*
+ // The alternative implementation:
+ //
+ location const& l1 (m.location ());
+ location const& l2 (ptr_->location ());
+
+ string n1 (class_name (*object_pointer (utype (m))));
+ string n2 (class_name (*object_pointer (utype (*ptr_))));
+
+ error (l1) << "object '" << n1 << "' pointed-to by the inverse "
+ << "data member in object '" << n2 << "' must be "
+ << "explicitly associated with the view" << endl;
+
+ info (l2) << "view data member that loads '" << n2 << "' is "
+ << "defined here" << endl;
+
+ throw operation_failed ();
+ */
+ }
+
+ virtual string
+ resolve_base (semantics::class_& b) const
+ {
+ view_object& vo (*ptr_->get<view_object*> ("view-object"));
+
+ qname t (vo.alias.empty ()
+ ? table_name (b)
+ : qname (vo.alias + "_" + table_name (b).uname ()));
+
+ return quote_id (t);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+
+ view_object& vo (*m.get<view_object*> ("view-object"));
+ string const& a (vo.alias);
+
+ qname t;
+ if (a.empty ())
+ t = table_name (c);
+ else
+ {
+ if (poly)
+ t = qname (a + "_" + table_name (c).uname ());
+ else
+ t = qname (a);
+ }
+ string qt (quote_id (t));
+
+ ptr_ = &m;
+
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> oc (qt, sk, sc_, poly_depth, s, this);
+ oc->traverse (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* pm, semantics::class_& c)
+ {
+ if (in_composite_)
+ {
+ object_columns_base::traverse_composite (pm, c);
+ return;
+ }
+
+ // Override the column prerix.
+ //
+ semantics::data_member& m (*pm);
+
+ // If we have literal column specified, use that.
+ //
+ if (m.count ("column"))
+ {
+ table_column const& tc (m.get<table_column> ("column"));
+
+ if (!tc.table.empty ())
+ table_prefix_ = tc.table;
+
+ column_prefix_ = object_columns_base::column_prefix (m);
+ }
+ // Otherwise, see if there is a column expression. For composite
+ // members in a view, this should be a single reference.
+ //
+ else if (m.count ("column-expr"))
+ {
+ column_expr const& e (m.get<column_expr> ("column-expr"));
+
+ if (e.size () > 1)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column expression specified for a data member "
+ << "of a composite value type" << endl;
+
+ throw operation_failed ();
+ }
+
+ data_member_path const& mp (e.back ().member_path);
+
+ if (mp.size () > 1)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: invalid data member in db pragma column"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ table_prefix_ = e.back ().table;
+ column_prefix_ = object_columns_base::column_prefix (*mp.back ());
+ }
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: no column prefix provided for a view data member"
+ << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: use db pragma column to specify the column prefix"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ in_composite_ = true;
+ object_columns_base::traverse_composite (pm, c);
+ in_composite_ = false;
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ string tbl;
+ string col;
+
+ // If we are inside a composite value, use the standard
+ // column name machinery.
+ //
+ if (in_composite_)
+ {
+ if (!table_prefix_.empty ())
+ {
+ tbl = quote_id (table_prefix_);
+ col += tbl;
+ col += '.';
+ }
+
+ col += quote_id (name);
+ }
+ // If we have literal column specified, use that.
+ //
+ else if (m.count ("column"))
+ {
+ table_column const& tc (m.get<table_column> ("column"));
+
+ if (!tc.expr)
+ {
+ if (!tc.table.empty ())
+ {
+ tbl = quote_id (tc.table);
+ col += tbl;
+ col += '.';
+ }
+
+ col += quote_id (tc.column);
+ }
+ else
+ col += tc.column;
+ }
+ // Otherwise, see if there is a column expression.
+ //
+ else if (m.count ("column-expr"))
+ {
+ column_expr const& e (m.get<column_expr> ("column-expr"));
+
+ for (column_expr::const_iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ switch (i->kind)
+ {
+ case column_expr_part::literal:
+ {
+ col += i->value;
+ break;
+ }
+ case column_expr_part::reference:
+ {
+ tbl = quote_id (i->table);
+ col += tbl;
+ col += '.';
+ col += column_qname (i->member_path);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: no column name provided for a view data member"
+ << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: use db pragma column to specify the column name"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ return column (m, tbl, col);
+ }
+
+ // The column argument is a qualified and quoted column or
+ // expression.
+ //
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ string const& sqlt (column_type ());
+ sc_.push_back (
+ statement_column (
+ table, convert_from (column, sqlt, m), sqlt, m));
+ return true;
+ }
+
+ protected:
+ statement_columns& sc_;
+ strings& from_;
+ const view_relationship_map& rel_map_;
+
+ bool in_composite_;
+ qname table_prefix_; // Table corresponding to column_prefix_;
+
+ // Set to the current pointer data member that we are traversing.
+ //
+ semantics::data_member* ptr_;
+ };
+
+ struct polymorphic_object_joins: object_columns_base, virtual context
+ {
+ typedef polymorphic_object_joins base;
+
+ polymorphic_object_joins (semantics::class_& obj,
+ bool query,
+ size_t depth,
+ string const& alias = "",
+ user_section* section = 0)
+ : object_columns_base (true, true),
+ obj_ (obj),
+ query_ (query),
+ depth_ (depth),
+ section_ (section),
+ alias_ (alias)
+ {
+ // Get the table and id columns.
+ //
+ table_ = alias_.empty ()
+ ? table_qname (obj_)
+ : quote_id (alias_ + "_" + table_name (obj_).uname ());
+
+ cols_->traverse (*id_member (obj_));
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If section is specified, skip bases that don't add anything
+ // to load.
+ //
+ bool skip (false), stop (false);
+ if (section_ != 0)
+ {
+ skip = true;
+
+ if (section_->object == &c)
+ {
+ user_section& s (*section_);
+
+ if (s.total != 0 || s.optimistic ())
+ skip = false;
+
+ section_ = s.base; // Move to the next base.
+
+ if (section_ == 0)
+ stop = true; // Stop at this base if there are no more overrides.
+ }
+ }
+ // Skip intermediates that don't add any data members.
+ //
+ else if (!query_)
+ {
+ column_count_type const& cc (column_count (c));
+ if (cc.total == cc.id + cc.separate_load)
+ skip = true;
+ }
+
+ if (!skip)
+ {
+ std::ostringstream cond;
+
+ qname table (table_name (c));
+ string alias (alias_.empty ()
+ ? quote_id (table)
+ : quote_id (alias_ + "_" + table.uname ()));
+
+ for (object_columns_list::iterator b (cols_->begin ()), i (b);
+ i != cols_->end ();
+ ++i)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ string qn (quote_id (i->name));
+ cond << alias << '.' << qn << '=' << table_ << '.' << qn;
+ }
+
+ string line ("LEFT JOIN " + quote_id (table));
+
+ if (!alias_.empty ())
+ line += (need_alias_as ? " AS " : " ") + alias;
+
+ line += " ON " + cond.str ();
+
+ joins.push_back (line);
+ }
+
+ if (!stop && --depth_ != 0)
+ inherits (c);
+ }
+
+ public:
+ strings joins;
+
+ strings::const_iterator
+ begin () const {return joins.begin ();}
+
+ strings::const_iterator
+ end () const {return joins.end ();}
+
+ private:
+ semantics::class_& obj_;
+ bool query_;
+ size_t depth_;
+ user_section* section_;
+ string alias_;
+ string table_;
+ instance<object_columns_list> cols_;
+ };
+
+ struct object_joins: object_columns_base, virtual context
+ {
+ typedef object_joins base;
+
+ //@@ context::{cur,top}_object; might have to be created every time.
+ //
+ object_joins (semantics::class_& scope,
+ bool query,
+ size_t depth,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
+ query_ (query),
+ depth_ (depth),
+ table_ (table_qname (scope)),
+ id_ (*id_member (scope))
+ {
+ id_cols_->traverse (id_);
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (*section_ == main_section && !s.separate_load ());
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to include base joins, but do it in reverse order as well
+ // as switch the table name (base columns come from different
+ // tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
+ {
+ names (c);
+
+ if (query_ || --depth_ != 0)
+ {
+ table_ = table_qname (polymorphic_base (c));
+ inherits (c);
+ }
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references; they are joined by
+ // polymorphic_object_joins in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ string t, a, dt, da;
+ std::ostringstream cond, dcond; // @@ diversion?
+
+ // Derive table alias for this member. Generally, we want the
+ // alias to be based on the column name. This is straightforward
+ // for single-column references. In case of a composite id, we
+ // will need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name.
+ //
+ string alias;
+ {
+ string n;
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+ }
+
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+
+ semantics::class_* joined_obj (0);
+
+ if (data_member_path* imp = inverse (m, key_prefix_))
+ {
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that table.
+ //
+ semantics::class_& imc (
+ poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
+
+ if (container (imb))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name.
+ //
+ t = table_qname (imc, *imp);
+
+ // Container's value is our id.
+ //
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (imb, utype (id_), "value", "value");
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << t << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ // Add the join for the object itself so that we are able to
+ // use it in the WHERE clause.
+ //
+ if (query_)
+ {
+ // Here we can use the most derived class instead of the
+ // one containing the inverse member.
+ //
+ qname const& table (table_name (c));
+
+ dt = quote_id (table);
+ da = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ data_member_path& id (*id_member (c));
+
+ instance<object_columns_list> oid_cols, cid_cols;
+ oid_cols->traverse (id);
+ cid_cols->traverse (imb, utype (id), "id", "object_id", &c);
+
+ for (object_columns_list::iterator b (cid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != cid_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ dcond << " AND ";
+
+ dcond << da << '.' << quote_id (j->name) << '=' <<
+ t << '.' << quote_id (i->name);
+ }
+
+ joined_obj = &c;
+ }
+ }
+ else
+ {
+ qname const& table (table_name (imc));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (imb, column_prefix (*imp));
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ // If we are generating query, JOIN base/derived classes so
+ // that we can use their data in the WHERE clause.
+ //
+ if (query_)
+ joined_obj = &imc;
+ }
+ }
+ else if (query_)
+ {
+ // We need the join to be able to use the referenced object
+ // in the WHERE clause.
+ //
+ qname const& table (table_name (c));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ instance<object_columns_list> oid_cols (column_prefix_);
+ oid_cols->traverse (m);
+
+ instance<object_columns_list> pid_cols;
+ pid_cols->traverse (*id_member (c));
+
+ for (object_columns_list::iterator b (pid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != pid_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ joined_obj = &c;
+ }
+
+ if (!t.empty ())
+ {
+ string line ("LEFT JOIN ");
+ line += t;
+
+ if (!a.empty ())
+ line += (need_alias_as ? " AS " : " ") + a;
+
+ line += " ON ";
+ line += cond.str ();
+
+ joins.push_back (line);
+ }
+
+ // Add dependent join (i.e., an object table join via the
+ // container table).
+ //
+ if (!dt.empty ())
+ {
+ string line ("LEFT JOIN ");
+ line += dt;
+
+ if (!da.empty ())
+ line += (need_alias_as ? " AS " : " ") + da;
+
+ line += " ON ";
+ line += dcond.str ();
+
+ joins.push_back (line);
+ }
+
+ // If we joined the object that is part of a polymorphic type
+ // hierarchy, then we may need join its bases as well as its
+ // derived types. This is only done for queries.
+ //
+ if (joined_obj != 0 && poly)
+ {
+ size_t depth (polymorphic_depth (*joined_obj));
+
+ // Join "up" (derived).
+ //
+ if (joined_obj != &c)
+ {
+ bool t (true); //@@ (im)perfect forward.
+ size_t d (polymorphic_depth (c) - depth); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
+ j->traverse (c);
+ joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
+ }
+
+ // Join "down" (base).
+ //
+ if (joined_obj != poly_root)
+ {
+ bool t (true); //@@ (im)perfect forward.
+ size_t d (depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
+ j->traverse (polymorphic_base (*joined_obj));
+ joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
+ }
+ }
+ }
+
+ public:
+ strings joins;
+
+ strings::const_iterator
+ begin () const {return joins.begin ();}
+
+ strings::const_iterator
+ end () const {return joins.end ();}
+
+ private:
+ bool query_;
+ size_t depth_;
+ string table_;
+ data_member_path& id_;
+ instance<object_columns_list> id_cols_;
+ };
+
+ // Check that eager object pointers in the objects that a view loads
+ // can be loaded from the cache (i.e., they have session support
+ // enabled).
+ //
+ struct view_object_check: object_members_base
+ {
+ typedef view_object_check base;
+
+ view_object_check (view_object& vo, view_relationship_map& rm)
+ : object_members_base (false, &main_section),
+ session_ (false), vo_ (vo), rel_map_ (rm) {}
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ // Include eager loaded members into the main section.
+ //
+ object_section& s (section (mp));
+ return *section_ == s || !s.separate_load ();
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ check (m, inverse (m), utype (m), c);
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::type& vt (container_vt (m));
+
+ if (semantics::class_* cvt = composite_wrapper (vt))
+ {
+ // Check this composite value for any pointers.
+ //
+ instance<view_object_check> t (vo_, rel_map_);
+ t->traverse (*cvt);
+
+ session_ = session_ || t->session_;
+ }
+ else if (semantics::class_* c = object_pointer (vt))
+ check (m, inverse (m, "value"), vt, *c);
+ }
+
+ void
+ check (semantics::data_member& m,
+ data_member_path* imp,
+ semantics::type& pt, // Pointer type.
+ semantics::class_& c)
+ {
+ // We don't care about lazy pointers.
+ //
+ if (lazy_pointer (pt))
+ return;
+
+ // First check the pointed-to object recursively.
+ //
+ if (!c.count ("view-object-check-seen"))
+ {
+ c.set ("view-object-check-seen", true);
+ instance<view_object_check> t (vo_, rel_map_);
+ t->traverse (c);
+
+ // We may come again for another view.
+ //
+ c.remove ("view-object-check-seen");
+
+ session_ = session_ || t->session_;
+ }
+
+ // See if the pointed-to object in this relationship is loaded
+ // by this view.
+ //
+ typedef view_relationship_map::const_iterator iterator;
+
+ std::pair<iterator, iterator> r (
+ rel_map_.equal_range (imp != 0 ? *imp : member_path_));
+
+ if (r.first == r.second)
+ return; // This relationship does not figure in the view.
+
+ view_object& vo (*(imp != 0
+ ? r.first->second.first
+ : r.first->second.second));
+
+ assert (vo.obj == &c); // Got the above right?
+
+ if (vo.ptr == 0)
+ return; // This object is not loaded by the view.
+
+ // The pointed-to object in this relationship is loaded
+ // by the view. The hard question, of course, is whether
+ // it has anything to do with us. We assume it does.
+ //
+ if (!session (c))
+ {
+ // Always direct data member.
+ //
+ semantics::class_& v (
+ dynamic_cast<semantics::class_&> (vo.ptr->scope ()));
+
+ location const& l1 (c.location ());
+ location const& l2 (m.location ());
+ location const& l3 (vo_.ptr->location ());
+ location const& l4 (vo.ptr->location ());
+
+ string on (class_name (c));
+ string vn (class_name (v));
+
+ error (l1) << "object '" << on << "' has session support disabled "
+ << "but may be loaded by view '" << vn << "' via "
+ << "several data members" << endl;
+
+ info (l2) << "indirectly via this data member..." << endl;
+ info (l3) << "...as a result of this object load" << endl;
+ info (l4) << "and directly as a result of this load" << endl;
+ info (l1) << "session support is required to only load one copy "
+ << "of the object" << endl;
+ info (l1) << "and don't forget to create a session instance when "
+ << "using this view" << endl;
+
+ throw operation_failed ();
+ }
+
+ session_ = true;
+ }
+
+ bool session_;
+
+ private:
+ view_object& vo_;
+ view_relationship_map& rel_map_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: virtual member_base
+ {
+ typedef bind_member base;
+
+ // NULL section means we are generating object bind().
+ //
+ bind_member (string const& var = string (),
+ string const& arg = string (),
+ object_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ arg_override_ (arg) {}
+
+ bind_member (string const& var,
+ string const& arg,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ arg_override_ (arg) {}
+
+ protected:
+ string arg_override_;
+ };
+
+ template <typename T>
+ struct bind_member_impl: bind_member, virtual member_base_impl<T>
+ {
+ typedef bind_member_impl base_impl;
+
+ bind_member_impl (base const& x): base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ // Treat version as present in every section.
+ //
+ if (section_ != 0 && !version (mi.m) && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are bound in a special
+ // way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ std::ostringstream ostr;
+ ostr << "b[n]";
+ b = ostr.str ();
+
+ arg = arg_override_.empty () ? string ("i") : arg_override_;
+
+ if (var_override_.empty ())
+ {
+ // Ignore inverse, separately-loaded members in the main
+ // section (nothing to persist).
+ //
+ if (section_ == 0 && separate_load (mi.m) && inverse (mi.m))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // Order of these tests is important.
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ os << "if (sk != statement_insert && sk != statement_update)"
+ << "{";
+ else if (section_ == 0 && separate_load (mi.m))
+ os << "if (sk == statement_insert)"
+ << "{";
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ os << "if (sk == statement_select)"
+ << "{";
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ else if (!readonly (*context::top_object))
+ {
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ (comp != 0 && readonly (*comp)) ||
+ (section_ == 0 && separate_update (mi.m)))
+ os << "if (sk != statement_update)"
+ << "{";
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (var_override_.empty ())
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ // We need to increment the index even if we skipped this
+ // member due to the schema version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ {
+ // See column_count_impl for details on what's going on here.
+ //
+ column_count_type cc;
+ if (semantics::class_* root = polymorphic (*mi.ptr))
+ {
+ for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (*mi.ptr);
+
+ os << "n += " << cc.total - cc.separate_load << "UL;";
+ }
+ else if (comp != 0)
+ {
+ bool ro (readonly (*comp));
+ column_count_type const& cc (column_count (*comp));
+
+ os << "n += " << cc.total << "UL";
+
+ // select = total
+ // insert = total - inverse
+ // update = total - inverse - readonly
+ //
+ if (cc.inverse != 0 || (!ro && cc.readonly != 0))
+ {
+ os << " - (" << endl
+ << "sk == statement_select ? 0 : ";
+
+ if (cc.inverse != 0)
+ os << cc.inverse << "UL";
+
+ if (!ro && cc.readonly != 0)
+ {
+ if (cc.inverse != 0)
+ os << " + ";
+
+ os << "(" << endl
+ << "sk == statement_insert ? 0 : " <<
+ cc.readonly << "UL)";
+ }
+
+ os << ")";
+ }
+
+ os << ";";
+ }
+ else
+ os << "n++;";
+
+ bool block (false);
+
+ // The same logic as in pre().
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ block = true;
+ else if (section_ == 0 && separate_load (mi.m))
+ block = true;
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ block = true;
+ else if (!readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c)) ||
+ (section_ == 0 && separate_update (mi.m)))
+ block = true;
+ }
+
+ if (block)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ semantics::class_& c (*mi.ptr);
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ os << "object_traits_impl< " << class_fq_name (c) << ", id_" <<
+ db << " >::bind (" << endl
+ << "b + n, " << (poly_derived ? "0, 0, " : "") << arg << "." <<
+ mi.var << "value, sk" << (versioned (c) ? ", svm" : "") << ");";
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "composite_value_traits< " << mi.fq_type () << ", id_" <<
+ db << " >::bind (" << endl
+ << "b + n, " << arg << "." << mi.var << "value, sk" <<
+ (versioned (*composite (mi.t)) ? ", svm" : "") << ");";
+ }
+
+ protected:
+ string b;
+ string arg;
+ };
+
+ struct bind_base: traversal::class_, virtual context
+ {
+ typedef bind_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ // If the derived class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ bool ro (readonly (c));
+ bool check (ro && !readonly (*context::top_object));
+
+ if (check)
+ os << "if (sk != statement_update)"
+ << "{";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::bind (b + n, i, sk" <<
+ (versioned (c) ? ", svm" : "") << ");";
+
+ column_count_type const& cc (column_count (c));
+
+ os << "n += ";
+
+ // select = total - separate_load
+ // insert = total - inverse - optimistic_managed - id(auto & !sending)
+ // update = total - inverse - optimistic_managed - id - readonly -
+ // separate_update
+ //
+ size_t select (cc.total - cc.separate_load);
+ size_t insert (cc.total - cc.inverse - cc.optimistic_managed);
+ size_t update (insert - cc.id - cc.readonly - cc.separate_update);
+
+ data_member_path* id;
+ if (!insert_send_auto_id && (id = id_member (c)) != 0 && auto_ (*id))
+ insert -= cc.id;
+
+ if (select == insert && insert == update)
+ os << select << "UL;";
+ else if (select != insert && insert == update)
+ os << "sk == statement_select ? " << select << "UL : " <<
+ insert << "UL;";
+ else if (select == insert && insert != update)
+ os << "sk == statement_update ? " << update << "UL : " <<
+ select << "UL;";
+ else
+ os << "sk == statement_select ? " << select << "UL : " <<
+ "sk == statement_insert ? " << insert << "UL : " <<
+ update << "UL;";
+
+ if (check)
+ os << "}";
+ else
+ os << endl;
+ }
+ };
+
+ //
+ // grow
+ //
+
+ struct grow_member: virtual member_base
+ {
+ typedef grow_member base;
+
+ grow_member (size_t& index,
+ string const& var = string (),
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ index_ (index) {}
+
+ grow_member (size_t& index,
+ string const& var,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix), index_ (index) {}
+
+ protected:
+ size_t& index_;
+ };
+
+ template <typename T>
+ struct grow_member_impl: grow_member, virtual member_base_impl<T>
+ {
+ typedef grow_member_impl base_impl;
+
+ grow_member_impl (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ // If we have a key prefix (container), then it can't be in a section
+ // (while mi.m can). The same for top-level -- if we got called, then
+ // we shouldn't ignore it. (Same logic as in has_grow_member).
+ //
+ if (!(!key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m))))
+ return false;
+
+ // Ignore polymorphic id references; they are not returned by
+ // the select statement.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ std::ostringstream ostr;
+ ostr << "t[" << index_ << "UL]";
+ e = ostr.str ();
+
+ if (var_override_.empty ())
+ {
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ semantics::class_* comp (composite (mi.t));
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ if (var_override_.empty ())
+ {
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ {
+ // See column_count_impl for details on what's going on here.
+ //
+ column_count_type cc;
+ if (semantics::class_* root = polymorphic (*mi.ptr))
+ {
+ for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (*mi.ptr);
+
+ index_ += cc.total - cc.separate_load;
+ }
+ else if (comp != 0)
+ index_ += column_count (*comp).total;
+ else
+ index_++;
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment. They
+ // can only be immediate members of the view class.
+ //
+ if (view_member (mi.m))
+ {
+ semantics::class_& c (*mi.ptr);
+
+ os << "if (object_traits_impl< " << class_fq_name (c) <<
+ ", id_" << db << " >::grow (" << endl
+ << "i." << mi.var << "value, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ semantics::class_& c (*composite (mi.t));
+
+ os << "if (composite_value_traits< " << mi.fq_type () <<
+ ", id_" << db << " >::grow (" << endl
+ << "i." << mi.var << "value, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+ }
+
+ protected:
+ string e;
+ };
+
+ struct grow_base: traversal::class_, virtual context
+ {
+ typedef grow_base base;
+
+ grow_base (size_t& index): index_ (index) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ os << "if (";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::grow (" << endl
+ << "i, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+
+ index_ += column_count (c).total;
+ }
+
+ protected:
+ size_t& index_;
+ };
+
+ //
+ // init image
+ //
+
+ struct init_image_member: virtual member_base
+ {
+ typedef init_image_member base;
+
+ init_image_member (string const& var = string (),
+ string const& member = string (),
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ member_override_ (member)
+ {
+ }
+
+ init_image_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ member_override_ (member)
+ {
+ }
+
+ protected:
+ string member_override_;
+ };
+
+ template <typename T>
+ struct init_image_member_impl: init_image_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_image_member_impl base_impl;
+
+ init_image_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual void
+ set_null (member_info&) = 0;
+
+ virtual void
+ check_accessor (member_info&, member_access&) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // Ignore containers (they get their own table) and inverse
+ // object pointers (they are not present in this binding).
+ //
+ if (container (mi) || inverse (mi.m, key_prefix_))
+ return false;
+
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ if (!member_override_.empty ())
+ {
+ member = member_override_;
+ os << "{";
+ }
+ else
+ {
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
+ // If we don't send auto id in INSERT statement, ignore this
+ // member altogether (we never send auto id in UPDATE).
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ return false;
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ if (!readonly (*context::top_object))
+ {
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ (section_ == 0 && separate_update (mi.m)) ||
+ (comp != 0 && readonly (*comp))) // Can't be id.
+ {
+ // If we are generating section init(), then sk can only be
+ // statement_update.
+ //
+ if (section_ == 0)
+ os << "if (sk == statement_insert)";
+ }
+ }
+
+ os << "{";
+
+ if (discriminator (mi.m))
+ member = "di.discriminator";
+ else
+ {
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("get"));
+
+ // Make sure this kind of member can be accessed with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (comp == 0)
+ check_accessor (mi, ma);
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Use the original type to form the const reference. VC++
+ // cannot grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, true, "v") << " =" << endl
+ << " " << ma.translate ("o") << ";"
+ << endl;
+
+ member = "v";
+ }
+ }
+
+ // Translate.
+ //
+ if (mi.ct != 0)
+ {
+ os << "// From " << location_string (mi.ct->loc, true) << endl
+ << type_ref_type (*mi.ct->as, mi.ct->as_hint, true, "vt") <<
+ " =" << endl
+ << " " << mi.ct->translate_to (member) << ";"
+ << endl;
+
+ member = "vt";
+ }
+
+ // If this is a wrapped composite value, then we need to "unwrap"
+ // it. If this is a NULL wrapper, then we also need to handle that.
+ // For simple values this is taken care of by the value_traits
+ // specializations.
+ //
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ // The wrapper type, not the wrapped type.
+ //
+ string const& wt (mi.fq_type (false));
+
+ // If this is a NULL wrapper and the member can be NULL, then
+ // we need to handle the NULL value.
+ //
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ {
+ os << "if (wrapper_traits< " << wt << " >::get_null (" <<
+ member << "))" << endl
+ << "composite_value_traits< " << mi.fq_type () << ", id_" <<
+ db << " >::set_null (" << endl
+ << "i." << mi.var << "value, sk" <<
+ (versioned (*comp) ? ", svm" : "") << ");"
+ << "else"
+ << "{";
+ }
+
+ os << "const" << mi.fq_type () << "& vw = " << endl
+ << " wrapper_traits< " + wt + " >::get_ref (" + member + ");"
+ << endl;
+
+ member = "vw";
+ }
+
+ if (discriminator (mi.m))
+ os << "const info_type& di (map->find (typeid (o)));"
+ << endl;
+
+ if (mi.ptr != 0)
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (utype (mi.m, key_prefix_));
+
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;";
+
+ if (weak_pointer (pt))
+ {
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > wptr_traits;"
+ << "typedef odb::pointer_traits< wptr_traits::" <<
+ "strong_pointer_type > ptr_traits;"
+ << endl
+ << "wptr_traits::strong_pointer_type sp (" <<
+ "wptr_traits::lock (" << member << "));";
+
+ member = "sp";
+ }
+ else
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
+ << "if (!is_null)"
+ << "{"
+ << "const " << type << "& ptr_id (" << endl;
+
+ if (lazy_pointer (pt))
+ os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
+ member << ")";
+ else
+ os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
+
+ os << ");"
+ << endl;
+
+ member = "ptr_id";
+ }
+ else if (comp != 0)
+ type = mi.fq_type ();
+ else
+ {
+ type = mi.fq_type ();
+
+ // Indicate to the value_traits whether this column can be NULL.
+ //
+ os << "bool is_null (" << null (mi.m, key_prefix_) << ");";
+ }
+
+ if (comp != 0)
+ traits = "composite_value_traits< " + type + ", id_" +
+ db.string () + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = db.string () + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ if (mi.ptr != 0)
+ {
+ os << "}"
+ << "else" << endl;
+
+ if (!null (mi.m, key_prefix_))
+ os << "throw null_pointer ();";
+ else if (comp != 0)
+ os << traits << "::set_null (i." << mi.var << "value, sk" <<
+ (versioned (*comp) ? ", svm" : "") << ");";
+ else
+ set_null (mi);
+ }
+
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ os << "}";
+ }
+
+ os << "}";
+
+ if (member_override_.empty ())
+ {
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ bool grow (generate_grow &&
+ context::grow (mi.m, mi.t, mi.ct, key_prefix_));
+
+ if (grow)
+ os << "if (";
+
+ os << traits << "::init (" << endl
+ << "i." << mi.var << "value," << endl
+ << member << "," << endl
+ << "sk";
+
+ if (versioned (*composite (mi.t)))
+ os << "," << endl
+ << "svm";
+
+ os << ")";
+
+ if (grow)
+ os << ")" << endl
+ << "grew = true";
+
+ os << ";";
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string member;
+ string traits;
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
+ struct init_image_base: traversal::class_, virtual context
+ {
+ typedef init_image_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ // If the derived class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ bool check (readonly (c) && !readonly (*context::top_object));
+
+ if (check)
+ os << "if (sk != statement_update)"
+ << "{";
+
+ if (generate_grow)
+ os << "if (";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::init (i, o, sk" <<
+ (versioned (c) ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "grew = true";
+
+ os << ";";
+
+ if (check)
+ os << "}";
+ else
+ os << endl;
+ }
+ };
+
+ //
+ // init value
+ //
+
+ struct init_value_member: virtual member_base
+ {
+ typedef init_value_member base;
+
+ init_value_member (string const& member = string (),
+ string const& var = string (),
+ bool ignore_implicit_discriminator = true,
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ member_override_ (member),
+ ignore_implicit_discriminator_ (ignore_implicit_discriminator)
+ {
+ }
+
+ init_value_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ member_override_ (member),
+ ignore_implicit_discriminator_ (true)
+ {
+ }
+
+ virtual void
+ get_null (string const& /*var*/) const {};
+
+ protected:
+ string member_override_;
+ bool ignore_implicit_discriminator_;
+ };
+
+ template <typename T>
+ struct init_value_member_impl: init_value_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_value_member_impl base_impl;
+
+ init_value_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual void
+ get_null (string const& var) const = 0;
+
+ virtual void
+ check_modifier (member_info&, member_access&) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ // Ignore implicit discriminators.
+ //
+ if (ignore_implicit_discriminator_ && discriminator (mi.m))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ if (!member_override_.empty ())
+ {
+ os << "{";
+ member = member_override_;
+ }
+ else
+ {
+ // Ignore separately loaded members.
+ //
+ if (section_ == 0 && separate_load (mi.m))
+ return false;
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ return true; // That's enough for the object pointer in view.
+
+ // Set the member using the modifier expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ // Make sure this kind of member can be modified with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (comp == 0)
+ check_modifier (mi, ma);
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (ma.placeholder ())
+ os << member_val_type (mi.m, false, "v") << ";"
+ << endl;
+ else
+ {
+ // Use the original type to form the reference. VC++ cannot
+ // grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, false, "v") << " =" << endl
+ << " ";
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (mi.cq && ma.direct ());
+ if (cast)
+ os << "const_cast< " << member_ref_type (mi.m, false) <<
+ " > (" << endl;
+
+ os << ma.translate ("o");
+
+ if (cast)
+ os << ")";
+
+ os << ";"
+ << endl;
+ }
+
+ member = "v";
+ }
+
+ // Translate.
+ //
+ if (mi.ct != 0)
+ {
+ os << type_val_type (*mi.ct->as, mi.ct->as_hint, false, "vt") << ";"
+ << endl;
+
+ translate_member = member;
+ member = "vt";
+ }
+
+ // If this is a wrapped composite value, then we need to "unwrap" it.
+ // If this is a NULL wrapper, then we also need to handle that. For
+ // simple values this is taken care of by the value_traits
+ // specializations.
+ //
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ // The wrapper type, not the wrapped type.
+ //
+ string const& wt (mi.fq_type (false));
+
+ // If this is a NULL wrapper and the member can be NULL, then
+ // we need to handle the NULL value.
+ //
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ {
+ os << "if (composite_value_traits< " << mi.fq_type () <<
+ ", id_" << db << " >::get_null (" << endl
+ << "i." << mi.var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << "))" << endl
+ << "wrapper_traits< " << wt << " >::set_null (" << member + ");"
+ << "else"
+ << "{";
+ }
+
+ os << mi.fq_type () << "& vw =" << endl
+ << " wrapper_traits< " + wt + " >::set_ref (" + member + ");"
+ << endl;
+
+ wrap_member = member;
+ member = "vw";
+ }
+
+ if (mi.ptr != 0)
+ {
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;"
+ << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "if (";
+
+ if (comp != 0)
+ os << "composite_value_traits< " << type << ", id_" << db <<
+ " >::get_null (" << endl
+ << "i." << mi.var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << ")";
+ else
+ get_null (mi.var);
+
+ os << ")" << endl;
+
+ // Don't throw null_pointer if we can't have NULLs and the pointer
+ // is NULL since this can be useful during migration. Instead, we
+ // rely on the database enforcing this.
+ //
+ os << member << " = ptr_traits::pointer_type ();";
+
+ os << "else"
+ << "{";
+
+ os << type << " ptr_id;";
+
+ member = "ptr_id";
+ }
+ else
+ type = mi.fq_type ();
+
+ if (comp != 0)
+ traits = "composite_value_traits< " + type + ", id_" +
+ db.string () + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = db.string () + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (mi.ptr != 0)
+ {
+ if (view_member (mi.m))
+ {
+ // The object pointer in view doesn't need any of this.
+ os << "}";
+ return;
+ }
+
+ // Restore the member variable name.
+ //
+ member = member_override_.empty () ? "v" : member_override_;
+
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (utype (mi.m, key_prefix_));
+
+ if (lazy_pointer (pt))
+ os << member << " = ptr_traits::pointer_type (" << endl
+ << "*static_cast<" << db << "::database*> (db), ptr_id);";
+ else
+ {
+ os << "// If a compiler error points to the line below, then" << endl
+ << "// it most likely means that a pointer used in a member" << endl
+ << "// cannot be initialized from an object pointer." << endl
+ << "//" << endl
+ << member << " = ptr_traits::pointer_type (" << endl
+ << "static_cast<" << db << "::database*> (db)->load<" << endl
+ << " obj_traits::object_type > (ptr_id));";
+
+ // If we are loading into an eager weak pointer, make sure there
+ // is someone else holding a strong pointer to it (normally a
+ // session). Otherwise, the object will be loaded and immediately
+ // deleted. Besides not making much sense, this also breaks the
+ // delayed loading machinery which expects the object to be around
+ // at least until the top-level load() returns.
+ //
+ if (weak_pointer (pt))
+ {
+ os << endl
+ << "if (odb::pointer_traits<" <<
+ "ptr_traits::strong_pointer_type>::null_ptr (" << endl
+ << "ptr_traits::lock (" << member << ")))" << endl
+ << "throw session_required ();";
+ }
+ }
+
+ os << "}";
+ }
+
+ // Wrap back (so to speak).
+ //
+ if (mi.wrapper != 0 && composite (mi.t) != 0)
+ {
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ os << "}";
+
+ member = wrap_member;
+ }
+
+ // Untranslate.
+ //
+ if (mi.ct != 0)
+ {
+ //@@ Use move() in C++11? Or not.
+ //
+ os << "// From " << location_string (mi.ct->loc, true) << endl
+ << translate_member << " = " <<
+ mi.ct->translate_from (member) << ";";
+
+ member = translate_member;
+ }
+
+ // Call the modifier if we are using a proper one.
+ //
+ if (member_override_.empty ())
+ {
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ if (ma.placeholder ())
+ {
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate (
+ "o", "v", "*static_cast<" + db.string () + "::database*> (db)")
+ << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ // This is the middle part. The pre and post parts are generated
+ // by init_view_pointer_member below.
+ //
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ string o_tp (mi.var + "object_type");
+ string o_tr (mi.var + "object_traits");
+ string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
+ string i_tp (mi.var + "info_type");
+
+ string id (mi.var + "id");
+ string o (mi.var + "o");
+ string pi (mi.var + "pi"); // Polymorphic type info.
+
+ // If load_() will be loading containers or the rest of the
+ // polymorphic object, then we need to perform several extra
+ // things. We need to initialize the id image in the object
+ // statements. We also have to lock the statements so that
+ // nobody messes up this id image.
+ //
+ bool init_id (
+ poly ||
+ has_a (c, test_container | include_eager_load, &main_section));
+
+ bool versioned (context::versioned (c));
+
+ os << "if (" << o << " != 0)"
+ << "{";
+
+ if (poly)
+ os << "callback_event ce (callback_event::pre_load);"
+ << pi << "->dispatch (" << i_tp << "::call_callback, " <<
+ "*db, " << o << ", &ce);";
+ else
+ os << o_tr << "::callback (*db, *" << o <<
+ ", callback_event::pre_load);";
+
+ os << o_tr << "::init (*" << o << ", i." << mi.var << "value, db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ // Call load_() to load the rest of the object (containers, etc).
+ //
+ if (id_member (poly ? *poly_root : c) != 0)
+ {
+ const char* s (poly_derived ? "osts" : "sts");
+
+ os << o_tr << "::statements_type& " << s << " (" << endl
+ << "conn.statement_cache ().find_object<" << o_tp << "> ());";
+
+ if (poly_derived)
+ os << r_tr << "::statements_type& sts (osts.root_statements ());";
+
+ if (init_id)
+ {
+ // This can only be top-level call so lock must succeed.
+ //
+ os << r_tr << "::statements_type::auto_lock l (sts);"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl
+ << r_tr << "::id_image_type& i (sts.id_image ());"
+ << r_tr << "::init (i, " << id << ");"
+ << db << "::binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || " <<
+ "idb.version == 0)"
+ << "{"
+ << r_tr << "::bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (optimistic (poly ? *poly_root : c) != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ os << o_tr << "::load_ (" << s << ", *" << o << ", false" <<
+ (versioned ? ", svm" : "") << ");";
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (poly)
+ os << endl
+ << "if (" << pi << " != &" << o_tr << "::info)"
+ << "{"
+ << "std::size_t d (" << o_tr << "::depth);"
+ << pi << "->dispatch (" << i_tp << "::call_load, *db, " <<
+ o << ", &d);"
+ << "}";
+
+ if (init_id)
+ os << "sts.load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+ }
+
+ os << "}";
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << traits << "::init (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "db";
+
+ if (versioned (*composite (mi.t)))
+ os << "," << endl
+ << "svm";
+
+ os << ");"
+ << endl;
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string traits;
+ string member;
+ string translate_member; // Untranslated member.
+ string wrap_member; // Wrapped member.
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
+ // This class generates the pre and post parts. The middle part is
+ // generated by init_value_member above.
+ //
+ struct init_view_pointer_member: virtual member_base,
+ member_base_impl<bool> // Dummy SQL type.
+ {
+ typedef init_view_pointer_member base;
+
+ init_view_pointer_member (bool pre, init_value_member const& ivm)
+ : member_base (0, 0, string (), string (), 0),
+ pre_ (pre), init_value_member_ (ivm) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // Only interested in object pointers inside views.
+ //
+ return mi.ptr != 0 && view_member (mi.m);
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ bool abst (abstract (c));
+ class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+
+ data_member_path* idm (id_member (poly ? *poly_root : c));
+
+ os << "// " << mi.m.name () << (pre_ ? " pre" : " post") << endl
+ << "//" << endl;
+
+ string o_tp (mi.var + "object_type");
+ string o_tr (mi.var + "object_traits");
+ string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
+ string i_tp (mi.var + "info_type");
+ string p_tp (mi.var + "pointer_type");
+ string p_tr (mi.var + "pointer_traits");
+ string c_tr (mi.var + "cache_traits");
+
+ string id (mi.var + "id"); // Object id.
+ string p (mi.var + "p"); // Object pointer.
+ string pg (mi.var + "pg"); // Pointer guard.
+ string ig (mi.var + "ig"); // Cache insert guard.
+ string o (mi.var + "o"); // Object.
+ string pi (mi.var + "pi"); // Polymorphic type info.
+
+ bool op_raw (c.get<bool> ("object-pointer-raw"));
+ bool mp_raw (utype (mi.m).is_a<semantics::pointer> ());
+
+ // Output aliases and variables before any schema version if-
+ // blocks since we need to be able to access them across all
+ // three parts.
+ //
+ if (pre_)
+ {
+ os << "typedef " << class_fq_name (c) << " " << o_tp << ";"
+ << "typedef object_traits_impl<" << o_tp << ", id_" << db <<
+ "> " << o_tr << ";";
+
+ if (poly_derived)
+ os << "typedef " << o_tr << "::root_traits " << r_tr << ";";
+
+ if (poly)
+ os << "typedef " << r_tr << "::info_type " << i_tp << ";";
+
+ os << "typedef " << r_tr << "::pointer_type " << p_tp << ";"
+ << "typedef " << r_tr << "::pointer_traits " << p_tr << ";";
+ if (idm != 0)
+ os << "typedef " << r_tr << "::pointer_cache_traits " <<
+ c_tr << ";";
+ os << endl;
+
+ if (idm != 0)
+ os << r_tr << "::id_type " << id << ";";
+ os << p_tp << " " << p << (op_raw ? " = 0" : "") << ";" // VC++
+ << p_tr << "::guard " << pg << ";";
+ if (idm != 0)
+ os << c_tr << "::insert_guard " << ig << ";";
+ os << o_tp << "* " << o << " (0);";
+
+ if (poly)
+ os << "const " << i_tp << "* " << pi << " = 0;"; // VC++
+
+ os << endl;
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ if (pre_)
+ {
+ string id_im;
+ if (idm != 0)
+ {
+ // Check for NULL.
+ //
+ string id_var;
+ {
+ id_im = mi.var + "value";
+
+ // In a polymorphic class, the id is in the root image.
+ //
+ for (size_t i (0); i < poly_depth - 1; ++i)
+ id_im += (i == 0 ? ".base" : "->base");
+
+ string n;
+ for (data_member_path::const_iterator i (idm->begin ());
+ i != idm->end ();
+ ++i)
+ {
+ // The same logic as in member_base.
+ //
+ if (!n.empty ())
+ n += "value."; // Composite.
+
+ string const& name ((*i)->name ());
+ n += name;
+
+ if (n[n.size () - 1] != '_')
+ n += '_';
+ }
+
+ id_var = id_im + (poly_derived ? "->" : ".") + n;
+ id_im = (poly_derived ? "*i." : "i.") + id_im;
+ }
+
+ os << "if (";
+
+ if (semantics::class_* comp = composite (mi.t))
+ os << "!composite_value_traits< " << o_tr << "::id_type, id_" <<
+ db << " >::get_null (" << endl
+ << "i." << id_var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << ")";
+ else
+ {
+ os << "!(";
+ init_value_member_.get_null (id_var);
+ os << ")";
+ }
+
+ os << ")"
+ << "{";
+
+ // Check cache.
+ //
+ os << id << " = " << r_tr << "::id (" << id_im << ");"
+ << p << " = " << c_tr << "::find (*db, " << id << ");"
+ << endl;
+
+ os << "if (" << p_tr << "::null_ptr (" << p << "))"
+ << "{";
+ }
+
+ // To support by-value object loading, we are going to load
+ // into an existing instance if the pointer is already not
+ // NULL. To limit the potential misuse (especially when it
+ // comes to sessions), we are going to limit this support
+ // only to raw pointers. Furthermore, we will only insert
+ // such an object into the cache if its object pointer is
+ // also raw.
+ //
+ if (mp_raw && !poly)
+ {
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.get<member_access> ("get"));
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Use the original type to form the const reference. VC++
+ // cannot grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, true, "m") << " =" << endl
+ << " " << ma.translate ("o") << ";"
+ << endl;
+
+ os << "if (m != 0)"
+ << "{";
+
+ if (op_raw)
+ os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
+ ", m));";
+
+ os << o << " = m;"
+ << "}"
+ << "else"
+ << "{";
+ }
+
+ if (poly)
+ {
+ os << r_tr << "::discriminator_type d (" << endl
+ << r_tr << "::discriminator (" << id_im << "));";
+
+ if (abst)
+ os << pi << " = &" << r_tr << "::map->find (d);";
+ else
+ os << pi << " = (d == " << o_tr << "::info.discriminator" << endl
+ << "? &" << o_tr << "::info" << endl
+ << ": &" << r_tr << "::map->find (d));";
+
+ os << p << " = " << pi << "->create ();";
+ }
+ else
+ os << p << " = object_factory<" << o_tp << ", " << p_tp <<
+ ">::create ();";
+
+ os << pg << ".reset (" << p << ");";
+ if (idm != 0)
+ os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
+ ", " << p << "));";
+
+ if (poly_derived)
+ os << o << " = static_cast<" << o_tp << "*> (" << p_tr <<
+ "::get_ptr (" << p << "));";
+ else
+ os << o << " = " << p_tr << "::get_ptr (" << p << ");";
+
+ if (mp_raw && !poly)
+ os << "}";
+
+ if (idm != 0)
+ os << "}" // Cache.
+ << "}"; // NULL.
+ }
+ else
+ {
+ os << "if (" << o << " != 0)"
+ << "{";
+
+ if (poly)
+ os << "callback_event ce (callback_event::post_load);"
+ << pi << "->dispatch (" << i_tp << "::call_callback, " <<
+ "*db, " << o << ", &ce);";
+ else
+ os << o_tr << "::callback (*db, *" << o <<
+ ", callback_event::post_load);";
+
+ if (idm != 0)
+ {
+ if (mp_raw && !op_raw && !poly)
+ os << "if (!" << p_tr << "::null_ptr (" << p << "))"
+ << "{";
+
+ os << c_tr << "::load (" << ig << ".position ());"
+ << ig << ".release ();";
+
+ if (mp_raw && !op_raw && !poly)
+ os << "}";
+ }
+
+ os << pg << ".release ();";
+
+ os << "}";
+
+ // If member pointer is not raw, then result is in p.
+ // If both member and object are raw, then result is in o.
+ // If member is raw but object is not, then result is in
+ // p if it is not NULL, and in o (either NULL or the same
+ // as the member value) otherwise.
+ //
+ member_access& ma (mi.m.get<member_access> ("set"));
+
+ if (ma.empty () && !poly)
+ {
+ // It is ok to have empty modifier expression as long as
+ // the member pointer is raw. This is the by-value load
+ // and the user is not interested in learning whether the
+ // object is NULL.
+ //
+ if (!mp_raw)
+ {
+ error (ma.loc) << "non-empty modifier expression required " <<
+ "for loading an object via a smart pointer" << endl;
+ throw operation_failed ();
+ }
+
+ os << "// Empty modifier expression was specified for this\n"
+ << "// object so make sure we have actually loaded the\n"
+ << "// data into the existing instance rather than, say,\n"
+ << "// finding the object in the cache or creating a new one.\n"
+ << "//\n"
+ << "assert (" << p_tr << "::null_ptr (" << p << "));";
+ }
+ else
+ {
+ if (!(mp_raw && op_raw) || poly)
+ {
+ string r (options.std () >= cxx_version::cxx11
+ ? "std::move (" + p + ")"
+ : p);
+
+ if (poly_derived)
+ // This pointer could have come from cache, so use dynamic
+ // cast.
+ //
+ r = p_tr + "::dynamic_pointer_cast<" + o_tp + "> (\n" +
+ r + ")";
+
+ // Unless the pointer is raw, explicitly construct the
+ // smart pointer from the object pointer so that we get
+ // the behavior similar to calling database::load() (in
+ // both cases we are the "ownership end-points"; unless
+ // the object was already in the session before loading
+ // this view (in which case using raw pointers as object
+ // pointers is a really stupid idea), this logic will do
+ // the right thing and what the user most likely expects.
+ //
+ if (!mp_raw)
+ r = member_val_type (mi.m, false) + " (\n" + r + ")";
+
+ if (mp_raw && !op_raw)
+ os << "if (!" << p_tr << "::null_ptr (" << p << "))" << endl;
+
+ os << "// If a compiler error points to the line below, then\n"
+ << "// it most likely means that a pointer used in view\n"
+ << "// member cannot be initialized from an object pointer.\n"
+ << "//" << endl;
+
+ set_member (mi.m, "o", r, "db");
+ }
+
+ if (mp_raw && !poly)
+ {
+ if (!op_raw)
+ os << "else" << endl; // NULL p
+
+ set_member (mi.m, "o", o, "db");
+ }
+ }
+ }
+
+ os << "}";
+ }
+
+ virtual bool const&
+ member_sql_type (semantics::data_member&) {return pre_;};
+
+ protected:
+ bool pre_;
+ init_value_member const& init_value_member_;
+ };
+
+ struct init_value_base: traversal::class_, virtual context
+ {
+ typedef init_value_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::init (o, i, db" <<
+ (versioned (c) ? ", svm" : "") << ");"
+ << endl;
+ }
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& c)
+ : object_members_base (
+ true,
+ object (c), // Only build table prefix for objects.
+ false),
+ c_ (c)
+ {
+ scope_ = object (c)
+ ? "access::object_traits_impl< "
+ : "access::composite_value_traits< ";
+
+ scope_ += class_fq_name (c) + ", id_" + db.string () + " >";
+ }
+
+ // Unless the database system can execute several interleaving
+ // statements, cache the result set.
+ //
+ virtual void
+ cache_result (string const& statement)
+ {
+ os << statement << ".cache ();";
+ }
+
+ // Additional code that need to be executed following the call to
+ // init_value.
+ //
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (object (c_))
+ object_members_base::traverse_composite (m, c);
+ else
+ {
+ // If we are generating traits for a composite value type, then
+ // we don't want to go into its bases or it composite members.
+ //
+ if (m == 0 && &c == &c_)
+ names (c);
+ }
+ }
+
+ virtual void
+ container_extra (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& t)
+ {
+ using semantics::type;
+
+ // Figure out if this member is from a base object or composite
+ // value and if it's from an object, whether it is reuse-abstract.
+ //
+ bool base, reuse_abst;
+
+ if (object (c_))
+ {
+ base = cur_object != &c_ ||
+ !object (dynamic_cast<type&> (m.scope ()));
+ reuse_abst = abstract (c_) && !polymorphic (c_);
+ }
+ else
+ {
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
+ }
+
+ container_kind_type ck (container_kind (t));
+
+ const custom_cxx_type* vct (0);
+ const custom_cxx_type* ict (0);
+ const custom_cxx_type* kct (0);
+
+ type& vt (container_vt (m, &vct));
+ type* it (0);
+ type* kt (0);
+
+ data_member_path* imp (context::inverse (m, "value"));
+
+ bool ordered (false);
+ bool inverse (imp != 0);
+ bool grow (false);
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (m, &ict);
+ ordered = true;
+
+ if (generate_grow)
+ grow = grow || context::grow (m, *it, ict, "index");
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (m, &kct);
+
+ if (generate_grow)
+ grow = grow || context::grow (m, *kt, kct, "key");
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ bool smart (!inverse &&
+ (ck != ck_ordered || ordered) &&
+ container_smart (t));
+
+ if (generate_grow)
+ grow = grow || context::grow (m, vt, vct, "value");
+
+ bool eager_ptr (is_a (member_path_,
+ member_scope_,
+ test_eager_pointer,
+ vt,
+ "value"));
+ if (!eager_ptr)
+ {
+ if (semantics::class_* cvt = composite_wrapper (vt))
+ eager_ptr = has_a (*cvt, test_eager_pointer);
+ }
+
+ bool versioned (context::versioned (m));
+
+ string name (flat_prefix_ + public_name (m) + "_traits");
+ string scope (scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ container_extra (m, t);
+
+ //
+ // Statements.
+ //
+ if (!reuse_abst)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ semantics::type& idt (container_idt (m));
+
+ qname table (table_name (m, table_prefix_));
+ string qtable (quote_id (table));
+ instance<object_columns_list> id_cols;
+ instance<object_columns_list> ik_cols; // index/key columns
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ ik_cols->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ // select_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "select_statement[] =" << endl;
+
+ if (inverse)
+ {
+ semantics::class_* c (object_pointer (vt));
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that class
+ // for the table name, etc.
+ //
+ if (polymorphic (*c))
+ c = &dynamic_cast<semantics::class_&> (imf.scope ());
+
+ data_member_path& inv_id (*id_member (*c));
+
+ qname inv_table; // Other table name.
+ string inv_qtable;
+ instance<object_columns_list> inv_id_cols; // Other id column.
+ instance<object_columns_list> inv_fid_cols; // Other foreign id
+ // column (ref to us).
+ statement_columns sc;
+
+ if (container (imb))
+ {
+ // many(i)-to-many
+ //
+
+ // This other container is a direct member of the class so the
+ // table prefix is just the class table name.
+ //
+ inv_table = table_name (*c, *imp);
+ inv_qtable = quote_id (inv_table);
+
+ inv_id_cols->traverse (imb, utype (inv_id), "id", "object_id", c);
+ inv_fid_cols->traverse (imb, idt, "value", "value");
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ // If this is a simple id, then pass the "id" key prefix. If
+ // it is a composite id, then the members have no prefix.
+ //
+ sc.push_back (
+ statement_column (
+ inv_qtable,
+ inv_qtable + "." + quote_id (i->name),
+ i->type,
+ *i->member,
+ inv_id_cols->size () == 1 ? "id" : ""));
+ }
+ }
+ else
+ {
+ // many(i)-to-one
+ //
+ inv_table = table_name (*c);
+ inv_qtable = quote_id (inv_table);
+
+ inv_id_cols->traverse (inv_id);
+ inv_fid_cols->traverse (imb, column_prefix (*imp));
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ sc.push_back (
+ statement_column (
+ inv_qtable,
+ inv_qtable + "." + quote_id (i->name),
+ i->type,
+ *i->member));
+ }
+ }
+
+ process_statement_columns (sc, statement_select, versioned);
+
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ instance<query_parameters> qp (statement_select, inv_table);
+ os << strlit ("FROM " + inv_qtable + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (inv_fid_cols->begin ()),
+ i (b); i != inv_fid_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += inv_qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+ os << strlit (where);
+ }
+ else
+ {
+ id_cols->traverse (m, idt, "id", "object_id");
+
+ statement_columns sc;
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc);
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ t->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ t->traverse (m, *kt, "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ t->traverse (m, vt, "value", "value");
+
+ process_statement_columns (sc, statement_select, versioned);
+
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ instance<query_parameters> qp (statement_select, table);
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (ordered)
+ {
+ // Top-level column.
+ //
+ string const& col (
+ column_qname (m, "index", "index", column_prefix ()));
+
+ where += " ORDER BY " + qtable + "." + col;
+ }
+
+ os << strlit (where);
+ }
+
+ os << ";"
+ << endl;
+
+ // insert_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "insert_statement[] =" << endl;
+
+ if (inverse)
+ os << strlit ("") << ";"
+ << endl;
+ else
+ {
+ statement_columns sc;
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc);
+
+ t->traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ t->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ t->traverse (m, *kt, "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ t->traverse (m, vt, "value", "value");
+
+ process_statement_columns (sc, statement_insert, versioned);
+
+ os << strlit ("INSERT INTO " + qtable + sep) << endl;
+
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e;)
+ {
+ string s;
+
+ if (i == b)
+ s += '(';
+ s += i->column;
+ s += (++i != e ? ',' : ')');
+ s += sep;
+
+ os << strlit (s) << endl;
+ }
+
+ os << strlit ("VALUES" + sep) << endl;
+
+ string values ("(");
+ instance<query_parameters> qp (statement_insert, table);
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e; ++i)
+ {
+ if (i != b)
+ {
+ values += ',';
+ values += sep;
+ }
+
+ values += convert_to (qp->next (*i), i->type, *i->member);
+ }
+ values += ')';
+
+ os << strlit (values) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (smart)
+ {
+ os << "const char " << scope << "::" << endl
+ << "update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ instance<query_parameters> qp (statement_update, table);
+ statement_columns sc;
+ {
+ bool f (false); // Imperfect forwarding.
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ instance<object_columns> t (sk, f, sc, p);
+ t->traverse (m, vt, "value", "value");
+ process_statement_columns (sc, statement_update, versioned);
+ }
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
+ i != ik_cols->end (); ++i)
+ {
+ where += " AND " + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // delete_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "delete_statement[] =" << endl;
+
+ if (inverse)
+ os << strlit ("") << ";"
+ << endl;
+ else
+ {
+ instance<query_parameters> qp (statement_delete, table);
+
+ os << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (smart)
+ {
+ for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
+ i != ik_cols->end (); ++i)
+ {
+ where += " AND " + quote_id (i->name) +
+ (ck == ck_ordered ? ">=" : "=") +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+ }
+
+ if (base)
+ return;
+
+ //
+ // Functions.
+ //
+
+ // bind (cond_image_type)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type& c)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "c", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "c", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "c", vt, vct, "value_type", "value");
+ bm->traverse (m);
+ break;
+ }
+ }
+ os << "}";
+ }
+
+ // bind (data_image_type)
+ //
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "data_image_type& d";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ // In the case of containers, insert and select column sets are
+ // the same since we can't have inverse members as container
+ // elements.
+ //
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "d", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ os << "n++;" // Simple value.
+ << endl;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "d", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+
+ if (semantics::class_* c = composite_wrapper (*kt))
+ os << "n += " << column_count (*c).total << "UL;"
+ << endl;
+ else
+ os << "n++;"
+ << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "d", vt, vct, "value_type", "value");
+ bm->traverse (m);
+
+ os << "}";
+ }
+
+ // bind (cond_image, data_image) (update)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type& c," << endl
+ << "data_image_type& d";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ // Use insert instead of update to include read-only members.
+ //
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "d", vt, vct, "value_type", "value");
+ bm->traverse (m);
+
+ if (semantics::class_* c = composite_wrapper (vt))
+ os << "n += " << column_count (*c).total << "UL;"
+ << endl;
+ else
+ os << "n++;"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "c", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "c", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "c", vt, vct, "value_type", "value");
+ bm->traverse (m);
+ break;
+ }
+ }
+ os << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ size_t index (0);
+
+ os << "void " << scope << "::" << endl
+ << "grow (data_image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "bool grew (false);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "index_", *it, ict, "index_type", "index");
+ gm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "key_", *kt, kct, "key_type", "key");
+ gm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "value_", vt, vct, "value_type", "value");
+ gm->traverse (m);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << "}";
+ }
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "init (data_image_type& i," << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type* j," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "const key_type* k," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "const value_type& v";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl
+ << "if (j != 0)";
+
+ instance<init_image_member> im (
+ "index_", "*j", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl
+ << "if (k != 0)";
+
+ instance<init_image_member> im (
+ "key_", "*k", *kt, kct, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ instance<init_image_member> im (
+ "value_", "v", vt, vct, "value_type", "value");
+ im->traverse (m);
+ }
+
+ if (generate_grow)
+ os << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+ }
+
+ // init (cond_image)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (cond_image_type& i, index_type j)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ instance<init_image_member> im (
+ "index_", "j", *it, ict, "index_type", "index");
+ im->traverse (m);
+
+ os << "}";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // Need to handle growth.
+ //
+ // os << "init (data_image_type&, const key_type&);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // Need to handle growth.
+ //
+ // os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // init (data)
+ //
+ os << "void " << scope << "::" << endl
+ << "init (";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type& j," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "key_type& k," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "value_type& v," << endl;
+ os << "const data_image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "index_", "j", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "key_", "k", *kt, kct, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ // If the value is an object pointer, pass the id type as a
+ // type override.
+ //
+ instance<init_value_member> im (
+ "value_", "v", vt, vct, "value_type", "value");
+ im->traverse (m);
+ }
+ os << "}";
+
+ // insert
+ //
+ {
+ string ia, ka, va, da;
+
+ if (!inverse)
+ {
+ ia = ordered ? " i" : "";
+ ka = " k";
+ va = " v";
+ da = " d";
+ }
+
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert (index_type" << ia << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert (const key_type&" << ka << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert (const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ }
+
+ os << "{";
+
+ if (!inverse)
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl
+ << "init (di, ";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "&i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "&k, ";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "v" << (versioned ? ", svm" : "") << ");";
+
+ os << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.data_bind (), id.bind, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}"
+ << "if (!sts.insert_statement ().execute ())" << endl
+ << "throw object_already_persistent ();";
+ }
+
+ os << "}";
+ }
+
+ // update
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "update (index_type i, const value_type& v, void* d)";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "{";
+
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "cond_image_type& ci (sts.cond_image ());"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (ci, i);";
+ os << "init (di, 0, v" << (versioned ? ", svm" : "") << ");";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //os << "init (di, 0, v);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //os << "init (di, v);";
+ break;
+ }
+ }
+
+ os << endl
+ << "if (sts.update_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.update_bind (), id.bind, id.count, ci, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_binding_update_version ();"
+ << "}";
+
+ os << "if (sts.update_statement ().execute () == 0)" << endl
+ << "throw object_not_persistent ();"
+ << "}";
+ }
+
+ // select
+ //
+ os << "bool " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "select (index_type&" << (ordered ? " i" : "") <<
+ ", value_type& v, void* d)";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "select (key_type& k, value_type& v, void* d)";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "select (value_type& v, void* d)";
+ break;
+ }
+ }
+
+ os << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl
+ << "init (";
+
+ // Extract current element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "k, ";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "v, di, &sts.connection ().database ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl;
+
+ init_value_extra ();
+
+ // If we are loading an eager pointer, then the call to init
+ // above executes other statements which potentially could
+ // change the image, including the id.
+ //
+ if (eager_ptr)
+ {
+ os << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.data_bind (), id.bind, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}";
+ }
+
+ // Fetch next.
+ //
+ os << "select_statement& st (sts.select_statement ());"
+ << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "grow (di, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ // Id cannot change.
+ //
+ << "bind (sts.data_bind (), 0, sts.id_binding ().count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "return r != select_statement::no_data;"
+ << "}";
+
+ // delete_
+ //
+ os << "void " << scope << "::" << endl
+ << "delete_ (";
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "index_type i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ os << "void*" << (inverse ? "" : " d") << ")"
+ << "{";
+
+ if (!inverse)
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));";
+
+ if (smart)
+ {
+ os << "cond_image_type& ci (sts.cond_image ());"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (ci, i);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << endl
+ << "if (sts.cond_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.cond_bind (), id.bind, id.count, ci);"
+ << "sts.cond_binding_update_version ();"
+ << "}";
+ }
+
+ os << "sts.delete_statement ().execute ();";
+ }
+
+ os << "}";
+
+ // persist
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "persist (const container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::persist (c, fs);"
+ << "}";
+ }
+
+ // load
+ //
+ os << "void " << scope << "::" << endl
+ << "load (container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "const binding& id (sts.id_binding ());"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "bind (sts.data_bind (), id.bind, id.count, sts.data_image ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}"
+ // We use the id binding directly so no need to check cond binding.
+ //
+ << "select_statement& st (sts.select_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);";
+
+ // If we are loading eager object pointers, we may need to cache
+ // the result since we will be loading other objects.
+ //
+ if (eager_ptr)
+ cache_result ("st");
+
+ os << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "data_image_type& di (sts.data_image ());"
+ << "grow (di, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ // Id cannot change.
+ //
+ << "bind (sts.data_bind (), 0, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "bool more (r != select_statement::no_data);"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::load (c, more, fs);"
+ << "}";
+
+ // update
+ //
+ if (!(inverse || readonly (member_path_, member_scope_)))
+ {
+ os << "void " << scope << "::" << endl
+ << "update (const container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::update (c, fs);"
+ << "}";
+ }
+
+ // erase
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "erase (";
+
+ if (smart)
+ os << "const container_type* c, ";
+
+ os << "statements_type& sts)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::erase (" << (smart ? "c, " : "") << "fs);"
+ << "}";
+ }
+ }
+
+ protected:
+ string scope_;
+ semantics::class_& c_;
+ };
+
+ // Extra statement cache members for containers.
+ //
+ struct container_cache_members: object_members_base, virtual context
+ {
+ typedef container_cache_members base;
+
+ container_cache_members ()
+ : object_members_base (true, false, false)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ bool smart (!context::inverse (m, "value") &&
+ !unordered (m) &&
+ container_smart (c));
+
+ string traits (flat_prefix_ + public_name (m) + "_traits");
+
+ os << db << "::" << (smart ? "smart_" : "") <<
+ "container_statements_impl< " << traits << " > " <<
+ flat_prefix_ << m.name () << ";";
+ }
+ };
+
+ struct container_cache_init_members: object_members_base, virtual context
+ {
+ typedef container_cache_init_members base;
+
+ container_cache_init_members ()
+ : object_members_base (true, false, false), first_ (true)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << flat_prefix_ << m.name () << " (c, id";
+ extra_members ();
+ os << ")";
+ }
+
+ virtual void
+ extra_members () {}
+
+ protected:
+ bool first_;
+ };
+
+ // Extra statement cache members for sections.
+ //
+ struct section_cache_members: virtual context
+ {
+ typedef section_cache_members base;
+
+ virtual void
+ traverse (user_section& s)
+ {
+ string traits (public_name (*s.member) + "_traits");
+
+ os << db << "::" << "section_statements< " <<
+ class_fq_name (*s.object) << ", " << traits << " > " <<
+ s.member->name () << ";";
+ }
+ };
+
+ struct section_cache_init_members: virtual context
+ {
+ typedef section_cache_init_members base;
+
+ section_cache_init_members (bool first): first_ (first) {}
+
+ virtual void
+ traverse (user_section& s)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << s.member->name () << " (c, im, idim, id, idv";
+ extra_members ();
+ os << ")";
+ }
+
+ virtual void
+ extra_members () {}
+
+ protected:
+ bool first_;
+ };
+
+ // Calls for container members.
+ //
+ struct container_calls: object_members_base, virtual context
+ {
+ typedef container_calls base;
+
+ enum call_type
+ {
+ persist_call,
+ load_call,
+ update_call,
+ erase_obj_call,
+ erase_id_call,
+ section_call
+ };
+
+ container_calls (call_type call, object_section* section = 0)
+ : object_members_base (true, false, true, false, section),
+ call_ (call),
+ obj_prefix_ ("obj"),
+ by_value_ (0)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // load calls.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (call_ == load_call &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite_wrapper (semantics::data_member* m,
+ semantics::class_& c,
+ semantics::type* w)
+ {
+ if (m == 0 ||
+ call_ == erase_id_call ||
+ (call_ == load_call && by_value_ != 0))
+ {
+ object_members_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Get this member using the accessor expression.
+ //
+ member_access& ma (
+ m->get<member_access> (call_ == load_call ? "set" : "get"));
+
+ // We don't support by-value modifiers for composite values
+ // with containers. However, at this point we don't know
+ // whether this composite value has any containers. So we
+ // are just going to set a flag that can be checked in
+ // traverse_container() below.
+ //
+ if (call_ == load_call && ma.placeholder ())
+ {
+ by_value_ = &ma;
+ object_members_base::traverse_composite (m, c);
+ by_value_ = 0;
+ return;
+ }
+
+ // We also don't support by-value accessors is there is a
+ // smart container inside (which, again, we don't know at
+ // this point). So keep track of such first instance.
+ //
+ member_access* old_by_value (by_value_);
+ if (call_ != load_call && ma.by_value && by_value_ == 0)
+ by_value_ = &ma;
+
+ string old_op (obj_prefix_);
+ string old_f (from_);
+ obj_prefix_.clear ();
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (call_ == load_call && ma.direct () && const_member (*m));
+ if (cast)
+ obj_prefix_ = "const_cast< " + member_ref_type (*m, false) +
+ " > (\n";
+
+ obj_prefix_ += ma.translate (old_op);
+
+ if (cast)
+ obj_prefix_ += ")";
+
+ // If this is not a synthesized expression, then store its
+ // location which we will output later for easier error
+ // tracking.
+ //
+ if (!ma.synthesized)
+ from_ += "// From " + location_string (ma.loc, true) + "\n";
+
+ // If this is a wrapped composite value, then we need to "unwrap" it.
+ //
+ if (w != 0)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*m, hint));
+
+ // Because we cannot have nested containers, member type should
+ // be the same as w.
+ //
+ assert (&t == w);
+
+ obj_prefix_ = "wrapper_traits< " + t.fq_name (hint) + " >::" +
+ (call_ == load_call ? "set_ref" : "get_ref") +
+ " (\n" + obj_prefix_ + ")";
+ }
+
+ object_members_base::traverse_composite (m, c);
+ from_ = old_f;
+ obj_prefix_ = old_op;
+ by_value_ = old_by_value;
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ using semantics::type;
+
+ bool inverse (context::inverse (m, "value"));
+ bool smart (!inverse && !unordered (m) && container_smart (c));
+ bool versioned (context::versioned (m));
+
+ // In certain cases we don't need to do anything.
+ //
+ if ((call_ != load_call && inverse) ||
+ (call_ == section_call && !smart) ||
+ (call_ == update_call && readonly (member_path_, member_scope_)))
+ return;
+
+ string const& name (m.name ());
+ string sts_name (flat_prefix_ + name);
+ string traits (flat_prefix_ + public_name (m) + "_traits");
+
+ os << "// " << member_prefix_ << m.name () << endl
+ << "//" << endl;
+
+ // Get this member using the accessor expression.
+ //
+ string var;
+ member_access& ma (
+ m.get<member_access> (call_ == load_call ? "set" : "get"));
+
+ // We don't support by-value modifiers for composite values
+ // with containers.
+ //
+ if (call_ == load_call && by_value_ != 0)
+ {
+ error (by_value_->loc) << "by-value modification of a composite "
+ << "value with container is not supported"
+ << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // We don't support by-value accessors for smart containers.
+ //
+ if (call_ != load_call && smart)
+ {
+ if (by_value_ != 0)
+ {
+ error (by_value_->loc) << "by-value access to a composite value "
+ << "with smart container is not supported"
+ << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (ma.by_value)
+ {
+ error (ma.loc) << "by-value access to a smart container is not "
+ << "supported" << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ os << "{";
+
+ if (call_ != erase_id_call && (call_ != erase_obj_call || smart))
+ {
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (call_ == load_call && ma.placeholder ())
+ os << member_val_type (m, false, "v") << ";"
+ << endl;
+ else
+ {
+ // Note: this case is for both access and modification.
+ //
+
+ // Output stored locations, if any.
+ //
+ os << from_;
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Note that here we don't decay arrays.
+ //
+ const string& ref_type (
+ member_ref_type (m, call_ != load_call, "v", false /* decay */));
+
+ // VC++ cannot grok the constructor syntax.
+ //
+ os << ref_type << " =" << endl
+ << " ";
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (call_ == load_call && ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << member_ref_type (m, false, "", false) <<
+ " > (" << endl;
+
+ os << ma.translate (obj_prefix_);
+
+ if (cast)
+ os << ")";
+
+ os << ";"
+ << endl;
+ }
+
+ var = "v";
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // If this is a wrapped container, then we need to "unwrap" it.
+ //
+ if (wrapper (t))
+ {
+ var = "wrapper_traits< " + t.fq_name (hint) + " >::" +
+ (call_ == load_call ? "set_ref" : "get_ref") + " (" + var + ")";
+ }
+ }
+
+ switch (call_)
+ {
+ case persist_call:
+ {
+ os << traits << "::persist (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case load_call:
+ {
+ os << traits << "::load (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case update_call:
+ {
+ os << traits << "::update (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case erase_obj_call:
+ {
+ os << traits << "::erase (" << endl;
+
+ if (smart)
+ os << "&" << var << "," << endl;
+
+ os << "esc." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case erase_id_call:
+ {
+ os << traits << "::erase (" << endl;
+
+ if (smart)
+ os << "0," << endl;
+
+ os << "esc." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case section_call:
+ {
+ os << "if (" << traits << "::container_traits_type::changed (" <<
+ var << "))" << endl
+ << "s.reset (true, true);"; // loaded, changed
+ break;
+ }
+ }
+
+ if (call_ == load_call)
+ {
+ // Call the modifier if we are using a proper one.
+ //
+ if (ma.placeholder ())
+ {
+ os << endl
+ << from_;
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate (
+ obj_prefix_, "v", "static_cast<" + db.string () +
+ "::database&> (db)") << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ protected:
+ call_type call_;
+ string obj_prefix_;
+ string from_;
+ member_access* by_value_;
+ };
+
+ //
+ //
+ struct section_traits: traversal::class_, virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c)
+ : c_ (c),
+ scope_ ("access::object_traits_impl< " + class_fq_name (c) +
+ ", id_" + db.string () + " >")
+ {
+ }
+
+ // Additional code that need to be executed following the call to
+ // init_value().
+ //
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ virtual void
+ section_extra (user_section&)
+ {
+ }
+
+ // Returning "1" means increment by one.
+ //
+ virtual string
+ optimistic_version_increment (semantics::data_member&)
+ {
+ return "1";
+ }
+
+ virtual string
+ update_statement_extra (user_section&)
+ {
+ return "";
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ data_member& m (*s.member);
+
+ class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+ class_* poly_base (poly_derived ? &polymorphic_base (c_) : 0);
+
+ data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // polymorphic hierarchy, then pretend it inherits from the special
+ // version update section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (m) + "_traits");
+ string scope (scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ // bind (id, image_type)
+ //
+ if (load || load_opt || update || update_opt)
+ {
+ os << "std::size_t " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << (reuse_abst ? "," : " id,") << endl
+ << "std::size_t" << (reuse_abst ? "," : " id_size,") << endl
+ << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ // Bind reuse base. It is always first and we never ask it
+ // to bind id(+ver).
+ //
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0 && b.separate_load ());
+ bool load_opt (b.optimistic () && b.separate_load ());
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (load || load_opt || update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::bind (" << endl
+ << "b, 0, 0, i, sk" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ // Bind members.
+ //
+ {
+ instance<bind_member> bm ("", "", &s);
+ traversal::names n (*bm);
+ names (c_, n);
+ }
+
+ // Bind polymorphic image chain for the select statement.
+ //
+ if (s.base != 0 && poly_derived && s.separate_load ())
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::bind (" << endl
+ << "b + n, 0, 0, *i" << acc << ", sk" <<
+ (b->versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ if (!reuse_abst)
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ os << "return n;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow && (load || load_opt))
+ {
+ os << "bool " << scope << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ size_t index (0);
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+ bool load_opt (b.optimistic ());
+
+ if (load || load_opt)
+ {
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "grew = object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::grow (i, t" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+
+ index += b.total + (load_opt ? 1 : 0);
+ }
+ }
+
+ {
+ user_section* ps (&s);
+ instance<grow_member> gm (index, "", ps);
+ traversal::names n (*gm);
+ names (c_, n);
+ }
+
+ // Grow polymorphic image chain.
+ //
+ if (s.base != 0 && poly_derived)
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ size_t cols;
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ cols = b->total + (b->optimistic () ? 1 : 0);
+ if (cols != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::grow (" << endl
+ << "*i" << acc << ", t + " << cols << "UL" <<
+ (b->versioned ? ", svm" : "") << "))" << endl
+ << "i" << acc << "->version++;"
+ << endl;
+ }
+
+ os << "return grew;" << endl
+ << "}";
+ }
+
+ // init (object, image)
+ //
+ if (load)
+ {
+ os << "void " << scope << "::" << endl
+ << "init (object_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (s.base != 0)
+ {
+ if (!poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+
+ if (load)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (o, i, db" <<
+ (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+ else
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::init (" << endl
+ << "o, *i" << acc << ", db" <<
+ (b->versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+ }
+
+ {
+ instance<init_value_member> iv ("", "", true, &s);
+ traversal::names n (*iv);
+ names (c_, n);
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ if (update)
+ {
+ os << (generate_grow ? "bool " : "void ") << scope << "::" << endl
+ << "init (image_type& i," << endl
+ << "const object_type& o";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ // There is no call to init_image_pre() here (which calls the
+ // copy callback for some databases) since we are not going to
+ // touch any of the members that were loaded by query.
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << (generate_grow ? "grew = " : "") <<
+ "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (i, o" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ {
+ instance<init_image_member> ii ("", "", &s);
+ traversal::names n (*ii);
+ names (c_, n);
+ }
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_extra (s);
+ return;
+ }
+
+ string sep (s.versioned ? "\n" : " ");
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ // Statements.
+ //
+ qname table (table_name (c_));
+ string qtable (quote_id (table));
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id_member (c_));
+
+ // select_statement
+ //
+ if (load || load_opt)
+ {
+ size_t depth (poly_derived ? polymorphic_depth (c_) : 1);
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, depth, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_select, s.versioned);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "select_statement[] =" << endl
+ << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ // Join polymorphic bases.
+ //
+ if (depth != 1 && s.base != 0)
+ {
+ bool f (false); //@@ (im)perfect forwarding
+ size_t d (depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c_, f, d, "", s.base);
+ j->traverse (*poly_base);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ // Join tables of inverse members belonging to this section.
+ //
+ {
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* ps (&s); // @@ (im)perfect forwarding
+ instance<object_joins> j (c_, f, depth, ps);
+ j->traverse (c_);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (update || update_opt)
+ {
+ instance<query_parameters> qp (statement_update, table);
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_update, s.versioned);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
+ string extra (update_statement_extra (s));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "load (extra_statement_cache_type& esc, object_type& obj" <<
+ (poly ? ", bool top" : "") << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);"
+ << endl;
+
+ if (s.versioned || s.versioned_containers)
+ os << "const schema_version_migration& svm (" << endl
+ << "esc." << m.name () << ".version_migration (" <<
+ schema_name << "));"
+ << endl;
+
+ // Load values, if any.
+ //
+ if (load || load_opt)
+ {
+ // The SELECT statement for the top override loads all the
+ // values.
+ //
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Note that we don't use delayed load machinery here. While
+ // a section can definitely contain self-referencing pointers,
+ // loading such a pointer won't mess up the data members in the
+ // image that we care about. It also holds true for streaming
+ // result, since the bindings are different.
+
+ os << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding ());"
+ << endl;
+
+ // For the polymorphic case, instead of storing an array of
+ // versions as we do for objects, we will add all the versions
+ // up and use that as a cumulative image chain version. If you
+ // meditate a bit on that, you will realize that it will work
+ // (hint: versions can only increase).
+ //
+ string ver;
+ string ver_decl;
+
+ if (s.base != 0 && poly_derived)
+ {
+ ver = "imv";
+ ver_decl = "std::size_t imv (im.version";
+
+ user_section* b (s.base);
+ string acc ("im.base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ ver_decl += " +\n" + acc + "->version";
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ ver_decl += ")";
+
+ os << ver_decl << ";"
+ << endl;
+ }
+ else
+ ver = "im.version";
+
+ os << "if (" << ver << " != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "}";
+
+ // Id binding is assumed initialized and bound.
+ //
+ os << "select_statement& st (sts.select_statement ());";
+
+ // The statement can be dynamically empty.
+ //
+ if (s.versioned)
+ os << "if (!st.empty ())"
+ << "{";
+
+ os << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ os << "if (r == select_statement::no_data)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (grow (c_, &s))
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()" <<
+ (s.versioned ? ", svm" : "") << "))" << endl
+ << "im.version++;"
+ << endl;
+
+ // The same logic as above.
+ //
+ if (s.base != 0 && poly_derived)
+ os << ver_decl << ";"
+ << endl;
+
+ os << "if (" << ver << " != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ if (opt != 0) // Not load_opt, we do it in poly-derived as well.
+ {
+ os << "if (";
+
+ if (poly_derived)
+ {
+ os << "root_traits::version (*im.base";
+ for (class_* b (poly_base);
+ b != poly_root;
+ b = &polymorphic_base (*b))
+ os << "->base";
+ os << ")";
+ }
+ else
+ os << "version (im)";
+
+ os << " != " << (poly_derived ? "root_traits::" : "") <<
+ "version (obj))" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ if (load)
+ {
+ os << "init (obj, im, &sts.connection ().database ()" <<
+ (s.versioned ? ", svm" : "") << ");";
+ init_value_extra (); // Stream results, etc.
+ os << endl;
+ }
+
+ if (s.versioned)
+ os << "}"; // if (!st.empty ())
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // Call base to load its containers, if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ // If we don't have any values of our own but out base
+ // does, then allow it to load them.
+ //
+ if (b->containers ||
+ (!load && (b->total != 0 || b->optimistic ())))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // This one is tricky: ideally we would do a direct call to
+ // the base's load() (which may not be our immediate base,
+ // BTW) but there is no easy way to resolve base's extra
+ // statements from ours. So, instead, we are going to go
+ // via the dispatch machinery which requires a connection
+ // rather than statements. Not the most efficient way but
+ // simple.
+
+ // Find the "previous" override by starting the search from
+ // our base.
+ //
+ if (b != 0)
+ {
+ // Note that here we are using the base section index to
+ // handle the special version update base.
+ //
+ os << "info.base->find_section_load (" << b->index << "UL) (" <<
+ "esc." << m.name () << ".connection (), obj, " <<
+ // If we don't have any values of our own, then allow the
+ // base load its.
+ //
+ (load ? "false" : "top") << ");"
+ << endl;
+ }
+ }
+
+ // Load our containers, if any.
+ //
+ if (s.containers)
+ {
+ instance<container_calls> t (container_calls::load_call, &s);
+ t->traverse (c_);
+ }
+
+ os << "}";
+ }
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "update (extra_statement_cache_type& esc, " <<
+ "const object_type& obj" <<
+ (poly_derived && s.base != 0 ? ", bool base" : "") << ")"
+ << "{";
+
+ // Call base if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers ||
+ (poly && b->optimistic ()))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // The same (tricky) logic as in load(). Note that here we are
+ // using the base section index to handle the special version
+ // update base.
+ //
+ if (b != 0)
+ os << "if (base)" << endl
+ << "info.base->find_section_update (" << b->index <<
+ "UL) (esc." << m.name () << ".connection (), obj);"
+ << endl;
+ else
+ os << "ODB_POTENTIALLY_UNUSED (base);"
+ << endl;
+ }
+
+ if (s.versioned || s.readwrite_versioned_containers)
+ os << "const schema_version_migration& svm (" << endl
+ << "esc." << m.name () << ".version_migration (" <<
+ schema_name << "));"
+ << endl;
+
+ // Update values, if any.
+ //
+ if (update || update_opt)
+ {
+ os << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "const binding& id (sts.idv_binding ());" // id+version
+ << "binding& imb (sts.update_image_binding ());"
+ << endl;
+
+ if (update)
+ {
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj" << (s.versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+ }
+
+ os << "if (im.version != sts.update_image_version () ||" << endl
+ << "id.version != sts.update_id_binding_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, id.bind, id.count, im, statement_update" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.update_image_version (im.version);"
+ << "sts.update_id_binding_version (id.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (s.versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl;
+
+ if (opt == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+
+ // Update readwrite containers if any.
+ //
+ if (s.readwrite_containers)
+ {
+ instance<container_calls> t (container_calls::update_call, &s);
+ t->traverse (c_);
+ }
+
+ // Update the optimistic concurrency version in the object member.
+ // Very similar code to object.
+ //
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+ }
+
+ os << "}";
+ }
+
+ section_extra (s);
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ using class_::traverse; // Unhide.
+
+ protected:
+ semantics::class_& c_;
+ string scope_;
+ };
+
+ // Output a list of parameters for the persist statement.
+ //
+ struct persist_statement_params: object_columns_base, virtual context
+ {
+ typedef persist_statement_params base;
+
+ persist_statement_params (string& params,
+ query_parameters& qp,
+ const string& sep)
+ : params_ (params), qp_ (qp), sep_ (sep)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ if (!inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m,
+ string const& name,
+ bool first)
+ {
+ string p;
+
+ if (version (m))
+ p = version_value (m);
+ else
+ {
+ const string& qname (quote_id (name));
+ const string& type (column_type ());
+
+ p = auto_ (m) // Only simple, direct id can be auto.
+ ? qp_.auto_id (m, qname, type)
+ : qp_.next (m, qname, type);
+ }
+
+ if (!p.empty ())
+ {
+ if (!first)
+ {
+ params_ += ',';
+ params_ += sep_;
+ }
+
+ params_ += (p != "DEFAULT" ? convert_to (p, column_type (), m) : p);
+ }
+
+ return !p.empty ();
+ }
+
+ virtual string
+ version_value (semantics::data_member&)
+ {
+ return "1";
+ }
+
+ private:
+ string& params_;
+ query_parameters& qp_;
+ const string& sep_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ ()
+ : typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false),
+ index_ (0),
+ grow_base_ (index_),
+ grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
+ bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
+ init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
+ init_view_pointer_member_pre_ (true, *init_value_member_),
+ init_view_pointer_member_post_ (false, *init_value_member_),
+ init_id_value_member_ ("id"),
+ init_id_value_member_id_image_ ("id", "id_"),
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
+ {
+ init ();
+ }
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false),
+ index_ (0),
+ grow_base_ (index_),
+ grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
+ bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
+ init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
+ init_view_pointer_member_pre_ (true, *init_value_member_),
+ init_view_pointer_member_post_ (false, *init_value_member_),
+ init_id_value_member_ ("id"),
+ init_id_value_member_id_image_ ("id", "id_"),
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ if (generate_grow)
+ {
+ grow_base_inherits_ >> grow_base_;
+ grow_member_names_ >> grow_member_;
+ }
+
+ bind_base_inherits_ >> bind_base_;
+ bind_member_names_ >> bind_member_;
+
+ init_image_base_inherits_ >> init_image_base_;
+ init_image_member_names_ >> init_image_member_;
+
+ init_value_base_inherits_ >> init_value_base_;
+ init_value_member_names_ >> init_value_member_;
+
+ init_view_pointer_member_pre_names_ >> init_view_pointer_member_pre_;
+ init_view_pointer_member_post_names_ >> init_view_pointer_member_post_;
+ }
+
+ virtual void
+ init_auto_id (semantics::data_member&, // id member
+ string const&) // image variable prefix
+ {
+ if (insert_send_auto_id)
+ assert (false);
+ }
+
+ virtual void
+ init_image_pre (type&)
+ {
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ context::top_object = context::cur_object = &c;
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+
+ context::top_object = context::cur_object = 0;
+ }
+
+ //
+ // statements
+ //
+
+ enum persist_position
+ {
+ persist_after_columns,
+ persist_after_values
+ };
+
+ virtual string
+ persist_statement_extra (type&, query_parameters&, persist_position)
+ {
+ return "";
+ }
+
+ virtual string
+ update_statement_extra (type&)
+ {
+ return "";
+ }
+
+ //
+ // common
+ //
+
+ virtual void
+ post_query_ (type&, bool /*once_off*/)
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ //
+ // object
+ //
+
+ virtual void
+ object_extra (type&) {}
+
+ virtual void
+ extra_statement_cache_extra_args (bool /*containers*/,
+ bool /*sections*/) {}
+
+ virtual void
+ object_query_statement_ctor_args (type&,
+ std::string const& q,
+ bool process,
+ bool /*prepared*/)
+ {
+ os << "conn," << endl
+ << "text," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ object_erase_query_statement_ctor_args (type&)
+ {
+ os << "conn," << endl
+ << "text," << endl
+ << "q.parameters_binding ()";
+ }
+
+ virtual string
+ optimistic_version_init (semantics::data_member&, bool /*index*/ = false)
+ {
+ return "1";
+ }
+
+ // Returning "1" means increment by one.
+ //
+ virtual string
+ optimistic_version_increment (semantics::data_member&,
+ bool /*index*/ = false)
+ {
+ return "1";
+ }
+
+ virtual bool
+ optimistic_insert_bind_version (semantics::data_member&)
+ {
+ return false;
+ }
+
+ virtual void
+ traverse_object (type& c);
+
+ //
+ // view
+ //
+
+ virtual void
+ view_extra (type&)
+ {
+ }
+
+ virtual void
+ view_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool /*prepared*/)
+ {
+ os << "conn," << endl
+ << q << ".clause ()," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual string
+ from_trailer (type&) { return "";}
+
+ virtual string
+ select_trailer (type& c)
+ {
+ return c.get<view_query> ("query").for_update ? "FOR UPDATE" : "";
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* r (0);
+
+ switch (vo.join)
+ {
+ case view_object::left: r = "LEFT JOIN"; break;
+ case view_object::right: r = "RIGHT JOIN"; break;
+ case view_object::full: r = "FULL JOIN"; break;
+ case view_object::inner: r = "INNER JOIN"; break;
+ case view_object::cross: r = "CROSS JOIN"; break;
+ }
+
+ return r;
+ }
+
+ virtual void
+ traverse_view (type& c);
+
+ struct expression
+ {
+ explicit
+ expression (std::string const& v): kind (literal), value (v) {}
+ expression (view_object* vo): kind (pointer), vo (vo) {}
+
+ enum kind_type {literal, pointer};
+
+ kind_type kind;
+ std::string value;
+ data_member_path member_path;
+ view_object* vo;
+ };
+
+ expression
+ translate_expression (type& c,
+ cxx_tokens const&,
+ semantics::scope& start_scope,
+ location_t loc,
+ string const& prag,
+ bool* placeholder = 0,
+ bool predicate = true);
+ //
+ // composite
+ //
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::composite_value_traits< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // Containers.
+ //
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ inherits (c, grow_base_inherits_);
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (b);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ os << "std::size_t n (0);"
+ << "ODB_POTENTIALLY_UNUSED (n);"
+ << endl;
+
+ inherits (c, bind_base_inherits_);
+ names (c, bind_member_names_);
+
+ os << "}";
+
+ // init (image, value)
+ //
+ os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
+ << "init (image_type& i," << endl
+ << "const value_type& o," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ inherits (c, init_image_base_inherits_);
+ names (c, init_image_member_names_);
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+
+ // init (value, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (value_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ inherits (c, init_value_base_inherits_);
+ names (c, init_value_member_names_);
+
+ os << "}";
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<view_query_columns_type> view_query_columns_type_;
+
+ size_t index_;
+ instance<grow_base> grow_base_;
+ traversal::inherits grow_base_inherits_;
+ instance<grow_member> grow_member_;
+ traversal::names grow_member_names_;
+ instance<grow_member> grow_version_member_;
+ instance<grow_member> grow_discriminator_member_;
+
+
+ instance<bind_base> bind_base_;
+ traversal::inherits bind_base_inherits_;
+ instance<bind_member> bind_member_;
+ traversal::names bind_member_names_;
+ instance<bind_member> bind_id_member_;
+ instance<bind_member> bind_version_member_;
+ instance<bind_member> bind_discriminator_member_;
+
+ instance<init_image_base> init_image_base_;
+ traversal::inherits init_image_base_inherits_;
+ instance<init_image_member> init_image_member_;
+ traversal::names init_image_member_names_;
+
+ instance<init_image_member> init_id_image_member_;
+ instance<init_image_member> init_version_image_member_;
+
+ instance<init_value_base> init_value_base_;
+ traversal::inherits init_value_base_inherits_;
+ instance<init_value_member> init_value_member_;
+ traversal::names init_value_member_names_;
+
+ instance<init_view_pointer_member> init_view_pointer_member_pre_;
+ instance<init_view_pointer_member> init_view_pointer_member_post_;
+ traversal::names init_view_pointer_member_pre_names_;
+ traversal::names init_view_pointer_member_post_names_;
+
+ instance<init_value_member> init_id_value_member_;
+ instance<init_value_member> init_id_value_member_id_image_;
+ instance<init_value_member> init_version_value_member_;
+ instance<init_value_member> init_named_version_value_member_;
+ instance<init_value_member> init_discriminator_value_member_;
+ instance<init_value_member> init_named_discriminator_value_member_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ extra_pre ();
+
+ os << "#include <cassert>" << endl
+ << "#include <cstring> // std::memcpy" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <typeinfo>" << endl;
+
+ os << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/polymorphic-map.hxx>" << endl;
+
+ if (embedded_schema)
+ os << "#include <odb/schema-catalog-impl.hxx>" << endl;
+
+ if (multi_dynamic)
+ os << "#include <odb/function-table.hxx>" << endl;
+
+ os << endl;
+
+ os << "#include <odb/" << db << "/traits.hxx>" << endl
+ << "#include <odb/" << db << "/database.hxx>" << endl
+ << "#include <odb/" << db << "/transaction.hxx>" << endl
+ << "#include <odb/" << db << "/connection.hxx>" << endl
+ << "#include <odb/" << db << "/statement.hxx>" << endl
+ << "#include <odb/" << db << "/statement-cache.hxx>" << endl;
+
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-statements.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-statements.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-statements.hxx>" << endl;
+
+ if (features.view)
+ os << "#include <odb/" << db << "/view-statements.hxx>" << endl;
+
+ if (features.section)
+ os << "#include <odb/" << db << "/section-statements.hxx>" << endl;
+
+ os << "#include <odb/" << db << "/container-statements.hxx>" << endl
+ << "#include <odb/" << db << "/exceptions.hxx>" << endl;
+
+ if (options.generate_query ())
+ {
+ if (options.generate_prepared ())
+ os << "#include <odb/" << db << "/prepared-query.hxx>" << endl;
+
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-result.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-result.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-result.hxx>" << endl;
+
+ if (features.view)
+ os << "#include <odb/" << db << "/view-result.hxx>" << endl;
+ }
+
+ extra_post ();
+
+ os << endl;
+ }
+
+ virtual void
+ extra_pre ()
+ {
+ }
+
+ virtual void
+ extra_post ()
+ {
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SOURCE_HXX
diff --git a/odb/odb/relational/sqlite/common.cxx b/odb/odb/relational/sqlite/common.cxx
new file mode 100644
index 0000000..03a3599
--- /dev/null
+++ b/odb/odb/relational/sqlite/common.cxx
@@ -0,0 +1,217 @@
+// file : odb/relational/sqlite/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/sqlite/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ case sql_type::INTEGER:
+ {
+ traverse_integer (mi);
+ break;
+ }
+ case sql_type::REAL:
+ {
+ traverse_real (mi);
+ break;
+ }
+ case sql_type::TEXT:
+ {
+ if (mi.st->stream)
+ traverse_text_stream (mi);
+ else
+ traverse_text (mi);
+ break;
+ }
+ case sql_type::BLOB:
+ {
+ if (mi.st->stream)
+ traverse_blob_stream (mi);
+ else
+ traverse_blob (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_sqlite >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info&)
+ {
+ type_ = "long long";
+ }
+
+ void member_image_type::
+ traverse_real (member_info&)
+ {
+ type_ = "double";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_stream (member_info&)
+ {
+ type_ = "sqlite::stream_buffers";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info&)
+ {
+ type_id_ = "sqlite::id_integer";
+ }
+
+ void member_database_type_id::
+ traverse_real (member_info&)
+ {
+ type_id_ = "sqlite::id_real";
+ }
+
+ void member_database_type_id::
+ traverse_text (member_info&)
+ {
+ type_id_ = "sqlite::id_text";
+ }
+
+ void member_database_type_id::
+ traverse_blob (member_info&)
+ {
+ type_id_ = "sqlite::id_blob";
+ }
+
+ void member_database_type_id::
+ traverse_text_stream (member_info&)
+ {
+ type_id_ = "sqlite::id_text_stream";
+ }
+
+ void member_database_type_id::
+ traverse_blob_stream (member_info&)
+ {
+ type_id_ = "sqlite::id_blob_stream";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/sqlite/common.hxx b/odb/odb/relational/sqlite/common.hxx
new file mode 100644
index 0000000..4d6089e
--- /dev/null
+++ b/odb/odb/relational/sqlite/common.hxx
@@ -0,0 +1,147 @@
+// file : odb/relational/sqlite/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SQLITE_COMMON_HXX
+#define ODB_RELATIONAL_SQLITE_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the sqlite namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_real (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_text (member_info& m)
+ {
+ traverse_string (m);
+ }
+
+ virtual void
+ traverse_blob (member_info& m)
+ {
+ traverse_string (m);
+ }
+
+ // String covers both text and blob.
+ //
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_text_stream (member_info& m)
+ {
+ traverse_stream (m);
+ }
+
+ virtual void
+ traverse_blob_stream (member_info& m)
+ {
+ traverse_stream (m);
+ }
+
+ virtual void
+ traverse_stream (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_real (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_stream (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_real (member_info&);
+
+ virtual void
+ traverse_text (member_info&);
+
+ virtual void
+ traverse_blob (member_info&);
+
+ virtual void
+ traverse_text_stream (member_info&);
+
+ virtual void
+ traverse_blob_stream (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_SQLITE_COMMON_HXX
diff --git a/odb/odb/relational/sqlite/context.cxx b/odb/odb/relational/sqlite/context.cxx
new file mode 100644
index 0000000..9a4369f
--- /dev/null
+++ b/odb/odb/relational/sqlite/context.cxx
@@ -0,0 +1,490 @@
+// file : odb/relational/sqlite/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <vector>
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/sqlite/context.hxx>
+#include <odb/relational/sqlite/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "INTEGER", 0, false},
+
+ {"char", "TEXT", 0, false},
+ {"wchar_t", "TEXT", 0, false},
+ {"signed char", "INTEGER", 0, false},
+ {"unsigned char", "INTEGER", 0, false},
+
+ {"short int", "INTEGER", 0, false},
+ {"short unsigned int", "INTEGER", 0, false},
+
+ {"int", "INTEGER", 0, false},
+ {"unsigned int", "INTEGER", 0, false},
+
+ {"long int", "INTEGER", 0, false},
+ {"long unsigned int", "INTEGER", 0, false},
+
+ {"long long int", "INTEGER", 0, false},
+ {"long long unsigned int", "INTEGER", 0, false},
+
+ // SQLite stores NaN as NULL.
+ //
+ {"float", "REAL", 0, true},
+ {"double", "REAL", 0, true},
+
+ {"::std::string", "TEXT", 0, false},
+ {"::std::wstring", "TEXT", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = true;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = false;
+ global_index = true;
+ global_fkey = false;
+ data_->bind_vector_ = "sqlite::bind*";
+ data_->truncated_vector_ = "bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("sqlite-grow"))
+ r_ = c.get<bool> ("sqlite-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("sqlite-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("sqlite-grow"))
+ return c.get<bool> ("sqlite-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> () ||
+ bt.is_a<semantics::fund_wchar> ())
+ {
+ if (a->size () != 0)
+ r = "TEXT";
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ namespace
+ {
+ struct sql_parser
+ {
+ typedef context::invalid_sql_type invalid_sql_type;
+
+ sql_parser (custom_db_types const* ct): ct_ (ct) {}
+
+ sql_type
+ parse (string sql)
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct_ != 0)
+ {
+ for (custom_db_types::const_iterator i (ct_->begin ());
+ i != ct_->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sql))
+ {
+ r.to = t.type.replace (sql, t.to);
+ r.from = t.type.replace (sql, t.from);
+ sql = t.type.replace (sql, t.as);
+ break;
+ }
+ }
+ }
+
+ // Parse the type into a sequence of identifiers.
+ //
+ try
+ {
+ l_.lex (sql);
+
+ for (sql_token t (l_.next ()); t.type () != sql_token::t_eos;)
+ {
+ sql_token::token_type tt (t.type ());
+
+ if (tt == sql_token::t_identifier)
+ {
+ ids_.push_back (context::upcase (t.identifier ()));
+ t = l_.next ();
+
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ if (!parse_range ())
+ return error (m_);
+
+ t = l_.next ();
+ }
+ }
+ else
+ return error ("expected SQLite type name instead of '" +
+ t.string () + "'");
+ }
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error ("invalid SQLite type declaration: " + e.message);
+ }
+
+ if (ids_.empty ())
+ return error ("expected SQLite type name");
+
+ // First check our own types.
+ //
+ if (ids_.size () == 2 && ids_[0] == "TEXT" && ids_[1] == "STREAM")
+ {
+ r.type = sql_type::TEXT;
+ r.stream = true;
+ }
+ if (ids_.size () == 2 && ids_[0] == "BLOB" && ids_[1] == "STREAM")
+ {
+ r.type = sql_type::BLOB;
+ r.stream = true;
+ }
+ //
+ // Apply the first four rules of the SQLite type to affinity
+ // conversion algorithm.
+ //
+ else if (find ("INT"))
+ r.type = sql_type::INTEGER;
+ else if (find ("TEXT") || find ("CHAR") || find ("CLOB"))
+ r.type = sql_type::TEXT;
+ else if (find ("BLOB"))
+ r.type = sql_type::BLOB;
+ else if (find ("REAL") || find ("FLOA") || find ("DOUB"))
+ r.type = sql_type::REAL;
+ else
+ {
+ // Instead of the fifth rule which maps everything else
+ // to NUMERICAL (which we don't have), map some commonly
+ // used type names to one of the above types.
+ //
+ string const& id (ids_[0]);
+
+ if (id == "NUMERIC")
+ r.type = sql_type::REAL;
+ else if (id == "DECIMAL")
+ r.type = sql_type::TEXT;
+ else if (id == "BOOLEAN" || id == "BOOL")
+ r.type = sql_type::INTEGER;
+ else if (id == "DATE" || id == "TIME" || id == "DATETIME")
+ r.type = sql_type::TEXT;
+ else
+ return error ("unknown SQLite type '" + id + "'");
+ }
+
+ return r;
+ }
+
+ bool
+ parse_range ()
+ {
+ // Skip tokens until we get the closing paren.
+ //
+ for (sql_token t (l_.next ());; t = l_.next ())
+ {
+ if (t.punctuation () == sql_token::p_rparen)
+ break;
+
+ if (t.type () == sql_token::t_eos)
+ {
+ m_ = "missing ')' in SQLite type declaration";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ sql_type
+ error (string const& m)
+ {
+ if (ct_ == 0)
+ return sql_type ();
+ else
+ throw invalid_sql_type (m);
+ }
+
+ bool
+ find (string const& str) const
+ {
+ for (identifiers::const_iterator i (ids_.begin ());
+ i != ids_.end (); ++i)
+ {
+ if (i->find (str) != string::npos)
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ custom_db_types const* ct_;
+ sql_lexer l_;
+ string m_; // Error message.
+
+ typedef vector<string> identifiers;
+ identifiers ids_;
+ };
+ }
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ sql_type context::
+ parse_sql_type (string const& sqlt, custom_db_types const* ct)
+ {
+ sql_parser p (ct);
+ return p.parse (sqlt);
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/context.hxx b/odb/odb/relational/sqlite/context.hxx
new file mode 100644
index 0000000..777998b
--- /dev/null
+++ b/odb/odb/relational/sqlite/context.hxx
@@ -0,0 +1,146 @@
+// file : odb/relational/sqlite/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SQLITE_CONTEXT_HXX
+#define ODB_RELATIONAL_SQLITE_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ INTEGER,
+ REAL,
+ TEXT,
+ BLOB,
+ invalid
+ };
+
+ sql_type (): type (invalid), stream (false) {}
+
+ core_type type;
+ bool stream; // TEXT or BLOB via sqlite3_blob_open().
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string const&, custom_db_types const* = 0);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type& f,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SQLITE_CONTEXT_HXX
diff --git a/odb/odb/relational/sqlite/header.cxx b/odb/odb/relational/sqlite/header.cxx
new file mode 100644
index 0000000..1aafe7a
--- /dev/null
+++ b/odb/odb/relational/sqlite/header.cxx
@@ -0,0 +1,63 @@
+// file : odb/relational/sqlite/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/inline.cxx b/odb/odb/relational/sqlite/inline.cxx
new file mode 100644
index 0000000..dd3274f
--- /dev/null
+++ b/odb/odb/relational/sqlite/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/sqlite/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "null;";
+ else
+ os << "i." << mi.var << "null = true;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/model.cxx b/odb/odb/relational/sqlite/model.cxx
new file mode 100644
index 0000000..da16ded
--- /dev/null
+++ b/odb/odb/relational/sqlite/model.cxx
@@ -0,0 +1,91 @@
+// file : odb/relational/sqlite/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ type (semantics::data_member& m)
+ {
+ // Translate BLOB|TEXT STREAM to just BLOB|TEXT.
+ //
+ string r (relational::object_columns::type (m));
+
+ sql_type const& t (parse_sql_type (r, m, false));
+ if (t.stream)
+ {
+ switch (t.type)
+ {
+ case sql_type::BLOB: r = "BLOB"; break;
+ case sql_type::TEXT: r = "TEXT"; break;
+ default: break;
+ }
+ }
+
+ return r;
+ }
+
+ virtual bool
+ null (semantics::data_member& m)
+ {
+ return options.sqlite_override_null () || base::null (m);
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to INTEGER.
+ //
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+ if (t.type != sql_type::INTEGER)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to SQLite INTEGER" << endl;
+
+ throw operation_failed ();
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+
+ virtual void
+ primary_key (sema_rel::primary_key& pk)
+ {
+ if (pk.auto_ () && options.sqlite_lax_auto_id ())
+ pk.extra ()["lax"] = "true";
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/schema.cxx b/odb/odb/relational/sqlite/schema.cxx
new file mode 100644
index 0000000..f5549b4
--- /dev/null
+++ b/odb/odb/relational/sqlite/schema.cxx
@@ -0,0 +1,455 @@
+// file : odb/relational/sqlite/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+
+ //
+ // Drop.
+ //
+
+ struct drop_column: trav_rel::drop_column, relational::common
+ {
+ drop_column (relational::common const& c)
+ : relational::common (c), first_ (true) {}
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ // SQLite does not support dropping columns. If this column is
+ // not NULLable, then there is nothing we can do. Otherwise, do
+ // a logical DROP by setting all the values to NULL.
+ //
+ sema_rel::column& c (find<sema_rel::column> (dc));
+
+ if (!c.null ())
+ {
+ cerr << "error: SQLite does not support dropping of columns" <<
+ endl;
+ cerr << "info: first dropped column is '" << dc.name () <<
+ "' in table '" << dc.table ().name () << "'" << endl;
+ cerr << "info: could have performed logical drop if the column " <<
+ "allowed NULL values" << endl;
+ throw operation_failed ();
+ }
+
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (dc.name ()) << " = NULL";
+ }
+
+ private:
+ bool first_;
+ };
+ // Not registered as an override.
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In SQLite, index names can be qualified with the database.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+ return quote_id (n);
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // In SQLite there is no way to drop foreign keys except as part
+ // of the table.
+ //
+ if (pass_ != 2)
+ return;
+
+ // Polymorphic base cleanup code. Because we cannot drop foreign
+ // keys, we will trigger cascade deletion. The only way to work
+ // around this problem is to delete from the root table and rely
+ // on the cascade to clean up the rest.
+ //
+ if (migration && t.extra ()["kind"] == "polymorphic derived object")
+ {
+ using sema_rel::model;
+ using sema_rel::table;
+ using sema_rel::primary_key;
+ using sema_rel::foreign_key;
+
+ model& m (dynamic_cast<model&> (t.scope ()));
+
+ table* p (&t);
+ do
+ {
+ // The polymorphic link is the first primary key.
+ //
+ for (table::names_iterator i (p->names_begin ());
+ i != p->names_end (); ++i)
+ {
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (
+ &i->nameable ()))
+ {
+ p = m.find<table> (fk->referenced_table ());
+ assert (p != 0); // Base table should be there.
+ break;
+ }
+ }
+ }
+ while (p->extra ()["kind"] != "polymorphic root object");
+
+ primary_key& rkey (*p->find<primary_key> (""));
+ primary_key& dkey (*t.find<primary_key> (""));
+ assert (rkey.contains_size () == dkey.contains_size ());
+ delete_ (p->name (), t.name (), rkey, dkey);
+ }
+
+ drop (t, migration);
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ using sema_rel::alter_table;
+ using sema_rel::add_column;
+ using sema_rel::add_foreign_key;
+
+ alter_table& at (static_cast<alter_table&> (ac.scope ()));
+
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD COLUMN ";
+
+ // In SQLite it is impossible to alter a column later, so unless
+ // it has a default value, we add it as NULL. Without this, it
+ // will be impossible to add a column to a table that contains
+ // some rows.
+ //
+ create (ac);
+
+ // SQLite doesn't support adding foreign keys other than inline
+ // via a column definition. See if we can handle any.
+ //
+ add_foreign_key* afk (0);
+
+ for (add_column::contained_iterator i (ac.contained_begin ());
+ i != ac.contained_end ();
+ ++i)
+ {
+ if ((afk = dynamic_cast<add_foreign_key*> (&i->key ())))
+ {
+ // Check that it is a single-column foreign key. Also make
+ // sure the column and foreign key are from the same changeset.
+ //
+ if (afk->contains_size () != 1 || &ac.scope () != &afk->scope ())
+ afk = 0;
+ else
+ break;
+ }
+ }
+
+ if (afk != 0)
+ {
+ os << " CONSTRAINT " << quote_id (afk->name ()) << " REFERENCES " <<
+ quote_id (afk->referenced_table ().uname ()) << " (" <<
+ quote_id (afk->referenced_columns ()[0]) << ")";
+
+ bool del (afk->on_delete () != sema_rel::foreign_key::no_action);
+ bool def (!afk->not_deferrable ());
+
+ if (del || def)
+ {
+ instance<relational::create_foreign_key> cfk (*this);
+
+ if (del)
+ cfk->on_delete (afk->on_delete ());
+
+ if (def)
+ cfk->deferrable (afk->deferrable ());
+ }
+
+ afk->set ("sqlite-fk-defined", true); // Mark it as defined.
+ }
+
+ os << endl;
+ post_statement ();
+ }
+
+ virtual void
+ auto_ (sema_rel::primary_key& pk)
+ {
+ if (pk.extra ().count ("lax"))
+ os << " /*AUTOINCREMENT*/";
+ else
+ os << " AUTOINCREMENT";
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // In SQLite, all constraints are defined as part of a table.
+ //
+ os << "," << endl
+ << " CONSTRAINT ";
+
+ create (fk);
+ }
+
+ virtual string
+ table_name (sema_rel::foreign_key& fk)
+ {
+ // In SQLite, the referenced table cannot be qualified with the
+ // database name (it has to be in the same database anyway).
+ //
+ return quote_id (fk.referenced_table ().uname ());
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In SQLite, index names can be qualified with the database.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+ return quote_id (n);
+ }
+
+ virtual string
+ table_name (sema_rel::index& in)
+ {
+ // In SQLite, the index table cannot be qualified with the
+ // database name (it has to be in the same database).
+ //
+ return quote_id (
+ static_cast<sema_rel::table&> (in.scope ()).name ().uname ());
+ }
+ };
+ entry<create_index> create_index_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::table& t)
+ {
+ // For SQLite we do everything in a single pass since there
+ // is no way to add constraints later.
+ //
+ if (pass_ == 1)
+ create (t);
+ }
+ };
+ entry<create_table> create_table_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQLite can only add a single column per ALTER TABLE statement.
+ //
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
+ names (at, n);
+
+ // SQLite does not support altering columns.
+ //
+ if (sema_rel::alter_column* ac = check<sema_rel::alter_column> (at))
+ {
+ cerr << "error: SQLite does not support altering of columns"
+ << endl;
+ cerr << "info: first altered column is '" << ac->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ // SQLite does not support dropping constraints. We are going to
+ // ignore this if the column is NULL'able since in most cases
+ // the constraint is going to be dropped as a result of the
+ // column drop (e.g., an object pointer member got deleted).
+ // If we were not to allow this, then it would be impossible
+ // to do logical drop for pointer columns.
+ //
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ drop_foreign_key* dfk (
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()));
+
+ if (dfk == 0)
+ continue;
+
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ for (foreign_key::contains_iterator j (fk.contains_begin ());
+ j != fk.contains_end (); ++j)
+ {
+ if (j->column ().null ())
+ continue;
+
+ cerr << "error: SQLite does not support dropping of foreign " <<
+ "keys" << endl;
+ cerr << "info: first dropped foreign key is '" << dfk->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ cerr << "info: could have ignored it if the contained " <<
+ "column(s) allowed NULL values" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQLite does not support altering columns (we have to do this
+ // in both alter_table_pre/post because of the
+ // check_alter_column_null() test in the common code).
+ //
+ if (sema_rel::alter_column* ac = check<sema_rel::alter_column> (at))
+ {
+ cerr << "error: SQLite does not support altering of columns"
+ << endl;
+ cerr << "info: first altered column is '" << ac->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ // Try to do logical column drop.
+ //
+ if (check<sema_rel::drop_column> (at))
+ {
+ pre_statement ();
+
+ os << "UPDATE " << quote_id (at.name ()) << endl
+ << " SET ";
+
+ drop_column dc (*this);
+ trav_rel::unames n (dc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ // SQLite doesn't support adding foreign keys other than inline
+ // via a column definition. See if there are any that we couldn't
+ // handle that way.
+ //
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ sema_rel::add_foreign_key* afk (
+ dynamic_cast<sema_rel::add_foreign_key*> (&i->nameable ()));
+
+ if (afk == 0 || afk->count ("sqlite-fk-defined"))
+ continue;
+
+ cerr << "error: SQLite does not support adding foreign keys"
+ << endl;
+ cerr << "info: first added foreign key is '" << afk->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " INTEGER NOT NULL," << endl
+ << " " << qm_ << " INTEGER NOT NULL)" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "INSERT OR IGNORE INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/source.cxx b/odb/odb/relational/sqlite/source.cxx
new file mode 100644
index 0000000..5a4b9d3
--- /dev/null
+++ b/odb/odb/relational/sqlite/source.cxx
@@ -0,0 +1,471 @@
+// file : odb/relational/sqlite/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters, context
+ {
+ query_parameters (base const& x): base (x) {}
+
+ virtual string
+ next (semantics::data_member& m,
+ const string& column,
+ const string& sqlt)
+ {
+ // Handle stream columns. Specifically, we somehow need to
+ // pass the column name to the code that runs in the
+ // statement. So what we are going to do is encode it
+ // in the parameter name.
+ //
+ if (sk_ == statement_insert || sk_ == statement_update)
+ {
+ const sql_type& t (parse_sql_type (sqlt, m, false));
+ if (t.stream)
+ {
+ // The column name is quoted.
+ //
+ string r (column);
+ r[0] = '$'; // Replace leading '"'.
+ r.resize (r.size () - 1); // Remove trailing '"'.
+
+ // Verify it only contains allowed characters.
+ //
+ for (size_t i (1); i != r.size (); ++i)
+ {
+ char c (r[i]);
+ if (c != '_' &&
+ (c < '0' || c > '9') &&
+ (c < 'a' || c > 'z') &&
+ (c < 'A' || c > 'Z'))
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: unsupported character '" << c << "' in "
+ << sqlt << " column name " << column << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: STREAM column can contain alpha-numeric "
+ << "characters plus '_'" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // For TEXT columns, since we use the *_bind_zeroblob()
+ // function (there is no *_bind_zerotext()), the value
+ // that will be stored is BLOB, not TEXT, unless we
+ // explicitly CAST it. The user better make sure the
+ // encoding of raw TEXT data they are going to write
+ // matches the database encoding.
+ //
+ if (t.type == sql_type::TEXT)
+ r = "CAST(" + r + " AS TEXT)";
+
+ return r;
+ }
+ }
+
+ return "?";
+ }
+ };
+ entry<query_parameters> query_parameters_;
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::integer;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::real;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_text (member_info& mi)
+ {
+ os << b << ".type = sqlite::image_traits<" << endl
+ << " " << mi.fq_type () << "," << endl
+ << " sqlite::id_text>::bind_value;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_blob (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::blob;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::stream;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_stream (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = true;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct statement_columns_common: context
+ {
+ void
+ process (relational::statement_columns& cs, statement_kind sk)
+ {
+ using relational::statement_columns;
+
+ // For SELECT statements, add _ROWID_ "follow-up" column to
+ // each stream column. The reason we need both, and not just
+ // ROWID is the NULL value. Let's hope that SELECT'ing a BLOB
+ // but not actually reading it with sqlite3_result_blob() is
+ // as fast as not SELECT'ing it.
+ //
+ if (sk != statement_select)
+ return;
+
+ for (statement_columns::iterator i (cs.begin ());
+ i != cs.end (); ++i)
+ {
+ if (parse_sql_type (i->type, *i->member).stream)
+ {
+ // Column is already table-qualified and quoted. Do some
+ // surgery to replace it with _ROWID_. That is, we want to
+ // transform "table"."column" to "table"."_ROWID_".
+ //
+ string c (i->column);
+ string::size_type n (c.size ()), p (c.rfind ('"', n - 2));
+ assert (p != string::npos);
+ string as (c, p + 1, n - p - 2);
+ c.resize (p);
+ c += "\"_ROWID_\"";
+
+ // We are going to pack this "tightly", without any newlines,
+ // so that the statement processing code treats them as a
+ // single column.
+ //
+ i->column += ',';
+ i->column += c;
+ }
+ }
+ }
+ };
+
+ struct container_traits: relational::container_traits,
+ statement_columns_common
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since SQLite can execute several
+ // interleaving statements.
+ //
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits,
+ statement_columns_common
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, statement_columns_common
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_auto_id (semantics::data_member& m, string const& im)
+ {
+ // Don't set the id value to NULL if this is a nullable wrapper.
+ // This will allow the user to control whether the value is auto or
+ // manually assigned by using something like this:
+ //
+ // #pragma db auto
+ // odb::nullable<int64_t> id;
+ //
+ semantics::type& t (utype (m));
+ if (wrapper (t) && t.template get<bool> ("wrapper-null-handler"))
+ return;
+
+ os << im << "null = true;"
+ << endl;
+ }
+
+ virtual string
+ select_trailer (type&)
+ {
+ // SQLite has not support for FOR UPDATE and since this is an
+ // optimization, we simply ignore it.
+ //
+ return "";
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* n (0);
+
+ if (vo.join == view_object::full)
+ n = "FULL OUTER JOIN";
+ else if (vo.join == view_object::right)
+ n = "RIGHT OUTER JOIN";
+
+ if (n != 0)
+ {
+ error (vo.loc) << n << " is not supported by SQLite" << endl;
+ throw operation_failed ();
+ }
+
+ return base::join_syntax (vo);
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/validator.cxx b/odb/odb/relational/validator.cxx
new file mode 100644
index 0000000..50c887e
--- /dev/null
+++ b/odb/odb/relational/validator.cxx
@@ -0,0 +1,638 @@
+// file : odb/relational/validator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+#include <iostream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/traversal.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/validator.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace
+ {
+ //
+ // Pass 2.
+ //
+
+ struct data_member2: traversal::data_member, context
+ {
+ data_member2 (bool& valid)
+ : valid_ (valid)
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ if (transient (m))
+ return;
+
+ if (null (m))
+ {
+ if (semantics::class_* c = composite_wrapper (utype (m)))
+ {
+ if (has_a (*c, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: composite member containing containers cannot "
+ << "be null" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column ()
+ << ": info: composite value type is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check on-delete.
+ //
+ if (m.count ("on-delete"))
+ {
+ const char* kp (container (m) ? "value" : "");
+ location l (m.location ());
+
+ // Make sure it is a pointer or a member with points_to pragma.
+ //
+ if (!object_pointer (utype (m, kp)) && !points_to (m))
+ {
+ error (l) << "on_delete specified for non-object pointer" << endl;
+ valid_ = false;
+ }
+
+ // Make sure it is not inverse.
+ //
+ if (inverse (m, kp))
+ {
+ error (l) << "on_delete specified for inverse object " <<
+ "pointer" << endl;
+ valid_ = false;
+ }
+
+ // Make sure the pointer is nullable if asked to set it to NULL.
+ //
+ using sema_rel::foreign_key;
+
+ if (m.get<foreign_key::action_type> ("on-delete") ==
+ foreign_key::set_null &&
+ !null (m, kp))
+ {
+ error (l) << "set_null specified for non-nullable object "
+ "pointer" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ };
+
+ struct object_no_id_members: object_members_base
+ {
+ object_no_id_members (bool& valid)
+ : object_members_base (false, false, true), valid_ (valid), dm_ (0)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ if (inverse (m))
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: inverse object pointer member '" << member_prefix_
+ << m.name () << "' in an object without an object id" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: container member '" << member_prefix_ << m.name ()
+ << "' in an object without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ semantics::data_member* old_dm (dm_);
+
+ if (dm_ == 0)
+ dm_ = m;
+
+ object_members_base::traverse_composite (m, c);
+
+ dm_ = old_dm;
+ }
+
+ private:
+ bool& valid_;
+ semantics::data_member* dm_; // Direct object data member.
+ };
+
+ struct composite_id_members: object_members_base
+ {
+ composite_id_members (bool& valid)
+ : object_members_base (false, false, true), valid_ (valid), dm_ (0)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: object pointer member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id"
+ << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ if (readonly (member_path_, member_scope_))
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: readonly member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id"
+ << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: container member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id"
+ << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ semantics::data_member* old_dm (dm_);
+
+ if (dm_ == 0)
+ dm_ = m;
+
+ object_members_base::traverse_composite (m, c);
+
+ dm_ = old_dm;
+ }
+
+ private:
+ bool& valid_;
+ semantics::data_member* dm_; // Direct composite member.
+ };
+
+ struct view_members: object_members_base
+ {
+ view_members (bool& valid)
+ : object_members_base (false, false, true), valid_ (valid), dm_ (0)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ if (dm_ != 0 && object_pointer (utype (m)))
+ {
+ location const& l (dm_->location ());
+
+ error (l) << "nested view data member '" << member_prefix_
+ << m.name () << "' is an object pointer" << endl;
+ info (l) << "views can only contain direct object pointer members"
+ << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: view data member '" << member_prefix_ << m.name ()
+ << "' is a container" << endl;
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << ": info: views cannot contain containers" << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ semantics::data_member* old_dm (dm_);
+
+ if (dm_ == 0)
+ dm_ = m;
+
+ object_members_base::traverse_composite (m, c);
+
+ dm_ = old_dm;
+ }
+
+ private:
+ bool& valid_;
+ semantics::data_member* dm_; // Direct view data member.
+ };
+
+ struct class2: traversal::class_, context
+ {
+ class2 (bool& valid)
+ : valid_ (valid),
+ typedefs_ (true),
+ data_member_ (valid),
+ object_no_id_members_ (valid),
+ composite_id_members_ (valid),
+ view_members_ (valid)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ data_member_names_ >> data_member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+ switch (ck)
+ {
+ case class_object:
+ names (c);
+ traverse_object (c);
+ break;
+ case class_view:
+ names (c);
+ traverse_view (c);
+ break;
+ case class_composite:
+ names (c);
+ traverse_composite (c);
+ break;
+ case class_other:
+ break;
+ }
+
+ // Make sure indexes are not defined for anything other than objects.
+ //
+ if (c.count ("index") && ck != class_object)
+ {
+ indexes& ins (c.get<indexes> ("index"));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ error (i->loc) << "index definition on a non-persistent class"
+ << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ data_member_path* id (id_member (c));
+
+ if (id != 0)
+ {
+ if (semantics::class_* cm = composite_wrapper (utype (*id)))
+ {
+ location idl (id->front ()->location ());
+
+ // Composite id cannot be auto.
+ //
+ if (auto_ (*id))
+ {
+ error (idl) << "composite id cannot be automatically assigned"
+ << endl;
+ valid_ = false;
+ }
+
+ // Make sure we don't have any containers or pointers in this
+ // composite value type.
+ //
+ if (valid_)
+ {
+ composite_id_members_.traverse (*cm);
+
+ if (!valid_)
+ info (idl) << "composite id is defined here" << endl;
+ }
+
+ // Check that the composite value type is default-constructible.
+ //
+ if (!cm->default_ctor ())
+ {
+ os << cm->file () << ":" << cm->line () << ":" << cm->column ()
+ << ": error: composite value type that is used as object id "
+ << "is not default-constructible" << endl;
+
+ os << cm->file () << ":" << cm->line () << ":" << cm->column ()
+ << ": info: provide default constructor for this value type"
+ << endl;
+
+ info (idl) << "composite id is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else
+ {
+ if (!abstract (c))
+ {
+ // Make sure we don't have any containers or inverse pointers.
+ //
+ object_no_id_members_.traverse (c);
+ }
+ }
+
+ names (c, data_member_names_);
+
+ // Validate bulk operation support.
+ //
+ for (bool i (true); i && c.count ("bulk"); i = false)
+ {
+ location_t l (c.get<location_t> ("bulk-location"));
+
+ if (polymorphic (c))
+ {
+ error (l) << "bulk operations on polymorphic objects are "
+ "not supported" << endl;
+ valid_ = false;
+ break;
+ }
+
+ if (has_a (c, test_straight_container))
+ {
+ error (l) << "bulk operations on objects with containers are "
+ "not supported" << endl;
+ valid_ = false;
+ break;
+ }
+
+ bool update (true);
+
+ // Unless we only have manually-updated sections, we cannot generate
+ // the bulk update operation.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (uss.begin ());
+ update && i != uss.end ();
+ ++i)
+ {
+ const user_section& s (*i);
+
+ // Skip special sections.
+ //
+ if (s.special != user_section::special_ordinary)
+ continue;
+
+ // Always-updated section still needs a separate statement
+ // (since it may not be loaded).
+ //
+ if (!s.update_empty () && s.update != user_section::update_manual)
+ update = false;
+ }
+
+ c.set ("bulk-persist", true);
+ if (update) c.set ("bulk-update", true);
+ c.set ("bulk-erase", true);
+ }
+
+ // Validate indexes.
+ //
+ {
+ indexes& ins (c.get<indexes> ("index"));
+
+ // Make sure index members are not transient, inverse, or
+ // containers.
+ //
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ index& in (*i);
+
+ for (index::members_type::iterator i (in.members.begin ());
+ i != in.members.end (); ++i)
+ {
+ index::member& im (*i);
+ semantics::data_member& m (*im.path.back ());
+
+ if (transient (m))
+ {
+ error (im.loc) << "index member is transient" << endl;
+ valid_ = false;
+ }
+
+ if (inverse (m))
+ {
+ error (im.loc) << "index member is an inverse object " <<
+ "pointer" << endl;
+ valid_ = false;
+ }
+
+ if (container (m))
+ {
+ error (im.loc) << "index member is a container" << endl;
+ valid_ = false;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ const view_query& vq (c.get<view_query> ("query"));
+
+ // Make sure we don't have any containers or object pointers.
+ //
+ view_members_.traverse (c);
+
+ names (c, data_member_names_);
+
+ // Allow certain kinds of empty views.
+ //
+ if (vq.kind != view_query::runtime &&
+ vq.kind != view_query::complete_execute)
+ {
+ // Allow all the members to be deleted.
+ //
+ column_count_type const& cc (column_count (c));
+
+ if (cc.total == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ names (c, data_member_names_);
+ }
+
+ public:
+ bool& valid_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member2 data_member_;
+ traversal::names data_member_names_;
+
+ object_no_id_members object_no_id_members_;
+ composite_id_members composite_id_members_;
+ view_members view_members_;
+ };
+ }
+
+ void
+ validate (options const&,
+ features&,
+ semantics::unit& u,
+ semantics::path const&,
+ unsigned short pass)
+ {
+ bool valid (true);
+
+ // Validate custom type mapping.
+ //
+ if (pass == 1)
+ {
+ // Create an empty list if we don't have one. This makes the
+ // rest of the code simpler.
+ //
+ if (!u.count ("custom-db-types"))
+ u.set ("custom-db-types", custom_db_types ());
+
+ custom_db_types & cts (u.get<custom_db_types> ("custom-db-types"));
+
+ for (custom_db_types::iterator i (cts.begin ()); i != cts.end (); ++i)
+ {
+ custom_db_type& ct (*i);
+
+ if (ct.type.empty ())
+ {
+ error (ct.loc) << "'type' clause expected in db pragma map" << endl;
+ valid = false;
+ }
+
+ if (ct.as.empty ())
+ {
+ error (ct.loc) << "'as' clause expected in db pragma map" << endl;
+ valid = false;
+ }
+
+ if (ct.to.empty ())
+ ct.to = "(?)";
+ else
+ {
+ size_t p (ct.to.find ("(?)"));
+
+ if (p == string::npos)
+ {
+ error (ct.loc) << "no '(?)' expression in the 'to' clause "
+ << "of db pragma map" << endl;
+ valid = false;
+ }
+ else if (ct.to.find ("(?)", p + 3) != string::npos)
+ {
+ error (ct.loc) << "multiple '(?)' expressions in the 'to' "
+ << "clause of db pragma map" << endl;
+ valid = false;
+ }
+ }
+
+ if (ct.from.empty ())
+ ct.from = "(?)";
+ else
+ {
+ size_t p (ct.from.find ("(?)"));
+
+ if (p == string::npos)
+ {
+ error (ct.loc) << "no '(?)' expression in the 'from' clause "
+ << "of db pragma map" << endl;
+ valid = false;
+ }
+ else if (ct.from.find ("(?)", p + 3) != string::npos)
+ {
+ error (ct.loc) << "multiple '(?)' expressions in the 'from' "
+ << "clause of db pragma map" << endl;
+ valid = false;
+ }
+ }
+ }
+ }
+
+ if (!valid)
+ throw operation_failed ();
+
+ if (pass == 1)
+ {
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+
+ if (!valid)
+ throw operation_failed ();
+ }
+}
diff --git a/odb/odb/relational/validator.hxx b/odb/odb/relational/validator.hxx
new file mode 100644
index 0000000..d6602f7
--- /dev/null
+++ b/odb/odb/relational/validator.hxx
@@ -0,0 +1,24 @@
+// file : odb/relational/validator.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_VALIDATOR_HXX
+#define ODB_RELATIONAL_VALIDATOR_HXX
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+namespace relational
+{
+ // The first pass is performed before processing. The second -- after.
+ // Throws operation_failed to signal a failure.
+ //
+ void
+ validate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&,
+ unsigned short pass);
+}
+
+#endif // ODB_RELATIONAL_VALIDATOR_HXX
diff --git a/odb/odb/semantics.hxx b/odb/odb/semantics.hxx
new file mode 100644
index 0000000..83416a6
--- /dev/null
+++ b/odb/odb/semantics.hxx
@@ -0,0 +1,19 @@
+// file : odb/semantics.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_HXX
+#define ODB_SEMANTICS_HXX
+
+#include <odb/semantics/class.hxx>
+#include <odb/semantics/class-template.hxx>
+#include <odb/semantics/derived.hxx>
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/enum.hxx>
+#include <odb/semantics/fundamental.hxx>
+#include <odb/semantics/namespace.hxx>
+#include <odb/semantics/template.hxx>
+#include <odb/semantics/union.hxx>
+#include <odb/semantics/union-template.hxx>
+#include <odb/semantics/unit.hxx>
+
+#endif // ODB_SEMANTICS_HXX
diff --git a/odb/odb/semantics/class-template.cxx b/odb/odb/semantics/class-template.cxx
new file mode 100644
index 0000000..f8bbca4
--- /dev/null
+++ b/odb/odb/semantics/class-template.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/class-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/class-template.hxx>
+
+namespace semantics
+{
+ class_template::
+ class_template (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ class_instantiation::
+ class_instantiation (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // class_template
+ //
+ {
+ type_info ti (typeid (class_template));
+ ti.add_base (typeid (type_template));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+
+ // class_instantiation
+ //
+ {
+ type_info ti (typeid (class_instantiation));
+ ti.add_base (typeid (class_));
+ ti.add_base (typeid (type_instantiation));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/class-template.hxx b/odb/odb/semantics/class-template.hxx
new file mode 100644
index 0000000..bffb3f2
--- /dev/null
+++ b/odb/odb/semantics/class-template.hxx
@@ -0,0 +1,68 @@
+// file : odb/semantics/class-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_CLASS_TEMPLATE_HXX
+#define ODB_SEMANTICS_CLASS_TEMPLATE_HXX
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/class.hxx>
+#include <odb/semantics/template.hxx>
+
+namespace semantics
+{
+ class class_template: public type_template, public scope
+ {
+ private:
+ typedef std::vector<inherits*> inherits_list;
+
+ public:
+ typedef pointer_iterator<inherits_list::const_iterator> inherits_iterator;
+
+ inherits_iterator
+ inherits_begin () const
+ {
+ return inherits_.begin ();
+ }
+
+ inherits_iterator
+ inherits_end () const
+ {
+ return inherits_.end ();
+ }
+
+ public:
+ class_template (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (inherits& e)
+ {
+ inherits_.push_back (&e);
+ }
+
+ void
+ add_edge_right (inherits&)
+ {
+ }
+
+ using scope::add_edge_left;
+ using scope::add_edge_right;
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ private:
+ inherits_list inherits_;
+ };
+
+ class class_instantiation: public class_, public type_instantiation
+ {
+ public:
+ class_instantiation (path const&, size_t line, size_t column, tree);
+
+ using class_::add_edge_left;
+ using type_instantiation::add_edge_left;
+ };
+}
+
+#endif // ODB_SEMANTICS_CLASS_TEMPLATE_HXX
diff --git a/odb/odb/semantics/class.cxx b/odb/odb/semantics/class.cxx
new file mode 100644
index 0000000..97cf088
--- /dev/null
+++ b/odb/odb/semantics/class.cxx
@@ -0,0 +1,175 @@
+// file : odb/semantics/class.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // TYPE_HAS_DEFAULT_CONSTRUCTOR
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/class.hxx>
+
+namespace semantics
+{
+ inherits::
+ inherits (access_type access, bool virt)
+ : virt_ (virt), access_ (access)
+ {
+ }
+
+ class_::
+ class_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ bool class_::
+ default_ctor () const
+ {
+ tree t (tree_node ());
+
+ // TYPE_HAS_DEFAULT_CONSTRUCTOR() returns true if we have a deleted
+ // default ctor. locate_ctor(), on the other hand, returns NULL_TREE in
+ // this case.
+ //
+ if (TYPE_HAS_DEFAULT_CONSTRUCTOR (t))
+ {
+#if BUILDING_GCC_MAJOR >= 8
+
+ // Work around GCC bug 86441. Essentially, we should not trigger an
+ // instantiation or completion of the default ctor. As a result, we will
+ // assume that if we have a lazy default ctor, it is not implicitly
+ // deleted.
+ //
+ if (CLASSTYPE_LAZY_DEFAULT_CTOR (t))
+ return true;
+
+ for (ovl_iterator i (CLASSTYPE_CONSTRUCTORS (t)); i; ++i)
+ {
+ tree f (*i);
+
+ if (TREE_CODE (f) == FUNCTION_DECL && DECL_DELETED_FN (f))
+ continue;
+
+ if (default_ctor_p (f))
+ return true;
+ }
+#else
+ return locate_ctor (t) != NULL_TREE;
+#endif
+
+ }
+
+ return false;
+ }
+
+ bool class_::
+ complete () const
+ {
+ return COMPLETE_TYPE_P (tree_node ());
+ }
+
+ bool class_::
+ abstract () const
+ {
+ return CLASSTYPE_PURE_VIRTUALS (tree_node ());
+ }
+
+ names* class_::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* ph) const
+ {
+ bool h (false);
+ bool& rh (ph != 0 ? *ph : h);
+
+ names* r (scope::lookup (name, ti, flags | exclude_outer, &rh));
+
+ if (r != 0)
+ return r;
+
+ // If we found a name but the types didn't match, then bail out
+ // unless we want hidden names.
+ //
+ if (rh && (flags & include_hidden) == 0)
+ return 0;
+
+ // Look in the base classes unless requested not to. For the name
+ // lookup purposes, bases can be viewed as a parallel set of outer
+ // scopes that are searched after the class scope and before any
+ // real outer scope. Interestingly, outer scopes of bases are not
+ // considered during this lookup, only their bases.
+ //
+ if ((flags & exclude_base) == 0)
+ {
+ // Being hidden in one base doesn't mean it is also hidden in the
+ // other. Normally that would be an ambiguous lookup, but we use
+ // relaxed rules.
+ //
+ bool any_h (false); // Indicates whether any base hides the name.
+
+ for (inherits_iterator i (inherits_begin ()); i != inherits_end (); ++i)
+ {
+ bool h (false); // Indicates whether this base hides the name.
+ names* br (i->base ().lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (br != 0)
+ {
+ if (r != 0)
+ throw ambiguous (*r, *br);
+
+ r = br;
+
+ if (h)
+ rh = true;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ if (any_h)
+ {
+ rh = true;
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+ }
+
+ // Look in the outer scope unless requested not to.
+ //
+ if ((flags & exclude_outer) == 0)
+ return scope ().lookup (name, ti, flags, &rh);
+
+ return 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // inherits
+ //
+ {
+ type_info ti (typeid (inherits));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // class_
+ //
+ {
+ type_info ti (typeid (class_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/class.hxx b/odb/odb/semantics/class.hxx
new file mode 100644
index 0000000..e02337a
--- /dev/null
+++ b/odb/odb/semantics/class.hxx
@@ -0,0 +1,142 @@
+// file : odb/semantics/class.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_CLASS_HXX
+#define ODB_SEMANTICS_CLASS_HXX
+
+#include <vector>
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ class class_;
+
+ class inherits: public edge
+ {
+ public:
+ typedef semantics::access access_type;
+
+ class_&
+ base () const
+ {
+ return *base_;
+ }
+
+ class_&
+ derived () const
+ {
+ return *derived_;
+ }
+
+ bool
+ virtual_ () const
+ {
+ return virt_;
+ }
+
+ access_type
+ access () const
+ {
+ return access_;
+ }
+
+ public:
+ inherits (access_type, bool virt);
+
+ void
+ set_left_node (class_& n)
+ {
+ derived_ = &n;
+ }
+
+ void
+ set_right_node (class_& n)
+ {
+ base_ = &n;
+ }
+
+ protected:
+ bool virt_;
+ access_type access_;
+
+ class_* base_;
+ class_* derived_;
+ };
+
+ //
+ //
+ class class_: public virtual type, public scope
+ {
+ private:
+ typedef std::vector<inherits*> inherits_list;
+
+ public:
+ typedef pointer_iterator<inherits_list::const_iterator> inherits_iterator;
+
+ inherits_iterator
+ inherits_begin () const
+ {
+ return inherits_.begin ();
+ }
+
+ inherits_iterator
+ inherits_end () const
+ {
+ return inherits_.end ();
+ }
+
+ public:
+ bool
+ default_ctor () const;
+
+ bool
+ complete () const;
+
+ bool
+ abstract () const;
+
+ public:
+ // When doing lookup in class scope, take into account bases.
+ //
+ static unsigned int const exclude_base = 0x04; // Exclude base classes.
+
+ virtual names*
+ lookup (string const& name,
+ type_id const&,
+ unsigned int flags = 0,
+ bool* hidden = 0) const;
+
+ using scope::lookup;
+
+ public:
+ class_ (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (inherits& e)
+ {
+ inherits_.push_back (&e);
+ }
+
+ void
+ add_edge_right (inherits&)
+ {
+ }
+
+ using scope::add_edge_left;
+ using type::add_edge_right;
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ protected:
+ class_ ()
+ {
+ }
+
+ private:
+ inherits_list inherits_;
+ };
+}
+
+#endif // ODB_SEMANTICS_CLASS_HXX
diff --git a/odb/odb/semantics/derived.cxx b/odb/odb/semantics/derived.cxx
new file mode 100644
index 0000000..771ad21
--- /dev/null
+++ b/odb/odb/semantics/derived.cxx
@@ -0,0 +1,254 @@
+// file : odb/semantics/derived.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/derived.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ qualifies::
+ qualifies ()
+ : hint_ (0)
+ {
+ }
+
+ qualifier::
+ qualifier (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ bool c,
+ bool v,
+ bool r)
+ : node (file, line, column, tn), c_ (c), v_ (v), r_ (r)
+ {
+ }
+
+ string qualifier::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string'. Instead it prints 'const string'.
+ //
+ type& bt (base_type ());
+
+ // Use the trailing qualifier syntax so that we don't get bogged down
+ // in stuff like 'const const foo*'. We also have to handle arrays in
+ // a special way since char[16] const is not a legal syntax.
+ //
+ string q;
+ if (c_)
+ q += " const";
+
+ if (v_)
+ q += " volatile";
+
+ if (r_)
+ q += " __restrict";
+
+ hint = qualifies ().hint ();
+
+ if (array* a = dynamic_cast<array*> (&bt))
+ return a->fq_name (hint, q);
+ else
+ return bt.fq_name (hint) + q;
+ }
+
+ points::
+ points ()
+ : hint_ (0)
+ {
+ }
+
+ pointer::
+ pointer (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ string pointer::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string*'. Instead it prints 'const string*'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '*';
+ return r;
+ }
+
+ references::
+ references ()
+ : hint_ (0)
+ {
+ }
+
+ reference::
+ reference (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ string reference::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string&'. Instead it prints 'const string&'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '&';
+ return r;
+ }
+
+ contains::
+ contains ()
+ : hint_ (0)
+ {
+ }
+
+ array::
+ array (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ unsigned long long size)
+ : node (file, line, column, tn), size_ (size)
+ {
+ }
+
+ string array::
+ fq_name (names* hint) const
+ {
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string[123]'. Instead it prints 'const string[123]'.
+ //
+ string t;
+ return fq_name (hint, t);
+ }
+
+ string array::
+ fq_name (names* hint, string& t) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint) + t;
+
+ t += '[';
+ ostringstream ostr;
+ ostr << size ();
+ t += ostr.str ();
+
+ if (size () > 0xFFFFFFFF)
+ t += "ULL";
+ else if (size () > 2147483647)
+ t += "U";
+
+ t += ']';
+
+ type& bt (base_type ());
+ hint = contains ().hint ();
+ array* a;
+
+ if (hint != 0 || (a = dynamic_cast<array*> (&bt)) == 0)
+ return bt.fq_name (hint) + t;
+ else
+ return a->fq_name (0, t);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // derived_type
+ //
+ {
+ type_info ti (typeid (derived_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+
+ // qualifies
+ //
+ {
+ type_info ti (typeid (qualifies));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // qualifier
+ //
+ {
+ type_info ti (typeid (qualifier));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // points
+ //
+ {
+ type_info ti (typeid (points));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // pointer
+ //
+ {
+ type_info ti (typeid (pointer));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // references
+ //
+ {
+ type_info ti (typeid (references));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // reference
+ //
+ {
+ type_info ti (typeid (reference));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // contains
+ //
+ {
+ type_info ti (typeid (contains));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // array
+ //
+ {
+ type_info ti (typeid (array));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/derived.hxx b/odb/odb/semantics/derived.hxx
new file mode 100644
index 0000000..e58ec9f
--- /dev/null
+++ b/odb/odb/semantics/derived.hxx
@@ -0,0 +1,440 @@
+// file : odb/semantics/derived.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_DERIVED_HXX
+#define ODB_SEMANTICS_DERIVED_HXX
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ // Derived types (cvr-qualifiers, pointer, reference, and array).
+ //
+
+ class derived_type: public type
+ {
+ public:
+ virtual type&
+ base_type () const = 0;
+ };
+
+ //
+ //
+ class qualifier;
+
+ class qualifies: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::qualifier qualifier_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ qualifier_type&
+ qualifier () const
+ {
+ return *qualifier_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ qualifies ();
+
+ void
+ set_left_node (qualifier_type& n)
+ {
+ qualifier_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ qualifier_type* qualifier_;
+ names* hint_;
+ };
+
+ class qualifier: public derived_type
+ {
+ public:
+ typedef semantics::qualifies qualifies_type;
+
+ bool
+ const_ () const
+ {
+ return c_;
+ }
+
+ bool
+ volatile_ () const
+ {
+ return v_;
+ }
+
+ bool
+ restrict_ () const
+ {
+ return r_;
+ }
+
+ virtual type&
+ base_type () const
+ {
+ return qualifies_->type ();
+ }
+
+ qualifies_type&
+ qualifies () const
+ {
+ return *qualifies_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ qualifier (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ bool c,
+ bool v,
+ bool r);
+
+ void
+ add_edge_left (qualifies_type& e)
+ {
+ qualifies_ = &e;
+ }
+
+ private:
+ bool c_, v_, r_;
+ qualifies_type* qualifies_;
+ };
+
+ //
+ //
+ class pointer;
+
+ class points: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::pointer pointer_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ pointer_type&
+ pointer () const
+ {
+ return *pointer_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ points ();
+
+ void
+ set_left_node (pointer_type& n)
+ {
+ pointer_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ pointer_type* pointer_;
+ names* hint_;
+ };
+
+ class pointer: public derived_type
+ {
+ public:
+ typedef semantics::points points_type;
+
+ virtual type&
+ base_type () const
+ {
+ return points_->type ();
+ }
+
+ points_type&
+ points () const
+ {
+ return *points_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ pointer (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (points_type& e)
+ {
+ points_ = &e;
+ }
+
+ private:
+ points_type* points_;
+ };
+
+
+ //
+ //
+ class reference;
+
+ class references: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::reference reference_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ reference_type&
+ reference () const
+ {
+ return *reference_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ references ();
+
+ void
+ set_left_node (reference_type& n)
+ {
+ reference_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ reference_type* reference_;
+ names* hint_;
+ };
+
+ class reference: public derived_type
+ {
+ public:
+ typedef semantics::references references_type;
+
+ virtual type&
+ base_type () const
+ {
+ return references_->type ();
+ }
+
+ references_type&
+ references () const
+ {
+ return *references_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ reference (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (references_type& e)
+ {
+ references_ = &e;
+ }
+
+ private:
+ references_type* references_;
+ };
+
+
+ //
+ //
+ class array;
+
+ class contains: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::array array_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ array_type&
+ array () const
+ {
+ return *array_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ contains ();
+
+ void
+ set_left_node (array_type& n)
+ {
+ array_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ array_type* array_;
+ names* hint_;
+ };
+
+ class array: public derived_type
+ {
+ public:
+ typedef semantics::contains contains_type;
+
+ // Return the number of elements in the array or 0 if it is not
+ // specified (e.g., int[]).
+ //
+ unsigned long long
+ size () const
+ {
+ return size_;
+ }
+
+ virtual type&
+ base_type () const
+ {
+ return contains_->type ();
+ }
+
+ contains_type&
+ contains () const
+ {
+ return *contains_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ private:
+ friend class qualifier;
+
+ string
+ fq_name (names*, string& trailer) const;
+
+ using derived_type::fq_name; // Unhide.
+
+ public:
+ array (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ unsigned long long size);
+
+ void
+ add_edge_left (contains_type& e)
+ {
+ contains_ = &e;
+ }
+
+ private:
+ contains_type* contains_;
+ unsigned long long size_;
+ };
+}
+
+#endif // ODB_SEMANTICS_DERIVED_HXX
diff --git a/odb/odb/semantics/elements.cxx b/odb/odb/semantics/elements.cxx
new file mode 100644
index 0000000..b5793d0
--- /dev/null
+++ b/odb/odb/semantics/elements.cxx
@@ -0,0 +1,619 @@
+// file : odb/semantics/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/namespace.hxx>
+#include <odb/semantics/unit.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ // access
+ //
+ static char const* access_str[] = {"public", "protected", "private"};
+
+ char const* access::
+ string () const
+ {
+ return access_str[value_];
+ }
+
+ //
+ //
+ node::
+ node (path const& file, size_t line, size_t column, tree tn)
+ : tree_node_ (tn), loc_ (file, line, column)
+ {
+ }
+
+ node::
+ node ()
+ : loc_ (0)
+ {
+ // GCC plugin machinery #define's abort as a macro.
+ //
+ // std::abort ();
+ abort ();
+ }
+
+ // nameable
+ //
+
+ bool nameable::
+ in_scope (scope_type& s)
+ {
+ for (scope_type* p (&scope ());; p = &p->scope_ ())
+ {
+ //@@ Need to handle namespace extensions.
+ //
+ if (p == &s)
+ return true;
+
+ if (!p->named_p () || p->global_scope ())
+ break;
+ }
+
+ return false;
+ }
+
+ bool nameable::
+ anonymous_ () const
+ {
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (n))
+ name = DECL_NAME (decl);
+
+ return name != 0 && IDENTIFIER_ANON_P (name);
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous_ (scope_entry const* prev) const
+ {
+ scope_entry scope (this, prev);
+
+ // Nameable is fq-anonymous if all the paths to the global scope
+ // have at least one anonymous link.
+ //
+ if (defined_ != 0 || !named_.empty ())
+ {
+ if (named ().global_scope ())
+ return false;
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+ }
+
+ // If we can get a literal name for this type node, then it is not
+ // anonymous as long as its scope is not anonymous.
+ //
+ tree type (tree_node ());
+
+ if (TYPE_P (type))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (type))
+ {
+ name = DECL_NAME (decl);
+ if (name != 0 && IDENTIFIER_ANON_P (name))
+ return true;
+
+ tree s (CP_DECL_CONTEXT (decl));
+
+ gcc_tree_code_type tc (TREE_CODE (s));
+
+ if (tc == TYPE_DECL)
+ s = TREE_TYPE (s);
+ else if (tc == NAMESPACE_DECL)
+ {
+ // "Unwind" any inline namespaces since they are not in
+ // semantic grapth.
+ //
+ while (s != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (s));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, s, true))
+#else
+ if (!is_associated_namespace (prev, s))
+#endif
+ break;
+
+ s = prev;
+ }
+ }
+
+ if (nameable* n = dynamic_cast<nameable*> (unit ().find (s)))
+ return scope.find (n) || n->fq_anonymous_ (&scope);
+ }
+ else
+ return false; // Assume this is a derived type (e.g., pointer).
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return false;
+
+ return n.scope ().fq_anonymous ();
+ }
+ else
+ return fq_anonymous ();
+ }
+
+ static string
+ qualify_names (string const& n, bool qualify_first)
+ {
+ // @@ Creating a lexer for each call is a bad idea. Need
+ // to cache it somewhere.
+ //
+ cxx_string_lexer l;
+ l.start (n);
+
+ string r, t;
+ bool punc (false);
+ bool scoped (false);
+
+ // Names returned by GCC's type_as_string() (on which this function
+ // is called) include inline namespaces (e.g., std::__cxx11::string).
+ // So, besides fully-qualifying names, this function also needs to get
+ // rid of those. The idea is to resolve names as we lex them, skipping
+ // inline namespaces and stopping once we reach something other than a
+ // namespace.
+ //
+ tree ns (global_namespace);
+ tree id;
+
+ for (cpp_ttype tt = l.next (t, &id); tt != CPP_EOF; tt = l.next (t, &id))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ r += ' ';
+
+ punc = false;
+ tree new_ns (global_namespace); // By default, revert to global.
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ r += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ r += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Check if this is a namespace and, if so, whether it is
+ // inline.
+ //
+ if (ns != 0)
+ {
+ new_ns = lookup_qualified_name (ns, id, false, false);
+
+ if (new_ns == error_mark_node ||
+ TREE_CODE (new_ns) != NAMESPACE_DECL)
+ new_ns = 0; // Not a namespace, stop resolving.
+ else
+ {
+ // Check if this is an inline namespace and skip it if so.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ if (is_nested_namespace (ns, new_ns, true))
+#else
+ if (is_associated_namespace (ns, new_ns))
+#endif
+ {
+ // Skip also the following scope operator. Strictly speaking
+ // there could be none (i.e., this is a name of an inline
+ // namespace) but we only use this function to print names
+ // of anonymous types.
+ //
+ assert (l.next (t) == CPP_SCOPE);
+ continue;
+ }
+ }
+ }
+ else
+ new_ns = 0; // Keep it disabled until we hit a new name.
+
+ // If the name was not preceeded with '::', qualify it.
+ //
+ if (!scoped)
+ {
+ if (!qualify_first)
+ qualify_first = true;
+ else
+ r += "::";
+ }
+
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ new_ns = ns; // Don't change the namespace.
+ }
+ // Fall through.
+ default:
+ {
+ r += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ ns = new_ns;
+ }
+
+ return r;
+ }
+
+ string nameable::
+ name_ () const
+ {
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ return qualify_names (
+ type_as_string (n, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME), false);
+ }
+
+ string nameable::
+ fq_name () const
+ {
+ return fq_name_ (0);
+ }
+
+ string nameable::
+ fq_name_ (scope_entry const* prev) const
+ {
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ scope_entry scope (this, prev);
+
+ if (named_p () && named ().global_scope ())
+ return "";
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+ }
+
+ string nameable::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return "";
+
+ return n.scope ().fq_name () + "::" + n.name ();
+ }
+ else
+ {
+ // Since there was no hint, prefer the literal name over the names
+ // edges.
+ //
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+
+ // Last resort is to call the other version of fq_name which will
+ // check the names edges.
+ //
+ return fq_name ();
+ }
+ }
+
+ // scope
+ //
+
+ scope::names_iterator_pair scope::
+ find (string const& name) const
+ {
+ names_map::const_iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_iterator_pair (names_.end (), names_.end ());
+ else
+ return names_iterator_pair (i->second.begin (), i->second.end ());
+ }
+
+ scope::names_iterator scope::
+ find (names& e)
+ {
+ list_iterator_map::iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ static bool
+ is_base (type_id const& b, compiler::type_info const& d)
+ {
+ using compiler::type_info;
+
+ for (type_info::base_iterator i (d.begin_base ());
+ i != d.end_base (); ++i)
+ {
+ type_info const& ti (i->type_info ());
+
+ if (b == ti.type_id () || is_base (b, ti))
+ return true;
+ }
+
+ return false;
+ }
+
+ names* scope::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* hidden) const
+ {
+ names_iterator_pair p (find (name));
+ names* r (0);
+
+ for (names_const_iterator i (p.first); i != p.second; ++i)
+ {
+ type_id const& xti (typeid (i->named ()));
+
+ // If types are equal, then we found a match. Also check if ti is
+ // a base type of xti.
+ //
+ if (xti == ti || is_base (ti, compiler::lookup (xti)))
+ {
+ if (r != 0)
+ {
+ // If both are namespaces, then the one is just an extension
+ // of the other.
+ //
+ if (!(r->named ().is_a<namespace_> () &&
+ i->named ().is_a<namespace_> ()))
+ throw ambiguous (*r, *i);
+ }
+ else
+ r = &*i;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ // If we found a name but the types didn't match, then bail out
+ // unless we want hidden names.
+ //
+ if (p.first != p.second)
+ {
+ if (hidden != 0)
+ *hidden = true;
+
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+
+ // Look in the outer scope unless requested not to or if this is
+ // the global scope.
+ //
+ if ((flags & exclude_outer) == 0 && named_p () && !global_scope ())
+ return scope ().lookup (name, ti, flags, hidden);
+
+ return 0;
+ }
+
+ void scope::
+ add_edge_left (names& e)
+ {
+ names_list::iterator i (names_.insert (names_.end (), &e));
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ void scope::
+ add_edge_left (names& e, names_iterator after)
+ {
+ names_list::iterator i;
+
+ if (after.base () == names_.end ())
+ i = names_.insert (names_.begin (), &e);
+ else
+ {
+ names_list::iterator j (after.base ());
+ i = names_.insert (++j, &e);
+ }
+
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // names
+ //
+ {
+ type_info ti (typeid (names));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // declares
+ //
+ {
+ type_info ti (typeid (declares));
+ ti.add_base (typeid (names));
+ insert (ti);
+ }
+
+ // defines
+ //
+ {
+ type_info ti (typeid (defines));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // typedefs
+ //
+ {
+ type_info ti (typeid (typedefs));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (nameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (scope));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // type
+ //
+ {
+ type_info ti (typeid (type));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // belongs
+ //
+ {
+ type_info ti (typeid (belongs));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // instance
+ //
+ {
+ type_info ti (typeid (instance));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // data_member
+ //
+ {
+ type_info ti (typeid (data_member));
+ ti.add_base (typeid (nameable));
+ ti.add_base (typeid (instance));
+ insert (ti);
+ }
+
+ // unsupported_type
+ //
+ {
+ type_info ti (typeid (unsupported_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/elements.hxx b/odb/odb/semantics/elements.hxx
new file mode 100644
index 0000000..699a1be
--- /dev/null
+++ b/odb/odb/semantics/elements.hxx
@@ -0,0 +1,841 @@
+// file : odb/semantics/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_ELEMENTS_HXX
+#define ODB_SEMANTICS_ELEMENTS_HXX
+
+#include <map>
+#include <list>
+#include <vector>
+#include <string>
+#include <cstdlib> // std::abort
+#include <cstddef> // std::size_t
+#include <utility> // std::pair
+#include <cassert>
+
+#include <libcutl/fs/path.hxx>
+#include <libcutl/container/graph.hxx>
+#include <libcutl/container/pointer-iterator.hxx>
+#include <libcutl/compiler/type-id.hxx>
+#include <libcutl/compiler/context.hxx>
+
+#include <odb/gcc-fwd.hxx>
+#include <odb/location.hxx>
+
+namespace semantics
+{
+ using namespace cutl;
+
+ using std::size_t;
+ using std::string;
+
+ using container::graph;
+ using container::pointer_iterator;
+
+ using compiler::type_id;
+ using compiler::context;
+
+ //
+ //
+ using fs::path;
+ using fs::invalid_path;
+
+ //
+ //
+ class access
+ {
+ public:
+ enum value { public_, protected_, private_ };
+
+ access (value v)
+ : value_ (v)
+ {
+ }
+
+ operator value () const
+ {
+ return value_;
+ }
+
+ char const* string () const;
+
+ private:
+ value value_;
+ };
+
+ //
+ //
+ class node;
+ class edge;
+ class unit;
+
+ // Support for inserting edges at specified positions.
+ //
+ template <typename N, typename I>
+ struct node_position
+ {
+ node_position (N& node, I pos)
+ : node_ (node), pos_ (pos)
+ {
+ }
+
+ operator N& () const
+ {
+ return node_;
+ }
+
+ template <typename E>
+ void
+ add_edge_left (E& e)
+ {
+ node_.add_edge_left (e, pos_);
+ }
+
+ template <typename E>
+ void
+ add_edge_right (E& e)
+ {
+ node_.add_edge_right (e, pos_);
+ }
+
+ private:
+ N& node_;
+ I pos_;
+ };
+
+ //
+ //
+ class edge: public context
+ {
+ public:
+ virtual
+ ~edge () {}
+
+ public:
+ template <typename X>
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
+ };
+
+ //
+ //
+ class node: public context
+ {
+ public:
+ virtual
+ ~node () {}
+
+ public:
+ tree
+ tree_node () const
+ {
+ return tree_node_;
+ }
+
+ public:
+ typedef ::location location_type;
+
+ path const&
+ file () const
+ {
+ return loc_.file;
+ }
+
+ size_t
+ line () const
+ {
+ return loc_.line;
+ }
+
+ size_t
+ column () const
+ {
+ return loc_.column;
+ }
+
+ location_type const&
+ location () const
+ {
+ return loc_;
+ }
+
+ public:
+ template <typename X>
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
+
+ public:
+ node (path const& file, size_t line, size_t column, tree);
+
+ // Sink functions that allow extensions in the form of one-way
+ // edges.
+ //
+ void
+ add_edge_left (edge&) {}
+
+ void
+ add_edge_right (edge&) {}
+
+ protected:
+ // For virtual inheritance. Should never be actually called.
+ //
+ node ();
+
+ protected:
+ typedef semantics::unit unit_type;
+
+ unit_type const&
+ unit () const
+ {
+ return *unit_;
+ }
+
+ unit_type&
+ unit ()
+ {
+ return *unit_;
+ }
+
+ private:
+ friend class semantics::unit;
+
+ void
+ unit (unit_type& u)
+ {
+ unit_ = &u;
+ }
+
+ private:
+ tree tree_node_;
+ unit_type* unit_;
+
+ location_type loc_;
+ };
+
+ //
+ //
+ class scope;
+ class nameable;
+
+
+ //
+ //
+ class names: public edge
+ {
+ public:
+ typedef semantics::scope scope_type;
+ typedef semantics::access access_type;
+
+ string const&
+ name () const
+ {
+ return name_;
+ }
+
+ scope_type&
+ scope () const
+ {
+ return *scope_;
+ }
+
+ // Return true if the entity that this edge names is a global scope.
+ // In this case calling scope() is undefined behavior.
+ //
+ bool
+ global_scope () const
+ {
+ return scope_ == 0;
+ }
+
+ nameable&
+ named () const
+ {
+ return *named_;
+ }
+
+ access_type
+ access () const
+ {
+ return access_;
+ }
+
+ // Names edge in terms of which this edge was defined. Can be NULL.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ names (string const& name, access_type access = access_type::public_)
+ : name_ (name), access_ (access), hint_ (0)
+ {
+ }
+
+ void
+ set_left_node (scope_type& n)
+ {
+ scope_ = &n;
+ }
+
+ void
+ set_right_node (nameable& n)
+ {
+ named_ = &n;
+ }
+
+ protected:
+ scope_type* scope_;
+ nameable* named_;
+ string name_;
+ access_type access_;
+ names* hint_;
+ };
+
+ //
+ // Declarations and definitions.
+ //
+
+ class declares: public names
+ {
+ public:
+ declares (string const& name, access_type access = access_type::public_)
+ : names (name, access)
+ {
+ }
+ };
+
+ class defines: public declares
+ {
+ public:
+ defines (string const& name, access_type access = access_type::public_)
+ : declares (name, access)
+ {
+ }
+ };
+
+ class type;
+ class typedefs: public declares
+ {
+ public:
+ typedef semantics::type type_type;
+
+ type_type&
+ type () const;
+
+ public:
+ typedefs (string const& name, access_type access = access_type::public_)
+ : declares (name, access)
+ {
+ }
+ };
+
+ //
+ //
+ class nameable: public virtual node
+ {
+ typedef std::vector<names*> names_list;
+
+ public:
+ typedef semantics::scope scope_type;
+
+ // Return true if this type is unnamed and no literal name, such as
+ // template-id or derived type declarator, can be used instead.
+ //
+ bool
+ anonymous () const
+ {
+ if (defined_ != 0 || !named_.empty ())
+ return false;
+
+ return anonymous_ ();
+ }
+
+ // Return true if the node itself or any of the scopes up to the
+ // global scope is anonymous. For a named class nested in an unnamed
+ // class, anonymous() will return false and fq_anonymous() will
+ // return true.
+ //
+ bool
+ fq_anonymous () const
+ {
+ return fq_anonymous_ (0);
+ }
+
+ // As above but use the hint to select the first outer scope. If
+ // hint is 0, use the defines edge.
+ //
+ bool
+ fq_anonymous (names* hint) const;
+
+ // Return the node's unqualifed name. If the node has a name, then
+ // return it, preferring the defines edge. Otherwise, return a
+ // literal name, e.g., template-id or a derived type declarator.
+ // Finally, if the type is anonymous, return <anonymous> string.
+ //
+ string
+ name () const
+ {
+ if (defined_ != 0)
+ return defined_->name ();
+
+ if (!named_.empty ())
+ return named_[0]->name ();
+
+ return name_ ();
+ }
+
+ // Return the node's fully-qualifed name.
+ //
+ virtual string
+ fq_name () const;
+
+ // As above but use the hint to select the first outer scope. If hint
+ // is 0, use the defines edge.
+ //
+ virtual string
+ fq_name (names* hint) const;
+
+ // Return true if the type is named.
+ //
+ bool
+ named_p () const
+ {
+ return defined_ != 0 || !named_.empty ();
+ }
+
+ scope_type&
+ scope () const
+ {
+ return named ().scope ();
+ }
+
+ names&
+ named () const
+ {
+ return defined_ != 0 ? *defined_ : *named_[0];
+ }
+
+ bool
+ in_scope (scope_type&);
+
+ public:
+ nameable ()
+ : defined_ (0)
+ {
+ }
+
+ void
+ add_edge_right (defines& e)
+ {
+ assert (defined_ == 0);
+ defined_ = &e;
+ }
+
+ void
+ add_edge_right (names& e)
+ {
+ named_.push_back (&e);
+ }
+
+ using node::add_edge_right;
+
+ protected:
+ // We need to keep the scope we have seen in the fq_* function
+ // family in order to detect names that are inside the node
+ // and which would otherwise lead to infinite recursion. Here
+ // is the canonical example:
+ //
+ // template <typename X>
+ // class c
+ // {
+ // typedef c this_type;
+ // };
+ //
+ struct scope_entry
+ {
+ scope_entry (nameable const* e, scope_entry const* p)
+ : entry_ (e), prev_ (p)
+ {
+ }
+
+ bool
+ find (nameable const* n) const
+ {
+ for (scope_entry const* i (this); i != 0; i = i->prev_)
+ if (i->entry_ == n)
+ return true;
+
+ return false;
+ }
+
+ private:
+ nameable const* entry_;
+ scope_entry const* prev_;
+ };
+
+ bool
+ anonymous_ () const;
+
+ bool
+ fq_anonymous_ (scope_entry const*) const;
+
+ string
+ name_ () const;
+
+ string
+ fq_name_ (scope_entry const*) const;
+
+ protected:
+ defines* defined_;
+ names_list named_;
+ };
+
+
+ // Ambiguous name lookup exception.
+ //
+ struct ambiguous
+ {
+ ambiguous (names& f, names& s): first (f), second (s) {}
+ names& first;
+ names& second;
+ };
+
+ // Unresolved name lookup exception.
+ //
+ struct unresolved
+ {
+ unresolved (string const& n, bool tm): name (n), type_mismatch (tm) {}
+ string name;
+ bool type_mismatch; // True if the name resolved but types didn't match.
+ };
+
+ //
+ //
+ class scope: public virtual nameable
+ {
+ protected:
+ typedef std::list<names*> names_list;
+ typedef std::map<names*, names_list::iterator> list_iterator_map;
+ typedef std::map<string, names_list> names_map;
+
+ public:
+ typedef pointer_iterator<names_list::iterator> names_iterator;
+ typedef pointer_iterator<names_list::const_iterator> names_const_iterator;
+
+ typedef
+ std::pair<names_const_iterator, names_const_iterator>
+ names_iterator_pair;
+
+ public:
+ bool
+ global_scope () const
+ {
+ return named ().global_scope ();
+ }
+
+ scope&
+ scope_ () const
+ {
+ return nameable::scope ();
+ }
+
+ names_iterator
+ names_begin ()
+ {
+ return names_.begin ();
+ }
+
+ names_iterator
+ names_end ()
+ {
+ return names_.end ();
+ }
+
+ names_const_iterator
+ names_begin () const
+ {
+ return names_.begin ();
+ }
+
+ names_const_iterator
+ names_end () const
+ {
+ return names_.end ();
+ }
+
+ // Find a name in this scope.
+ //
+ public:
+ virtual names_iterator_pair
+ find (string const& name) const;
+
+ names_iterator
+ find (names&);
+
+ // Lookup a name of the specified type in this scope and, if not
+ // found, in outer scopes.
+ //
+ public:
+ static unsigned int const exclude_outer = 0x01; // Exclude outer scopes.
+ static unsigned int const include_hidden = 0x02; // Include hidden names.
+
+ virtual names*
+ lookup (string const& name,
+ type_id const&,
+ unsigned int flags = 0,
+ bool* hidden = 0) const;
+
+ template <typename T>
+ T&
+ lookup (string const& name, unsigned int flags = 0) const
+ {
+ bool hidden (false);
+
+ if (names* n = lookup (name, typeid (T), flags, &hidden))
+ return dynamic_cast<T&> (n->named ());
+
+ throw unresolved (name, hidden);
+ }
+
+ public:
+ scope (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ void
+ add_edge_left (names&);
+
+ void
+ add_edge_left (names&, names_iterator after);
+
+ using nameable::add_edge_right;
+
+ protected:
+ scope ()
+ {
+ }
+
+ private:
+ names_list names_;
+ list_iterator_map iterator_map_;
+ names_map names_map_;
+ };
+
+ //
+ //
+ class points;
+
+ class belongs;
+ class qualifies;
+
+ class type: public virtual nameable
+ {
+ typedef std::vector<qualifies*> qualified;
+
+ public:
+ typedef pointer_iterator<qualified::const_iterator> qualified_iterator;
+
+ qualified_iterator
+ qualified_begin () const
+ {
+ return qualified_.begin ();
+ }
+
+ qualified_iterator
+ qualified_end () const
+ {
+ return qualified_.end ();
+ }
+
+ public:
+ bool
+ pointed_p () const {return pointed_ != 0;}
+
+ points&
+ pointed () const {return *pointed_;}
+
+ public:
+ type (): pointed_ (0) {}
+
+ void
+ add_edge_right (belongs&)
+ {
+ }
+
+ void
+ add_edge_right (qualifies& e)
+ {
+ qualified_.push_back (&e);
+ }
+
+ void
+ add_edge_right (points& e)
+ {
+ pointed_ = &e;
+ }
+
+ using nameable::add_edge_right;
+
+ private:
+ qualified qualified_;
+ points* pointed_;
+ };
+
+ //
+ //
+ class instance;
+
+ class belongs: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::instance instance_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ instance_type&
+ instance () const
+ {
+ return *instance_;
+ }
+
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ belongs ()
+ : hint_ (0)
+ {
+ }
+
+ void
+ set_left_node (instance_type& n)
+ {
+ instance_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ private:
+ type_type* type_;
+ instance_type* instance_;
+ names* hint_;
+ };
+
+ //
+ //
+ class instance: public virtual node
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::belongs belongs_type;
+
+ type_type&
+ type () const
+ {
+ return belongs_->type ();
+ }
+
+ belongs_type&
+ belongs () const
+ {
+ return *belongs_;
+ }
+
+ public:
+ void
+ add_edge_left (belongs_type& e)
+ {
+ belongs_ = &e;
+ }
+
+ protected:
+ instance ()
+ {
+ }
+
+ private:
+ belongs_type* belongs_;
+ };
+
+ // Data member for class and union types.
+ //
+ class data_member: public nameable, public instance
+ {
+ public:
+ data_member (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ protected:
+ data_member ()
+ {
+ }
+ };
+
+ // Unsupported type.
+ //
+ class unsupported_type: public type
+ {
+ public:
+ string const&
+ type_name () const
+ {
+ return type_name_;
+ }
+
+ public:
+ unsupported_type (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ string const& type_name)
+ : node (file, line, column, tn), type_name_ (type_name)
+ {
+ }
+
+ private:
+ string const type_name_;
+ };
+}
+
+#include <odb/semantics/elements.ixx>
+
+#endif // ODB_SEMANTICS_ELEMENTS_HXX
diff --git a/odb/odb/semantics/elements.ixx b/odb/odb/semantics/elements.ixx
new file mode 100644
index 0000000..32f9ced
--- /dev/null
+++ b/odb/odb/semantics/elements.ixx
@@ -0,0 +1,13 @@
+// file : odb/semantics/elements.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace semantics
+{
+ // typedefs
+ //
+ inline typedefs::type_type& typedefs::
+ type () const
+ {
+ return dynamic_cast<type_type&> (named ());
+ }
+}
diff --git a/odb/odb/semantics/enum.cxx b/odb/odb/semantics/enum.cxx
new file mode 100644
index 0000000..6432986
--- /dev/null
+++ b/odb/odb/semantics/enum.cxx
@@ -0,0 +1,85 @@
+// file : odb/semantics/enum.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/enum.hxx>
+
+namespace semantics
+{
+ enumerates::
+ enumerates ()
+ {
+ }
+
+ enumerator::
+ enumerator (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ unsigned long long value)
+ : node (file, line, column, tn), value_ (value)
+ {
+ }
+
+ underlies::
+ underlies ()
+ : type_ (0), enum__ (0), hint_ (0)
+ {
+ }
+
+ enum_::
+ enum_ (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // enumerates
+ //
+ {
+ type_info ti (typeid (enumerates));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // enumerator
+ //
+ {
+ type_info ti (typeid (enumerator));
+ ti.add_base (typeid (nameable));
+ ti.add_base (typeid (instance));
+ insert (ti);
+ }
+
+ // underlies
+ //
+ {
+ type_info ti (typeid (underlies));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // enum_
+ //
+ {
+ type_info ti (typeid (enum_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/enum.hxx b/odb/odb/semantics/enum.hxx
new file mode 100644
index 0000000..bfcce53
--- /dev/null
+++ b/odb/odb/semantics/enum.hxx
@@ -0,0 +1,228 @@
+// file : odb/semantics/enum.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_ENUM_HXX
+#define ODB_SEMANTICS_ENUM_HXX
+
+#include <vector>
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/fundamental.hxx>
+
+namespace semantics
+{
+ class enum_;
+ class enumerator;
+
+ class enumerates: public edge
+ {
+ public:
+ typedef semantics::enum_ enum_type;
+ typedef semantics::enumerator enumerator_type;
+
+ enum_type&
+ enum_ () const
+ {
+ return *enum__;
+ }
+
+ enumerator_type&
+ enumerator () const
+ {
+ return *enumerator_;
+ }
+
+ public:
+ enumerates ();
+
+ void
+ set_left_node (enum_type& n)
+ {
+ enum__ = &n;
+ }
+
+ void
+ set_right_node (enumerator_type& n)
+ {
+ enumerator_ = &n;
+ }
+
+ protected:
+ enum_type* enum__;
+ enumerator_type* enumerator_;
+ };
+
+ //
+ //
+ class enumerator: public nameable, public instance
+ {
+ public:
+ typedef semantics::enum_ enum_type;
+
+ enum_type&
+ enum_ () const
+ {
+ return enumerated_->enum_ ();
+ }
+
+ enumerates&
+ enumerated () const
+ {
+ return *enumerated_;
+ }
+
+ // If the enumeration is signed, then this value should be re-
+ // interpreted as signed.
+ //
+ unsigned long long
+ value () const
+ {
+ return value_;
+ }
+
+ public:
+ enumerator (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ unsigned long long value);
+
+ void
+ add_edge_right (enumerates& e)
+ {
+ enumerated_ = &e;
+ }
+
+ using nameable::add_edge_right;
+
+ private:
+ unsigned long long value_;
+ enumerates* enumerated_;
+ };
+
+ //
+ //
+ class underlies: public edge
+ {
+ public:
+ typedef semantics::enum_ enum_type;
+
+ integral_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ enum_type&
+ enum_ () const
+ {
+ return *enum__;
+ }
+
+ // Names edge in terms of which this edge was defined. Can be NULL.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ underlies ();
+
+ void
+ set_left_node (integral_type& n)
+ {
+ type_ = &n;
+ }
+
+ void
+ set_right_node (enum_type& n)
+ {
+ enum__ = &n;
+ }
+
+ protected:
+ integral_type* type_;
+ enum_type* enum__;
+ names* hint_;
+ };
+
+ //
+ //
+ class enum_: public type, public scope
+ {
+ private:
+ typedef std::vector<enumerates*> enumerates_list;
+
+ public:
+ typedef
+ pointer_iterator<enumerates_list::const_iterator>
+ enumerates_iterator;
+
+ enumerates_iterator
+ enumerates_begin () const
+ {
+ return enumerates_.begin ();
+ }
+
+ enumerates_iterator
+ enumerates_end () const
+ {
+ return enumerates_.end ();
+ }
+
+ underlies&
+ underlied () const
+ {
+ return *underlied_;
+ }
+
+ integral_type&
+ underlying_type () const
+ {
+ return underlied_->type ();
+ }
+
+ names*
+ underlying_type_hint () const
+ {
+ return underlied_->hint ();
+ }
+
+ bool
+ unsigned_ () const
+ {
+ return underlying_type ().unsigned_ ();
+ }
+
+ public:
+ enum_ (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_right (underlies& e)
+ {
+ underlied_ = &e;
+ }
+
+ void
+ add_edge_left (enumerates& e)
+ {
+ enumerates_.push_back (&e);
+ }
+
+ using scope::add_edge_left;
+
+ private:
+ enumerates_list enumerates_;
+ underlies* underlied_;
+ };
+}
+
+#endif // ODB_SEMANTICS_ENUM_HXX
diff --git a/odb/odb/semantics/fundamental.cxx b/odb/odb/semantics/fundamental.cxx
new file mode 100644
index 0000000..ed4a67f
--- /dev/null
+++ b/odb/odb/semantics/fundamental.cxx
@@ -0,0 +1,222 @@
+// file : odb/semantics/fundamental.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/fundamental.hxx>
+
+namespace semantics
+{
+ string fund_type::
+ fq_name () const
+ {
+ return name ();
+ }
+
+ string fund_type::
+ fq_name (names* hint) const
+ {
+ if (hint == 0)
+ return name ();
+
+ return type::fq_name (hint);
+ }
+
+ // char
+ //
+ bool fund_char::
+ unsigned_ () const
+ {
+ return TYPE_UNSIGNED (tree_node ()) != 0;
+ }
+
+ // wchar_t
+ //
+ bool fund_wchar::
+ unsigned_ () const
+ {
+ return TYPE_UNSIGNED (tree_node ()) != 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // fund_type
+ //
+ {
+ type_info ti (typeid (fund_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+
+ // fund_void
+ //
+ {
+ type_info ti (typeid (fund_void));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // integral_type
+ //
+ {
+ type_info ti (typeid (integral_type));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_bool
+ //
+ {
+ type_info ti (typeid (fund_bool));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char
+ //
+ {
+ type_info ti (typeid (fund_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_wchar
+ //
+ {
+ type_info ti (typeid (fund_wchar));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char16
+ //
+ {
+ type_info ti (typeid (fund_char16));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char32
+ //
+ {
+ type_info ti (typeid (fund_char32));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_signed_char
+ //
+ {
+ type_info ti (typeid (fund_signed_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_char
+ //
+ {
+ type_info ti (typeid (fund_unsigned_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_short
+ //
+ {
+ type_info ti (typeid (fund_short));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_short
+ //
+ {
+ type_info ti (typeid (fund_unsigned_short));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_int
+ //
+ {
+ type_info ti (typeid (fund_int));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_int
+ //
+ {
+ type_info ti (typeid (fund_unsigned_int));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_long
+ //
+ {
+ type_info ti (typeid (fund_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_long
+ //
+ {
+ type_info ti (typeid (fund_unsigned_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_long_long
+ //
+ {
+ type_info ti (typeid (fund_long_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_long_long
+ //
+ {
+ type_info ti (typeid (fund_unsigned_long_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_float
+ //
+ {
+ type_info ti (typeid (fund_float));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_double
+ //
+ {
+ type_info ti (typeid (fund_double));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_long_double
+ //
+ {
+ type_info ti (typeid (fund_long_double));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/fundamental.hxx b/odb/odb/semantics/fundamental.hxx
new file mode 100644
index 0000000..15b5cbb
--- /dev/null
+++ b/odb/odb/semantics/fundamental.hxx
@@ -0,0 +1,150 @@
+// file : odb/semantics/fundamental.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_FUNDAMENTAL_HXX
+#define ODB_SEMANTICS_FUNDAMENTAL_HXX
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ // Fundamental C++ types.
+ //
+
+ struct fund_type: type
+ {
+ virtual string
+ fq_name () const;
+
+ virtual string
+ fq_name (names*) const;
+ };
+
+ struct fund_void: fund_type
+ {
+ fund_void (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+
+ //
+ // Integral.
+ //
+
+ struct integral_type: fund_type
+ {
+ virtual bool
+ unsigned_ () const = 0;
+ };
+
+ struct fund_bool: integral_type
+ {
+ fund_bool (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_char: integral_type
+ {
+ fund_char (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const;
+ };
+
+ struct fund_wchar: integral_type
+ {
+ fund_wchar (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const;
+ };
+
+ struct fund_char16: integral_type
+ {
+ fund_char16 (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_char32: integral_type
+ {
+ fund_char32 (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_signed_char: integral_type
+ {
+ fund_signed_char (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_char: integral_type
+ {
+ fund_unsigned_char (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_short: integral_type
+ {
+ fund_short (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_short: integral_type
+ {
+ fund_unsigned_short (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_int: integral_type
+ {
+ fund_int (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_int: integral_type
+ {
+ fund_unsigned_int (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_long: integral_type
+ {
+ fund_long (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_long: integral_type
+ {
+ fund_unsigned_long (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_long_long: integral_type
+ {
+ fund_long_long (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_long_long: integral_type
+ {
+ fund_unsigned_long_long (tree tn)
+ : node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ //
+ // Real.
+ //
+
+ struct fund_float: fund_type
+ {
+ fund_float (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+
+ struct fund_double: fund_type
+ {
+ fund_double (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+
+ struct fund_long_double: fund_type
+ {
+ fund_long_double (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+}
+
+#endif // ODB_SEMANTICS_FUNDAMENTAL_HXX
diff --git a/odb/odb/semantics/namespace.cxx b/odb/odb/semantics/namespace.cxx
new file mode 100644
index 0000000..d9be903
--- /dev/null
+++ b/odb/odb/semantics/namespace.cxx
@@ -0,0 +1,107 @@
+// file : odb/semantics/namespace.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/namespace.hxx>
+
+namespace semantics
+{
+ namespace_::
+ namespace_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn), original_ (0)
+ {
+ }
+
+ namespace_::
+ namespace_ ()
+ : original_ (0)
+ {
+ }
+
+ names* namespace_::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* hidden) const
+ {
+ if (original_ != 0)
+ return original_->lookup (name, ti, flags, hidden);
+
+ // Being hidden in one namespace doesn't mean it is also hidden in
+ // the other. Normally that would be an ambiguous lookup, but we use
+ // relaxed rules.
+ //
+ bool h (false); // Indicates whether this namespace hides the name.
+ bool any_h (false); // Indicates whether any namespace hides the name.
+
+ names* r (scope::lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (r != 0 && h && hidden != 0)
+ *hidden = true;
+
+ for (extensions_iterator i (extensions_begin ());
+ i != extensions_end ();
+ ++i)
+ {
+ h = false;
+ names* er ((*i)->scope::lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (er != 0)
+ {
+ if (r != 0)
+ {
+ // If both are namespaces, then the one is just an extension
+ // of the other.
+ //
+ if (!(r->named ().is_a<namespace_> () &&
+ er->named ().is_a<namespace_> ()))
+ throw ambiguous (*r, *er);
+ }
+ else
+ r = er;
+
+ if (h && hidden != 0)
+ *hidden = true;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ if (any_h)
+ {
+ if (hidden != 0)
+ *hidden = true;
+
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+
+ // Look in the outer scope unless requested not to or if this is
+ // the global scope.
+ //
+ if ((flags & exclude_outer) == 0 && !global_scope ())
+ return scope ().lookup (name, ti, flags, hidden);
+
+ return 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ type_info ti (typeid (namespace_));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/namespace.hxx b/odb/odb/semantics/namespace.hxx
new file mode 100644
index 0000000..b025c2e
--- /dev/null
+++ b/odb/odb/semantics/namespace.hxx
@@ -0,0 +1,71 @@
+// file : odb/semantics/namespace.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_NAMESPACE_HXX
+#define ODB_SEMANTICS_NAMESPACE_HXX
+
+#include <vector>
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ class namespace_: public scope
+ {
+ typedef std::vector<namespace_*> extensions_type;
+
+ public:
+ bool
+ extension () const
+ {
+ return original_ != 0;
+ }
+
+ namespace_&
+ original ()
+ {
+ return *original_;
+ }
+
+ void
+ original (namespace_& ns)
+ {
+ original_ = &ns;
+ ns.extensions_.push_back (this);
+ }
+
+ public:
+ typedef extensions_type::const_iterator extensions_iterator;
+
+ extensions_iterator
+ extensions_begin () const {return extensions_.begin ();}
+
+ extensions_iterator
+ extensions_end () const {return extensions_.end ();}
+
+ public:
+ virtual names*
+ lookup (string const& name,
+ type_id const&,
+ unsigned int flags = 0,
+ bool* hidden = 0) const;
+
+ using scope::lookup;
+
+ public:
+ namespace_ (path const&, size_t line, size_t column, tree);
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ protected:
+ namespace_ ();
+
+ private:
+ namespace_* original_;
+ extensions_type extensions_;
+ };
+}
+
+#endif // ODB_SEMANTICS_NAMESPACE_HXX
diff --git a/odb/odb/semantics/relational.hxx b/odb/odb/semantics/relational.hxx
new file mode 100644
index 0000000..db08a61
--- /dev/null
+++ b/odb/odb/semantics/relational.hxx
@@ -0,0 +1,18 @@
+// file : odb/semantics/relational.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_HXX
+#define ODB_SEMANTICS_RELATIONAL_HXX
+
+#include <odb/semantics/relational/changelog.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/column.hxx>
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/foreign-key.hxx>
+#include <odb/semantics/relational/index.hxx>
+#include <odb/semantics/relational/key.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/primary-key.hxx>
+#include <odb/semantics/relational/table.hxx>
+
+#endif // ODB_SEMANTICS_RELATIONAL_HXX
diff --git a/odb/odb/semantics/relational/changelog.cxx b/odb/odb/semantics/relational/changelog.cxx
new file mode 100644
index 0000000..353497f
--- /dev/null
+++ b/odb/odb/semantics/relational/changelog.cxx
@@ -0,0 +1,187 @@
+// file : odb/semantics/relational/changelog.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <vector>
+#include <sstream>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/changelog.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ changelog::
+ changelog (xml::parser& p)
+ : contains_model_ (0)
+ {
+ using namespace xml;
+
+ p.next_expect (parser::start_element, xmlns, "changelog");
+ p.content (content::complex);
+
+ if (p.attribute<unsigned int> ("version") != 1)
+ throw parsing (p, "unsupported changelog format version");
+
+ database_ = p.attribute ("database");
+ schema_name_ = p.attribute ("schema-name", "");
+
+ // Because things are stored in the reverse order, first save the
+ // changesets as XML chunks and then re-parse them in the reverse
+ // order. We have to do it this way so that we can do lookups along
+ // the alters edges.
+ //
+ typedef vector<string> changesets;
+ changesets cs;
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "changeset"))
+ break; // Not our elements.
+
+ ostringstream os;
+ os.exceptions (ios_base::badbit | ios_base::failbit);
+ serializer s (os, "changeset", 0); // No pretty-printing.
+ size_t depth (0);
+
+ do
+ {
+ switch (p.next ())
+ {
+ case parser::start_element:
+ {
+ s.start_element (p.qname ());
+
+ if (depth == 0)
+ s.namespace_decl (xmlns, "");
+
+ typedef parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ());
+ i != am.end (); ++i)
+ s.attribute (i->first, i->second.value);
+
+ depth++;
+ break;
+ }
+ case parser::end_element:
+ {
+ depth--;
+ s.end_element ();
+ break;
+ }
+ case parser::characters:
+ {
+ s.characters (p.value ());
+ break;
+ }
+ default:
+ {
+ depth = 0;
+ break;
+ }
+ }
+ } while (depth != 0);
+
+ cs.push_back (os.str ());
+ }
+
+ // Get the model.
+ //
+ p.next_expect (parser::start_element, xmlns, "model");
+ model_type& m (new_node<model_type> (p, *this));
+ new_edge<contains_model_type> (*this, m);
+ p.next_expect (parser::end_element);
+
+ // Re-parse the changesets in reverse order.
+ //
+ qscope* base (&m);
+ for (changesets::reverse_iterator i (cs.rbegin ()); i != cs.rend (); ++i)
+ {
+ istringstream is (*i);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+ parser ip (is, p.input_name ());
+
+ ip.next_expect (parser::start_element, xmlns, "changeset");
+
+ changeset& c (new_node<changeset> (ip, *base, *this));
+ new_edge<contains_changeset> (*this, c);
+ base = &c;
+
+ ip.next_expect (parser::end_element);
+ }
+
+ p.next_expect (parser::end_element);
+ }
+
+ void changelog::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "changelog");
+ s.namespace_decl (xmlns, "");
+ s.attribute ("database", database_);
+ if (!schema_name_.empty ())
+ s.attribute ("schema-name", schema_name_);
+ s.attribute ("version", 1); // Format version.
+
+ // For better readability serialize things in reverse order so that
+ // the most recent changeset appears first.
+ //
+ for (contains_changeset_list::const_reverse_iterator i (
+ contains_changeset_.rbegin ());
+ i != contains_changeset_.rend (); ++i)
+ {
+ (*i)->changeset ().serialize (s);
+ s.characters ("\n");
+ }
+
+ model ().serialize (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // contains_model
+ //
+ {
+ type_info ti (typeid (contains_model));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // contains_changeset
+ //
+ {
+ type_info ti (typeid (contains_changeset));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // changelog
+ //
+ {
+ type_info ti (typeid (changelog));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/changelog.hxx b/odb/odb/semantics/relational/changelog.hxx
new file mode 100644
index 0000000..2398cf6
--- /dev/null
+++ b/odb/odb/semantics/relational/changelog.hxx
@@ -0,0 +1,164 @@
+// file : odb/semantics/relational/changelog.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_CHANGELOG_HXX
+#define ODB_SEMANTICS_RELATIONAL_CHANGELOG_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class model;
+ class changeset;
+ class changelog;
+
+ class contains_model: public edge
+ {
+ public:
+ typedef relational::changelog changelog_type;
+ typedef relational::model model_type;
+
+ changelog_type&
+ changelog () const {return *changelog_;}
+
+ model_type&
+ model () const {return *model_;}
+
+ public:
+ void
+ set_left_node (changelog_type& n) {changelog_ = &n;}
+
+ void
+ set_right_node (model_type& n) {model_ = &n;}
+
+ protected:
+ changelog_type* changelog_;
+ model_type* model_;
+ };
+
+ class contains_changeset: public edge
+ {
+ public:
+ typedef relational::changelog changelog_type;
+ typedef relational::changeset changeset_type;
+
+ changelog_type&
+ changelog () const {return *changelog_;}
+
+ changeset_type&
+ changeset () const {return *changeset_;}
+
+ public:
+ void
+ set_left_node (changelog_type& n) {changelog_ = &n;}
+
+ void
+ set_right_node (changeset_type& n) {changeset_ = &n;}
+
+ protected:
+ changelog_type* changelog_;
+ changeset_type* changeset_;
+ };
+
+ class changelog: public graph, public node
+ {
+ typedef std::vector<contains_changeset*> contains_changeset_list;
+
+ public:
+ typedef relational::model model_type;
+ typedef relational::contains_model contains_model_type;
+
+ model_type&
+ model () const {return contains_model_->model ();}
+
+ contains_model_type&
+ contains_model () const {return *contains_model_;}
+
+ public:
+ typedef
+ pointer_iterator<contains_changeset_list::const_iterator>
+ contains_changeset_iterator;
+
+ contains_changeset const&
+ contains_changeset_back () const
+ {
+ return *contains_changeset_.back ();
+ }
+
+ contains_changeset const&
+ contains_changeset_at (contains_changeset_list::size_type i) const
+ {
+ return *contains_changeset_[i];
+ }
+
+ contains_changeset_iterator
+ contains_changeset_begin () const
+ {
+ return contains_changeset_.begin ();
+ }
+
+ contains_changeset_iterator
+ contains_changeset_end () const
+ {
+ return contains_changeset_.end ();
+ }
+
+ contains_changeset_list::size_type
+ contains_changeset_size () const
+ {
+ return contains_changeset_.size ();
+ }
+
+ bool
+ contains_changeset_empty () const
+ {
+ return contains_changeset_.empty ();
+ }
+
+ public:
+ string const&
+ database () const {return database_;}
+
+ string const&
+ schema_name () const {return schema_name_;}
+
+ public:
+ changelog (string const& db, string const& sn)
+ : database_ (db), schema_name_ (sn), contains_model_ (0) {}
+ changelog (xml::parser&);
+
+ void
+ add_edge_left (contains_model_type& e)
+ {
+ assert (contains_model_ == 0);
+ contains_model_ = &e;
+ }
+
+ void
+ add_edge_left (contains_changeset& e)
+ {
+ contains_changeset_.push_back (&e);
+ }
+
+ virtual string
+ kind () const {return "changelog";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ private:
+ changelog (changelog const&);
+ changelog& operator= (changelog const&);
+
+ protected:
+ string database_;
+ string schema_name_;
+ contains_model_type* contains_model_;
+ contains_changeset_list contains_changeset_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_CHANGELOG_HXX
diff --git a/odb/odb/semantics/relational/changeset.cxx b/odb/odb/semantics/relational/changeset.cxx
new file mode 100644
index 0000000..b044a0c
--- /dev/null
+++ b/odb/odb/semantics/relational/changeset.cxx
@@ -0,0 +1,56 @@
+// file : odb/semantics/relational/changeset.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/changeset.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ changeset::
+ changeset (changeset const& c, qscope& b, graph& g)
+ : qscope (c, &b, g),
+ version_ (c.version_),
+ alters_model_ (0)
+ {
+ }
+
+ changeset::
+ changeset (xml::parser& p, qscope& b, graph& g)
+ : qscope (p, &b, g),
+ version_ (p.attribute<version_type> ("version")),
+ alters_model_ (0)
+ {
+ }
+
+ void changeset::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "changeset");
+ s.attribute ("version", version_);
+ qscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (changeset));
+ ti.add_base (typeid (qscope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/changeset.hxx b/odb/odb/semantics/relational/changeset.hxx
new file mode 100644
index 0000000..efe2c61
--- /dev/null
+++ b/odb/odb/semantics/relational/changeset.hxx
@@ -0,0 +1,93 @@
+// file : odb/semantics/relational/changeset.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_CHANGESET_HXX
+#define ODB_SEMANTICS_RELATIONAL_CHANGESET_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class model;
+ class changeset;
+
+ class alters_model: public edge
+ {
+ public:
+ typedef relational::model model_type;
+ typedef relational::changeset changeset_type;
+
+ model_type&
+ model () const {return *model_;}
+
+ changeset_type&
+ changeset () const {return *changeset_;}
+
+ public:
+ alters_model () : model_ (0), changeset_ (0) {}
+
+ void
+ set_left_node (changeset_type& c)
+ {
+ assert (changeset_ == 0);
+ changeset_ = &c;
+ }
+
+ void
+ set_right_node (model_type& m)
+ {
+ assert (model_ == 0);
+ model_ = &m;
+ }
+
+ protected:
+ model_type* model_;
+ changeset_type* changeset_;
+ };
+
+ class changeset: public qscope
+ {
+ public:
+ typedef relational::version version_type;
+
+ version_type
+ version () const {return version_;}
+
+ // Returns model to which this changeset applies (as opposed to the
+ // ultimate base model). Note that it may not be set.
+ //
+ model&
+ base_model () const {return alters_model_->model ();}
+
+ public:
+ changeset (version_type v): version_ (v), alters_model_ (0) {}
+ changeset (changeset const&, qscope& base, graph&);
+ changeset (xml::parser&, qscope& base, graph&);
+
+ virtual string
+ kind () const {return "changeset";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ public:
+ virtual void
+ add_edge_left (alters_model& am)
+ {
+ assert (alters_model_ == 0);
+ alters_model_ = &am;
+ }
+
+ using qscope::add_edge_left;
+ using qscope::add_edge_right;
+
+ private:
+ version_type version_;
+ alters_model* alters_model_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_CHANGESET_HXX
diff --git a/odb/odb/semantics/relational/column.cxx b/odb/odb/semantics/relational/column.cxx
new file mode 100644
index 0000000..9d4d6e5
--- /dev/null
+++ b/odb/odb/semantics/relational/column.cxx
@@ -0,0 +1,201 @@
+// file : odb/semantics/relational/column.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/column.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // column
+ //
+ column::
+ column (column const& c, uscope&, graph& g)
+ : unameable (c, g),
+ type_ (c.type_),
+ null_ (c.null_),
+ default__ (c.default__),
+ options_ (c.options_)
+ {
+ }
+
+ column::
+ column (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g),
+ type_ (p.attribute ("type", string ())),
+ null_ (p.attribute<bool> ("null")),
+ default__ (p.attribute ("default", string ())),
+ options_ (p.attribute ("options", string ()))
+ {
+ p.content (xml::content::empty);
+ }
+
+ column& column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<column> (*this, s, g);
+ }
+
+ void column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "column");
+ serialize_attributes (s);
+ s.end_element ();
+ }
+
+ void column::
+ serialize_attributes (xml::serializer& s) const
+ {
+ unameable::serialize_attributes (s);
+
+ s.attribute ("type", type ());
+ s.attribute ("null", null ()); // Output even if false.
+
+ if (!default_ ().empty ())
+ s.attribute ("default", default_ ());
+
+ if (!options ().empty ())
+ s.attribute ("options", options ());
+ }
+
+ // add_column
+ //
+ add_column& add_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_column> (*this, s, g);
+ }
+
+ void add_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-column");
+ column::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // drop_column
+ //
+ drop_column::
+ drop_column (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_column& drop_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_column> (*this, s, g);
+ }
+
+ void drop_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-column");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // alter_column
+ //
+ alter_column::
+ alter_column (alter_column const& ac, uscope& s, graph& g)
+ : column (ac, s, g),
+ alters_ (0),
+ null_altered_ (ac.null_altered_)
+ {
+ column* b (s.lookup<column, drop_column> (ac.name ()));
+ assert (b != 0);
+ g.new_edge<alters> (*this, *b);
+ }
+
+ alter_column::
+ alter_column (xml::parser& p, uscope& s, graph& g)
+ : column (p, s, g),
+ alters_ (0),
+ null_altered_ (p.attribute_present ("null"))
+ {
+ name_type n (p.attribute<name_type> ("name"));
+ column* b (s.lookup<column, drop_column> (n));
+ assert (b != 0);
+ g.new_edge<alters> (*this, *b);
+ }
+
+ alter_column& alter_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<alter_column> (*this, s, g);
+ }
+
+ void alter_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "alter-column");
+
+ // Here we override the standard column logic.
+ //
+ unameable::serialize_attributes (s);
+
+ if (null_altered_)
+ s.attribute ("null", null_);
+
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["column"] = &unameable::parser_impl<column>;
+ m["add-column"] = &unameable::parser_impl<add_column>;
+ m["drop-column"] = &unameable::parser_impl<drop_column>;
+ m["alter-column"] = &unameable::parser_impl<alter_column>;
+
+ using compiler::type_info;
+
+ // column
+ //
+ {
+ type_info ti (typeid (column));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+
+ // add_column
+ //
+ {
+ type_info ti (typeid (add_column));
+ ti.add_base (typeid (column));
+ insert (ti);
+ }
+
+ // drop_column
+ //
+ {
+ type_info ti (typeid (drop_column));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+
+ // alter_column
+ //
+ {
+ type_info ti (typeid (alter_column));
+ ti.add_base (typeid (column));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/column.hxx b/odb/odb/semantics/relational/column.hxx
new file mode 100644
index 0000000..b7a2c31
--- /dev/null
+++ b/odb/odb/semantics/relational/column.hxx
@@ -0,0 +1,205 @@
+// file : odb/semantics/relational/column.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_COLUMN_HXX
+#define ODB_SEMANTICS_RELATIONAL_COLUMN_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/table.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class contains;
+
+ class column: public unameable
+ {
+ typedef std::vector<contains*> contained_list;
+
+ public:
+ virtual string const&
+ type () const {return type_;}
+
+ virtual bool
+ null () const {return null_;}
+
+ virtual void
+ null (bool n) {null_ = n;}
+
+ virtual string const&
+ default_ () const {return default__;}
+
+ virtual void
+ default_ (string const& d) {default__ = d;}
+
+ virtual string const&
+ options () const {return options_;}
+
+ virtual void
+ options (string const& o) {options_ = o;}
+
+ public:
+ typedef relational::table table_type;
+
+ table_type&
+ table () const {return dynamic_cast<table_type&> (scope ());}
+
+ // Key containment.
+ //
+ public:
+ typedef
+ pointer_iterator<contained_list::const_iterator>
+ contained_iterator;
+
+ contained_iterator
+ contained_begin () const {return contained_.begin ();}
+
+ contained_iterator
+ contained_end () const {return contained_.end ();}
+
+ public:
+ column (string const& id, string const& type, bool null)
+ : unameable (id), type_ (type), null_ (null)
+ {
+ }
+
+ column (column const&, uscope&, graph&);
+ column (xml::parser&, uscope&, graph&);
+
+ virtual column&
+ clone (uscope&, graph&) const;
+
+ void
+ add_edge_right (contains& e)
+ {
+ contained_.push_back (&e);
+ }
+
+ using unameable::add_edge_right;
+
+ virtual string
+ kind () const
+ {
+ return "column";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ protected:
+ string type_;
+ bool null_;
+ string default__;
+ string options_;
+
+ private:
+ contained_list contained_;
+ };
+
+ class add_column: public column
+ {
+ public:
+ add_column (string const& id, string const& type, bool null)
+ : column (id, type, null) {}
+ add_column (column const& c, uscope& s, graph& g): column (c, s, g) {}
+ add_column (xml::parser& p, uscope& s, graph& g): column (p, s, g) {}
+
+ virtual add_column&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add column";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_column: public unameable
+ {
+ public:
+ drop_column (string const& id): unameable (id) {}
+ drop_column (drop_column const& c, uscope&, graph& g)
+ : unameable (c, g) {}
+ drop_column (xml::parser&, uscope&, graph&);
+
+ public:
+ typedef relational::table table_type;
+
+ table_type&
+ table () const {return dynamic_cast<table_type&> (scope ());}
+
+ public:
+ virtual drop_column&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop column";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class alter_column: public column
+ {
+ public:
+ virtual string const&
+ type () const {return base ().type ();}
+
+ bool
+ null_altered () const {return null_altered_;}
+
+ virtual bool
+ null () const {return null_altered_ ? null_ : base ().null ();}
+
+ virtual void
+ null (bool n) {null_ = n; null_altered_ = true;}
+
+ virtual string const&
+ default_ () const {return base ().default_ ();}
+
+ virtual string const&
+ options () const {return base ().options ();}
+
+ public:
+ column&
+ base () const {return dynamic_cast<column&> (alters_->base ());}
+
+ public:
+ alter_column (string const& id)
+ : column (id, "", false), alters_ (0), null_altered_ (false) {}
+ alter_column (alter_column const&, uscope&, graph&);
+ alter_column (xml::parser&, uscope&, graph&);
+
+ virtual alter_column&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "alter column";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ virtual void
+ add_edge_left (alters& a)
+ {
+ assert (alters_ == 0);
+ alters_ = &a;
+ }
+
+ private:
+ alters* alters_;
+
+ bool null_altered_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_COLUMN_HXX
diff --git a/odb/odb/semantics/relational/deferrable.cxx b/odb/odb/semantics/relational/deferrable.cxx
new file mode 100644
index 0000000..076ff69
--- /dev/null
+++ b/odb/odb/semantics/relational/deferrable.cxx
@@ -0,0 +1,55 @@
+// file : odb/semantics/relational/deferrable.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <odb/semantics/relational/deferrable.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ static const char* deferrable_[] =
+ {
+ "NOT DEFERRABLE",
+ "IMMEDIATE",
+ "DEFERRED"
+ };
+
+ string deferrable::
+ string () const
+ {
+ return deferrable_[v_];
+ }
+
+ ostream&
+ operator<< (ostream& os, deferrable const& v)
+ {
+ return os << v.string ();
+ }
+
+ istream&
+ operator>> (istream& is, deferrable& v)
+ {
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "not_deferrable" || s == "NOT DEFERRABLE")
+ v = deferrable::not_deferrable;
+ else if (s == "immediate" || s == "IMMEDIATE")
+ v = deferrable::immediate;
+ else if (s == "deferred" || s == "DEFERRED")
+ v = deferrable::deferred;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/deferrable.hxx b/odb/odb/semantics/relational/deferrable.hxx
new file mode 100644
index 0000000..b2f888d
--- /dev/null
+++ b/odb/odb/semantics/relational/deferrable.hxx
@@ -0,0 +1,41 @@
+// file : odb/semantics/relational/deferrable.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
+#define ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
+
+#include <string>
+#include <iosfwd>
+
+namespace semantics
+{
+ namespace relational
+ {
+ struct deferrable
+ {
+ enum value
+ {
+ not_deferrable,
+ immediate,
+ deferred
+ };
+
+ deferrable (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+ private:
+ value v_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, deferrable const&);
+
+ std::istream&
+ operator>> (std::istream&, deferrable&);
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
diff --git a/odb/odb/semantics/relational/elements.cxx b/odb/odb/semantics/relational/elements.cxx
new file mode 100644
index 0000000..de1878a
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.cxx
@@ -0,0 +1,179 @@
+// file : odb/semantics/relational/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/column.hxx>
+#include <odb/semantics/relational/primary-key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ string const xmlns = "http://www.codesynthesis.com/xmlns/odb/changelog";
+
+ // duplicate_name
+ //
+ template <>
+ duplicate_name::
+ duplicate_name (uscope& s, unameable& o, unameable& d)
+ : scope (s), orig (o), dup (d), name (o.name ())
+ {
+ }
+
+ template <>
+ duplicate_name::
+ duplicate_name (qscope& s, qnameable& o, qnameable& d)
+ : scope (s), orig (o), dup (d), name (o.name ().string ())
+ {
+ }
+
+ // scope<uname>
+ //
+ template <>
+ void scope<uname>::
+ add_edge_left (names_type& e)
+ {
+ nameable_type& n (e.nameable ());
+ name_type const& name (e.name ());
+
+ typename names_map::iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ {
+ typename names_list::iterator i;
+
+ // We want the order to be add/alter columns first, then the
+ // primary key, then other keys, and finnally drop columns.
+ //
+ if (n.is_a<column> () ||
+ n.is_a<add_column> () ||
+ n.is_a<alter_column> ())
+ {
+ i = names_.insert (first_key_, &e);
+ }
+ else if (!n.is_a<drop_column> ())
+ {
+ if (n.is_a<primary_key> ())
+ first_key_ = i = names_.insert (
+ first_key_ != names_.end () ? first_key_ : first_drop_column_,
+ &e);
+ else
+ {
+ i = names_.insert (first_drop_column_, &e);
+
+ if (first_key_ == names_.end ())
+ first_key_ = i;
+ }
+ }
+ else
+ {
+ i = names_.insert (names_.end (), &e);
+
+ if (first_drop_column_ == names_.end ())
+ first_drop_column_ = i;
+ }
+
+ names_map_[name] = i;
+ iterator_map_[&e] = i;
+ }
+ else
+ throw duplicate_name (*this, (*i->second)->nameable (), n);
+ }
+
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ // If we are removing the first key, then move to the next key (or
+ // the end which means there are no keys).
+ //
+ if (first_key_ == i->second)
+ first_key_++;
+
+ // The same for the first drop column.
+ //
+ if (first_drop_column_ == i->second)
+ first_drop_column_++;
+
+ names_.erase (i->second);
+ names_map_.erase (e.name ());
+ iterator_map_.erase (i);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // alters
+ //
+ {
+ type_info ti (typeid (alters));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // names
+ //
+ {
+ type_info ti (typeid (unames));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qnames));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (unameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qnameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (uscope));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qscope));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/elements.hxx b/odb/odb/semantics/relational/elements.hxx
new file mode 100644
index 0000000..06ec552
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.hxx
@@ -0,0 +1,468 @@
+// file : odb/semantics/relational/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
+#define ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
+
+#include <map>
+#include <list>
+#include <vector>
+#include <string>
+#include <cassert>
+
+#include <libcutl/container/graph.hxx>
+#include <libcutl/container/pointer-iterator.hxx>
+#include <libcutl/compiler/context.hxx>
+
+#ifdef ODB_BUILD2
+#include <libstudxml/parser.hxx>
+#include <libstudxml/serializer.hxx>
+#else
+#include <libcutl/xml/parser.hxx>
+#include <libcutl/xml/serializer.hxx>
+namespace cutl {namespace xml {typedef parser content;}}
+#endif
+
+#include <odb/semantics/relational/name.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ using namespace cutl;
+
+ using std::string;
+
+ using container::pointer_iterator;
+ using compiler::context;
+
+ typedef unsigned long long version;
+
+ //
+ //
+ extern string const xmlns;
+
+ //
+ //
+ class node;
+ class edge;
+
+ typedef container::graph<node, edge> graph;
+
+ //
+ //
+ class edge: public context
+ {
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ public:
+ virtual
+ ~edge () {}
+ };
+
+ //
+ //
+ class node: public context
+ {
+ // Return name of the node.
+ //
+ public:
+ virtual string
+ kind () const = 0;
+
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ public:
+ virtual
+ ~node () {}
+
+ // XML serialization.
+ //
+ virtual void
+ serialize (xml::serializer&) const = 0;
+
+ // Sink functions that allow extensions in the form of one-way
+ // edges.
+ //
+ void
+ add_edge_right (edge&) {}
+
+ void
+ remove_edge_right (edge&) {}
+ };
+
+ //
+ //
+ class alters: public edge
+ {
+ public:
+ node&
+ base () const {return *base_;}
+
+ node&
+ modifier () const {return *modifier_;}
+
+ public:
+ alters () : base_ (0), modifier_ (0) {}
+
+ void
+ set_left_node (node& m)
+ {
+ assert (modifier_ == 0);
+ modifier_ = &m;
+ }
+
+ void
+ set_right_node (node& b)
+ {
+ assert (base_ == 0);
+ base_ = &b;
+ }
+
+ void
+ clear_left_node (node& m)
+ {
+ assert (modifier_ == &m);
+ modifier_ = 0;
+ }
+
+ void
+ clear_right_node (node& b)
+ {
+ assert (base_ == &b);
+ base_ = 0;
+ }
+
+ protected:
+ node* base_;
+ node* modifier_;
+ };
+
+ //
+ //
+ template <typename N>
+ class scope;
+
+ template <typename N>
+ class nameable;
+
+ //
+ //
+ template <typename N>
+ class names: public edge
+ {
+ public:
+ typedef N name_type;
+ typedef relational::scope<N> scope_type;
+ typedef relational::nameable<N> nameable_type;
+
+ name_type const&
+ name () const
+ {
+ return name_;
+ }
+
+ scope_type&
+ scope () const
+ {
+ return *scope_;
+ }
+
+ nameable_type&
+ nameable () const
+ {
+ return *nameable_;
+ }
+
+ public:
+ names (name_type const& name): name_ (name) {}
+
+ void
+ set_left_node (scope_type& n)
+ {
+ scope_ = &n;
+ }
+
+ void
+ set_right_node (nameable_type& n)
+ {
+ nameable_ = &n;
+ }
+
+ void
+ clear_left_node (scope_type& n)
+ {
+ assert (scope_ == &n);
+ scope_ = 0;
+ }
+
+ void
+ clear_right_node (nameable_type& n)
+ {
+ assert (nameable_ == &n);
+ nameable_ = 0;
+ }
+
+ protected:
+ name_type name_;
+ scope_type* scope_;
+ nameable_type* nameable_;
+ };
+
+ typedef names<uname> unames;
+ typedef names<qname> qnames;
+
+ //
+ //
+ template <typename N>
+ class nameable: public virtual node
+ {
+ public:
+ typedef N name_type;
+ typedef relational::names<N> names_type;
+ typedef relational::scope<N> scope_type;
+
+ name_type const&
+ name () const {return named_->name ();}
+
+ scope_type&
+ scope () const {return named ().scope ();}
+
+ names_type&
+ named () const {return *named_;}
+
+ string const&
+ id () const {return id_;}
+
+ public:
+ // Id identifies the C++ node (e.g., a class or a data member) that
+ // this model node corresponds to. The ids are not necessarily unique
+ // (e.g., there can be a foreign key and an index with the same id that
+ // correspond to a container member). However, in any given scope, the
+ // {id,typeid} must be unique. This becomes important when we try to
+ // find correspondance between nodes during model diff'ing.
+ //
+ nameable (string const& id): id_ (id), named_ (0) {}
+
+ virtual nameable&
+ clone (scope_type&, graph&) const = 0;
+
+ // Virtual because we call it via nameable interface (e.g., in copy).
+ //
+ virtual void
+ add_edge_right (names_type& e)
+ {
+ assert (named_ == 0);
+ named_ = &e;
+ }
+
+ virtual void
+ remove_edge_right (names_type& e)
+ {
+ assert (named_ == &e);
+ named_ = 0;
+ }
+
+ using node::add_edge_right;
+ using node::remove_edge_right;
+
+ protected:
+ nameable (nameable const&, graph& g);
+ nameable (xml::parser&, graph& g);
+
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ public:
+ typedef void (*parser_func) (xml::parser&, scope_type&, graph&);
+ typedef std::map<std::string, parser_func> parser_map;
+ static parser_map parser_map_;
+
+ template <typename T>
+ static void
+ parser_impl (xml::parser&, scope_type&, graph&);
+
+ private:
+ string id_;
+ names_type* named_;
+ };
+
+ typedef nameable<uname> unameable;
+ typedef nameable<qname> qnameable;
+
+
+ //
+ //
+ struct duplicate_name
+ {
+ template <typename N>
+ duplicate_name (relational::scope<N>&,
+ relational::nameable<N>& orig,
+ relational::nameable<N>& dup);
+
+ node& scope;
+ node& orig;
+ node& dup;
+
+ string name;
+ };
+
+ template <typename N>
+ class scope: public virtual node
+ {
+ protected:
+ typedef N name_type;
+ typedef relational::names<N> names_type;
+ typedef relational::nameable<N> nameable_type;
+
+ typedef std::list<names_type*> names_list;
+ typedef std::map<name_type, typename names_list::iterator> names_map;
+ typedef
+ std::map<names_type const*, typename names_list::iterator>
+ names_iterator_map;
+
+ public:
+ typedef pointer_iterator<typename names_list::iterator> names_iterator;
+ typedef
+ pointer_iterator<typename names_list::const_iterator>
+ names_const_iterator;
+
+ public:
+ // Iteration.
+ //
+ names_iterator
+ names_begin ()
+ {
+ return names_.begin ();
+ }
+
+ names_iterator
+ names_end ()
+ {
+ return names_.end ();
+ }
+
+ names_const_iterator
+ names_begin () const
+ {
+ return names_.begin ();
+ }
+
+ names_const_iterator
+ names_end () const
+ {
+ return names_.end ();
+ }
+
+ bool
+ names_empty () const
+ {
+ return names_.empty ();
+ }
+
+ // Find (this scope only).
+ //
+ template <typename T>
+ T*
+ find (name_type const&);
+
+ names_iterator
+ find (name_type const&);
+
+ names_const_iterator
+ find (name_type const&) const;
+
+ names_iterator
+ find (names_type const&);
+
+ names_const_iterator
+ find (names_type const&) const;
+
+ // Lookup in this and all altered scopes until we find what we are
+ // looking for or hit a stop node of type S (e.g., drop_*).
+ //
+ template <typename T, typename S>
+ T*
+ lookup (name_type const&);
+
+ public:
+ scope*
+ base () const
+ {
+ return alters_ != 0 ? &dynamic_cast<scope&> (alters_->base ()) : 0;
+ }
+
+ public:
+ scope ()
+ : first_key_ (names_.end ()),
+ first_drop_column_ (names_.end ()),
+ alters_ (0) {}
+
+ // Virtual because we call it via scope interface (e.g., in copy).
+ //
+ virtual void
+ add_edge_left (alters& a)
+ {
+ assert (alters_ == 0);
+ alters_ = &a;
+ }
+
+ virtual void
+ remove_edge_left (alters& a)
+ {
+ assert (alters_ == &a);
+ alters_ = 0;
+ }
+
+ virtual void
+ add_edge_left (names_type&);
+
+ virtual void
+ remove_edge_left (names_type&);
+
+ protected:
+ scope (scope const&, scope* base, graph&);
+ scope (xml::parser&, scope* base, graph&);
+
+ void
+ serialize_content (xml::serializer&) const;
+
+ protected:
+ names_list names_;
+ names_map names_map_;
+ names_iterator_map iterator_map_;
+
+ typename names_list::iterator first_key_;
+ typename names_list::iterator first_drop_column_;
+
+ alters* alters_;
+ };
+
+ template <>
+ void scope<uname>::
+ add_edge_left (names_type&);
+
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type&);
+
+ typedef scope<uname> uscope;
+ typedef scope<qname> qscope;
+ }
+}
+
+#include <odb/semantics/relational/elements.txx>
+
+#endif // ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/odb/semantics/relational/elements.txx b/odb/odb/semantics/relational/elements.txx
new file mode 100644
index 0000000..2362d48
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.txx
@@ -0,0 +1,215 @@
+// file : odb/semantics/relational/elements.txx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace semantics
+{
+ namespace relational
+ {
+ // nameable
+ //
+ template <typename N>
+ typename nameable<N>::parser_map nameable<N>::parser_map_;
+
+ template <typename N>
+ template <typename T>
+ void nameable<N>::
+ parser_impl (xml::parser& p, scope_type& s, graph& g)
+ {
+ name_type n (p.attribute ("name", name_type ()));
+ T& x (g.new_node<T> (p, s, g));
+ g.new_edge<names_type> (s, x, n);
+ }
+
+ template <typename N>
+ nameable<N>::
+ nameable (nameable const& n, graph&)
+ : id_ (n.id_), named_ (0)
+ {
+ }
+
+ template <typename N>
+ nameable<N>::
+ nameable (xml::parser&, graph&)
+ // : id_ (p.attribute<string> ("id"))
+ : named_ (0)
+ {
+ // The name attribute is handled in parser_impl().
+ }
+
+ template <typename N>
+ void nameable<N>::
+ serialize_attributes (xml::serializer& s) const
+ {
+ // Omit empty names (e.g., a primary key).
+ //
+ name_type const& n (name ());
+ if (!n.empty ())
+ s.attribute ("name", n);
+
+ //s.attribute ("id", id_);
+ }
+
+ // scope
+ //
+
+ template <typename N>
+ template <typename T, typename S>
+ T* scope<N>::
+ lookup (name_type const& name)
+ {
+ if (T* r = find<T> (name))
+ return r;
+
+ if (scope* b = base ())
+ {
+ if (find<S> (name) == 0)
+ return b->lookup<T, S> (name);
+ }
+
+ return 0;
+ }
+
+ template <typename N>
+ template <typename T>
+ T* scope<N>::
+ find (name_type const& name)
+ {
+ typename names_map::iterator i (names_map_.find (name));
+ return i != names_map_.end ()
+ ? dynamic_cast<T*> (&(*i->second)->nameable ())
+ : 0;
+ }
+
+ template <typename N>
+ typename scope<N>::names_iterator scope<N>::
+ find (name_type const& name)
+ {
+ typename names_map::iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_.end ();
+ else
+ return i->second;
+ }
+
+ template <typename N>
+ typename scope<N>::names_const_iterator scope<N>::
+ find (name_type const& name) const
+ {
+ typename names_map::const_iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_.end ();
+ else
+ return names_const_iterator (i->second);
+ }
+
+ template <typename N>
+ typename scope<N>::names_iterator scope<N>::
+ find (names_type const& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ template <typename N>
+ typename scope<N>::names_const_iterator scope<N>::
+ find (names_type const& e) const
+ {
+ typename names_iterator_map::const_iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ template <typename N>
+ scope<N>::
+ scope (scope const& s, scope* base, graph& g)
+ : first_key_ (names_.end ()),
+ first_drop_column_ (names_.end ()),
+ alters_ (0)
+ {
+ // Set the alters edge for lookup.
+ //
+ if (base != 0)
+ g.new_edge<alters> (*this, *base);
+
+ for (names_const_iterator i (s.names_begin ());
+ i != s.names_end (); ++i)
+ {
+ nameable_type& n (i->nameable ().clone (*this, g));
+ g.new_edge<names_type> (*this, n, i->name ());
+ }
+ }
+
+ template <typename N>
+ scope<N>::
+ scope (xml::parser& p, scope* base, graph& g)
+ : first_key_ (names_.end ()),
+ first_drop_column_ (names_.end ()),
+ alters_ (0)
+ {
+ // Set the alters edge for lookup.
+ //
+ if (base != 0)
+ g.new_edge<alters> (*this, *base);
+
+ using namespace xml;
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ typename nameable_type::parser_map::iterator i (
+ nameable_type::parser_map_.find (p.name ()));
+
+ if (p.namespace_ () != xmlns || i == nameable_type::parser_map_.end ())
+ break; // Not one of our elements.
+
+ p.next ();
+ i->second (p, *this, g);
+ p.next_expect (parser::end_element);
+ }
+ }
+
+ template <typename N>
+ void scope<N>::
+ serialize_content (xml::serializer& s) const
+ {
+ for (names_const_iterator i (names_begin ()); i != names_end (); ++i)
+ i->nameable ().serialize (s);
+ }
+
+ class column;
+ class primary_key;
+
+ template <typename N>
+ void scope<N>::
+ add_edge_left (names_type& e)
+ {
+ name_type const& name (e.name ());
+
+ typename names_map::iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ {
+ typename names_list::iterator i (names_.insert (names_.end (), &e));
+ names_map_[name] = i;
+ iterator_map_[&e] = i;
+ }
+ else
+ throw duplicate_name (*this, (*i->second)->nameable (), e.nameable ());
+ }
+
+ template <typename N>
+ void scope<N>::
+ remove_edge_left (names_type& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ names_.erase (i->second);
+ names_map_.erase (e.name ());
+ iterator_map_.erase (i);
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/foreign-key.cxx b/odb/odb/semantics/relational/foreign-key.cxx
new file mode 100644
index 0000000..0357d95
--- /dev/null
+++ b/odb/odb/semantics/relational/foreign-key.cxx
@@ -0,0 +1,218 @@
+// file : odb/semantics/relational/foreign-key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/foreign-key.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ static const char* action_str[] = {"NO ACTION", "CASCADE", "SET NULL"};
+
+ ostream&
+ operator<< (ostream& os, foreign_key::action_type v)
+ {
+ return os << action_str[v];
+ }
+
+ istream&
+ operator>> (istream& is, foreign_key::action_type& v)
+ {
+ string s;
+ getline (is, s);
+
+ if (!is.eof ())
+ is.setstate (istream::failbit);
+
+ if (!is.fail ())
+ {
+ if (s == "NO ACTION")
+ v = foreign_key::no_action;
+ else if (s == "CASCADE")
+ v = foreign_key::cascade;
+ else if (s == "SET NULL")
+ v = foreign_key::set_null;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+ }
+
+ foreign_key::
+ foreign_key (foreign_key const& k, uscope& s, graph& g)
+ : key (k, s, g),
+ referenced_table_ (k.referenced_table_),
+ referenced_columns_ (k.referenced_columns_),
+ deferrable_ (k.deferrable_),
+ on_delete_ (k.on_delete_)
+ {
+ }
+
+ foreign_key::
+ foreign_key (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ deferrable_ (p.attribute ("deferrable", deferrable_type ())),
+ on_delete_ (p.attribute ("on-delete", no_action))
+ {
+ using namespace xml;
+
+ p.next_expect (parser::start_element, xmlns, "references");
+ referenced_table_ = p.attribute<qname> ("table");
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "column"))
+ break; // Not our elements.
+
+ p.next ();
+ referenced_columns_.push_back (p.attribute<uname> ("name"));
+ p.content (content::empty);
+ p.next_expect (parser::end_element);
+ }
+
+ p.next_expect (parser::end_element);
+ }
+
+ foreign_key& foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<foreign_key> (*this, s, g);
+ }
+
+ void foreign_key::
+ serialize_attributes (xml::serializer& s) const
+ {
+ key::serialize_attributes (s);
+
+ if (deferrable () != deferrable_type::not_deferrable)
+ s.attribute ("deferrable", deferrable ());
+
+ if (on_delete () != no_action)
+ s.attribute ("on-delete", on_delete ());
+ }
+
+ void foreign_key::
+ serialize_content (xml::serializer& s) const
+ {
+ key::serialize_content (s);
+
+ // Referenced columns.
+ //
+ s.start_element (xmlns, "references");
+ s.attribute ("table", referenced_table ());
+
+ for (columns::const_iterator i (referenced_columns_.begin ());
+ i != referenced_columns_.end (); ++i)
+ {
+ s.start_element (xmlns, "column");
+ s.attribute ("name", *i);
+ s.end_element ();
+ }
+
+ s.end_element (); // references
+ }
+
+ void foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "foreign-key");
+ serialize_attributes (s);
+ serialize_content (s);
+ s.end_element (); // foreign-key
+ }
+
+ // add_foreign_key
+ //
+ add_foreign_key& add_foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_foreign_key> (*this, s, g);
+ }
+
+ void add_foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-foreign-key");
+ foreign_key::serialize_attributes (s);
+ foreign_key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_foreign_key
+ //
+ drop_foreign_key::
+ drop_foreign_key (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_foreign_key& drop_foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_foreign_key> (*this, s, g);
+ }
+
+ void drop_foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-foreign-key");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["foreign-key"] = &unameable::parser_impl<foreign_key>;
+ m["add-foreign-key"] = &unameable::parser_impl<add_foreign_key>;
+ m["drop-foreign-key"] = &unameable::parser_impl<drop_foreign_key>;
+
+ using compiler::type_info;
+
+ // foreign_key
+ //
+ {
+ type_info ti (typeid (foreign_key));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+
+ // add_foreign_key
+ //
+ {
+ type_info ti (typeid (add_foreign_key));
+ ti.add_base (typeid (foreign_key));
+ insert (ti);
+ }
+
+ // drop_foreign_key
+ //
+ {
+ type_info ti (typeid (drop_foreign_key));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/foreign-key.hxx b/odb/odb/semantics/relational/foreign-key.hxx
new file mode 100644
index 0000000..32179fa
--- /dev/null
+++ b/odb/odb/semantics/relational/foreign-key.hxx
@@ -0,0 +1,152 @@
+// file : odb/semantics/relational/foreign-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX
+#define ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX
+
+#include <iosfwd>
+
+#include <odb/semantics/relational/deferrable.hxx>
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class foreign_key: public key
+ {
+ public:
+ qname const&
+ referenced_table () const
+ {
+ return referenced_table_;
+ }
+
+ typedef std::vector<string> columns;
+
+ columns const&
+ referenced_columns () const
+ {
+ return referenced_columns_;
+ }
+
+ columns&
+ referenced_columns ()
+ {
+ return referenced_columns_;
+ }
+
+ public:
+ typedef relational::deferrable deferrable_type;
+
+ deferrable_type
+ deferrable () const {return deferrable_;}
+
+ bool
+ not_deferrable () const
+ {
+ return deferrable_ == deferrable_type::not_deferrable;
+ }
+
+ enum action_type
+ {
+ no_action,
+ cascade,
+ set_null
+ };
+
+ action_type
+ on_delete () const {return on_delete_;}
+
+ public:
+ foreign_key (string const& id,
+ qname const& referenced_table,
+ deferrable_type deferrable,
+ action_type on_delete = no_action)
+ : key (id),
+ referenced_table_ (referenced_table),
+ deferrable_ (deferrable),
+ on_delete_ (on_delete)
+ {
+ }
+
+ foreign_key (foreign_key const&, uscope&, graph&);
+ foreign_key (xml::parser&, uscope&, graph&);
+
+ virtual foreign_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "foreign key";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ void
+ serialize_content (xml::serializer&) const;
+
+ private:
+ qname referenced_table_;
+ columns referenced_columns_;
+ deferrable_type deferrable_;
+ action_type on_delete_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, foreign_key::action_type);
+
+ std::istream&
+ operator>> (std::istream&, foreign_key::action_type&);
+
+ class add_foreign_key: public foreign_key
+ {
+ public:
+ add_foreign_key (string const& id,
+ qname const& rt,
+ deferrable_type d,
+ action_type od = no_action)
+ : foreign_key (id, rt, d, od) {}
+ add_foreign_key (foreign_key const& fk, uscope& s, graph& g)
+ : foreign_key (fk, s, g) {}
+ add_foreign_key (xml::parser& p, uscope& s, graph& g)
+ : foreign_key (p, s, g) {}
+
+ virtual add_foreign_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add foreign key";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_foreign_key: public unameable
+ {
+ public:
+ drop_foreign_key (string const& id): unameable (id) {}
+ drop_foreign_key (drop_foreign_key const& dfk, uscope&, graph& g)
+ : unameable (dfk, g) {}
+ drop_foreign_key (xml::parser&, uscope&, graph&);
+
+ virtual drop_foreign_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop foreign key";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX
diff --git a/odb/odb/semantics/relational/index.cxx b/odb/odb/semantics/relational/index.cxx
new file mode 100644
index 0000000..2329f3a
--- /dev/null
+++ b/odb/odb/semantics/relational/index.cxx
@@ -0,0 +1,145 @@
+// file : odb/semantics/relational/index.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/index.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // index
+ //
+ index::
+ index (index const& i, uscope& s, graph& g)
+ : key (i, s, g),
+ type_ (i.type_),
+ method_ (i.method_),
+ options_ (i.options_)
+ {
+ }
+
+ index::
+ index (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ type_ (p.attribute ("type", string ())),
+ method_ (p.attribute ("method", string ())),
+ options_ (p.attribute ("options", string ()))
+ {
+ }
+
+ index& index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<index> (*this, s, g);
+ }
+
+ void index::
+ serialize_attributes (xml::serializer& s) const
+ {
+ key::serialize_attributes (s);
+
+ if (!type ().empty ())
+ s.attribute ("type", type ());
+
+ if (!method ().empty ())
+ s.attribute ("method", method ());
+
+ if (!options ().empty ())
+ s.attribute ("options", options ());
+ }
+
+ void index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "index");
+ serialize_attributes (s);
+ key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // add_index
+ //
+ add_index& add_index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_index> (*this, s, g);
+ }
+
+ void add_index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-index");
+ index::serialize_attributes (s);
+ index::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_index
+ //
+ drop_index::
+ drop_index (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_index& drop_index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_index> (*this, s, g);
+ }
+
+ void drop_index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-index");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["index"] = &unameable::parser_impl<index>;
+ m["add-index"] = &unameable::parser_impl<add_index>;
+ m["drop-index"] = &unameable::parser_impl<drop_index>;
+
+ using compiler::type_info;
+
+ // index
+ //
+ {
+ type_info ti (typeid (index));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+
+ // add_index
+ //
+ {
+ type_info ti (typeid (add_index));
+ ti.add_base (typeid (index));
+ insert (ti);
+ }
+
+ // drop_index
+ //
+ {
+ type_info ti (typeid (drop_index));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/index.hxx b/odb/odb/semantics/relational/index.hxx
new file mode 100644
index 0000000..68648cb
--- /dev/null
+++ b/odb/odb/semantics/relational/index.hxx
@@ -0,0 +1,100 @@
+// file : odb/semantics/relational/index.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_INDEX_HXX
+#define ODB_SEMANTICS_RELATIONAL_INDEX_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // Note that in our model indexes are defined in the table scope.
+ //
+ class index: public key
+ {
+ public:
+ string const&
+ type () const {return type_;}
+
+ string const&
+ method () const {return method_;}
+
+ string const&
+ options () const {return options_;}
+
+ public:
+ index (string const& id,
+ string const& t = string (),
+ string const& m = string (),
+ string const& o = string ())
+ : key (id), type_ (t), method_ (m), options_ (o) {}
+ index (index const&, uscope&, graph&);
+ index (xml::parser&, uscope&, graph&);
+
+ virtual index&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "index";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ private:
+ string type_; // E.g., "UNIQUE", etc.
+ string method_; // E.g., "BTREE", etc.
+ string options_; // Database-specific index options.
+ };
+
+ class add_index: public index
+ {
+ public:
+ add_index (string const& id,
+ string const& t = string (),
+ string const& m = string (),
+ string const& o = string ())
+ : index (id, t, m, o) {}
+ add_index (index const& i, uscope& s, graph& g): index (i, s, g) {}
+ add_index (xml::parser& p, uscope& s, graph& g): index (p, s, g) {}
+
+ virtual add_index&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add index";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_index: public unameable
+ {
+ public:
+ drop_index (string const& id): unameable (id) {}
+ drop_index (drop_index const& di, uscope&, graph& g)
+ : unameable (di, g) {}
+ drop_index (xml::parser&, uscope&, graph&);
+
+ virtual drop_index&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop index";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_INDEX_HXX
diff --git a/odb/odb/semantics/relational/key.cxx b/odb/odb/semantics/relational/key.cxx
new file mode 100644
index 0000000..3511618
--- /dev/null
+++ b/odb/odb/semantics/relational/key.cxx
@@ -0,0 +1,97 @@
+// file : odb/semantics/relational/key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/key.hxx>
+#include <odb/semantics/relational/column.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ key::
+ key (key const& k, uscope& s, graph& g)
+ : unameable (k, g)
+ {
+ for (contains_iterator i (k.contains_begin ());
+ i != k.contains_end (); ++i)
+ {
+ column* c (s.lookup<column, drop_column> (i->column ().name ()));
+ assert (c != 0);
+ g.new_edge<contains> (*this, *c, i->options ());
+ }
+ }
+
+ key::
+ key (xml::parser& p, uscope& s, graph& g)
+ : unameable (p, g)
+ {
+ using namespace xml;
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "column"))
+ break; // Not our elements.
+
+ p.next ();
+ p.content (content::empty);
+
+ uname n (p.attribute<uname> ("name"));
+ column* c (s.lookup<column, drop_column> (n));
+ if (c == 0)
+ throw parsing (p, "invalid column name in the 'name' attribute");
+
+ string o (p.attribute ("options", string ()));
+ g.new_edge<contains> (*this, *c, o);
+
+ p.next_expect (parser::end_element);
+ }
+ }
+
+ void key::
+ serialize_content (xml::serializer& s) const
+ {
+ for (contains_iterator i (contains_begin ()); i != contains_end (); ++i)
+ {
+ s.start_element (xmlns, "column");
+ s.attribute ("name", i->column ().name ());
+ if (!i->options ().empty ())
+ s.attribute ("options", i->options ());
+ s.end_element ();
+ }
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // contains
+ //
+ {
+ type_info ti (typeid (contains));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // key
+ //
+ {
+ type_info ti (typeid (key));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/key.hxx b/odb/odb/semantics/relational/key.hxx
new file mode 100644
index 0000000..814d2ec
--- /dev/null
+++ b/odb/odb/semantics/relational/key.hxx
@@ -0,0 +1,104 @@
+// file : odb/semantics/relational/key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_KEY_HXX
+#define ODB_SEMANTICS_RELATIONAL_KEY_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/table.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class key;
+ class column;
+
+ class contains: public edge
+ {
+ public:
+ typedef relational::key key_type;
+ typedef relational::column column_type;
+
+ key_type&
+ key () const {return *key_;}
+
+ column_type&
+ column () const {return *column_;}
+
+ string const&
+ options () const {return options_;}
+
+ public:
+ contains (string const& o = string ()) : options_ (o) {}
+
+ void
+ set_left_node (key_type& n)
+ {
+ key_ = &n;
+ }
+
+ void
+ set_right_node (column_type& n)
+ {
+ column_ = &n;
+ }
+
+ protected:
+ key_type* key_;
+ column_type* column_;
+ string options_;
+ };
+
+ class key: public unameable
+ {
+ typedef std::vector<contains*> contains_list;
+
+ public:
+ typedef contains_list::size_type contains_size_type;
+
+ typedef
+ pointer_iterator<contains_list::const_iterator>
+ contains_iterator;
+
+ contains_iterator
+ contains_begin () const {return contains_.begin ();}
+
+ contains_iterator
+ contains_end () const {return contains_.end ();}
+
+ contains_size_type
+ contains_size () const {return contains_.size ();}
+
+ contains&
+ contains_at (contains_size_type i) const {return *contains_[i];}
+
+ public:
+ typedef relational::table table_type;
+
+ table_type&
+ table () const {return dynamic_cast<table_type&> (scope ());}
+
+ public:
+ key (std::string const& id): unameable (id) {}
+
+ void
+ add_edge_left (contains& e)
+ {
+ contains_.push_back (&e);
+ }
+
+ protected:
+ key (key const&, uscope&, graph&);
+ key (xml::parser&, uscope&, graph&);
+
+ void
+ serialize_content (xml::serializer&) const;
+
+ private:
+ contains_list contains_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_KEY_HXX
diff --git a/odb/odb/semantics/relational/model.cxx b/odb/odb/semantics/relational/model.cxx
new file mode 100644
index 0000000..8763045
--- /dev/null
+++ b/odb/odb/semantics/relational/model.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/relational/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/model.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ model::
+ model (model const& m, graph& g)
+ : qscope (m, 0, g),
+ version_ (m.version_)
+ {
+ }
+
+ model::
+ model (xml::parser& p, graph& g)
+ : qscope (p, 0, g),
+ version_ (p.attribute<version_type> ("version"))
+ {
+ }
+
+ void model::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "model");
+ s.attribute ("version", version_);
+ qscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (model));
+ ti.add_base (typeid (qscope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/model.hxx b/odb/odb/semantics/relational/model.hxx
new file mode 100644
index 0000000..02d1863
--- /dev/null
+++ b/odb/odb/semantics/relational/model.hxx
@@ -0,0 +1,49 @@
+// file : odb/semantics/relational/model.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_MODEL_HXX
+#define ODB_SEMANTICS_RELATIONAL_MODEL_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class model: public graph, public qscope
+ {
+ public:
+ typedef relational::version version_type;
+
+ version_type
+ version () const {return version_;}
+
+ void
+ version (version_type v) {version_ = v;}
+
+ public:
+ model (version_type v): version_ (v) {}
+ model (model const&, graph&);
+ model (xml::parser&, graph&);
+
+ virtual string
+ kind () const {return "model";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ public:
+ using qscope::add_edge_left;
+ using qscope::add_edge_right;
+
+ private:
+ model (model const&);
+ model& operator= (model const&);
+
+ private:
+ version_type version_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_MODEL_HXX
diff --git a/odb/odb/semantics/relational/name.cxx b/odb/odb/semantics/relational/name.cxx
new file mode 100644
index 0000000..6eb2e16
--- /dev/null
+++ b/odb/odb/semantics/relational/name.cxx
@@ -0,0 +1,89 @@
+// file : odb/semantics/relational/name.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <odb/semantics/relational/name.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ string qname::
+ string () const
+ {
+ std::string r;
+
+ bool f (true);
+ for (iterator i (begin ()); i < end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += *i;
+ }
+
+ return r;
+ }
+
+ qname qname::
+ from_string (std::string const& s)
+ {
+ using std::string;
+
+ qname n;
+
+ string::size_type p (string::npos);
+
+ for (size_t i (0); i < s.size (); ++i)
+ {
+ char c (s[i]);
+
+ if (c == '.')
+ {
+ if (p == string::npos)
+ n.append (string (s, 0, i));
+ else
+ n.append (string (s, p + 1, i - p - 1));
+
+ p = i;
+ }
+ }
+
+ if (p == string::npos)
+ n.append (s);
+ else
+ n.append (string (s, p + 1, string::npos));
+
+ return n;
+ }
+
+ ostream&
+ operator<< (ostream& os, qname const& n)
+ {
+ return os << n.string ();
+ }
+
+ istream&
+ operator>> (istream& is, qname& n)
+ {
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ n = qname::from_string (s);
+ else
+ n.clear ();
+
+ return is;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/name.hxx b/odb/odb/semantics/relational/name.hxx
new file mode 100644
index 0000000..5268b4a
--- /dev/null
+++ b/odb/odb/semantics/relational/name.hxx
@@ -0,0 +1,159 @@
+// file : odb/semantics/relational/name.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_NAME_HXX
+#define ODB_SEMANTICS_RELATIONAL_NAME_HXX
+
+#include <string>
+#include <vector>
+#include <iosfwd>
+
+namespace semantics
+{
+ namespace relational
+ {
+ typedef std::string uname;
+
+ class qname
+ {
+ public:
+ typedef relational::uname uname_type;
+
+ qname () {}
+
+ explicit
+ qname (uname_type const& n) {append (n);}
+
+ template <typename I>
+ qname (I begin, I end)
+ {
+ for (; begin != end; ++begin)
+ append (*begin);
+ }
+
+ qname&
+ operator= (uname_type const& n)
+ {
+ components_.resize (1);
+ components_[0] = n;
+ return *this;
+ }
+
+ void
+ append (uname_type const& n) {components_.push_back (n);}
+
+ void
+ append (qname const& n)
+ {
+ components_.insert (components_.end (),
+ n.components_.begin (),
+ n.components_.end ());
+ }
+
+ void
+ clear () {components_.clear ();}
+
+ // Append a string to the last component.
+ //
+ qname&
+ operator+= (std::string const& s)
+ {
+ if (empty ())
+ append (s);
+ else
+ uname () += s;
+
+ return *this;
+ }
+
+ friend qname
+ operator+ (qname const& n, std::string const& s)
+ {
+ qname r (n);
+
+ if (r.empty ())
+ r.append (s);
+ else
+ r.uname () += s;
+
+ return r;
+ }
+
+ void
+ swap (qname& n) {components_.swap (n.components_);}
+
+ public:
+ bool
+ empty () const {return components_.empty ();}
+
+ bool
+ qualified () const {return components_.size () > 1;}
+
+ bool
+ fully_qualified () const
+ {
+ return qualified () && components_.front ().empty ();
+ }
+
+ public:
+ typedef std::vector<uname_type> components;
+ typedef components::const_iterator iterator;
+
+ iterator
+ begin () const {return components_.begin ();}
+
+ iterator
+ end () const {return components_.end ();}
+
+ uname_type&
+ uname () {return components_.back ();}
+
+ uname_type const&
+ uname () const {return components_.back ();}
+
+ qname
+ qualifier () const
+ {
+ return empty ()
+ ? qname ()
+ : qname (components_.begin (), components_.end () - 1);
+ }
+
+ std::string
+ string () const;
+
+ static qname
+ from_string (std::string const&);
+
+ public:
+ friend bool
+ operator== (qname const& x, qname const& y)
+ {
+ return x.components_ == y.components_;
+ }
+
+ friend bool
+ operator!= (qname const& x, qname const& y)
+ {
+ return x.components_ != y.components_;
+ }
+
+ friend bool
+ operator< (qname const& x, qname const& y)
+ {
+ return x.components_ < y.components_;
+ }
+
+ private:
+ components components_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, qname const&);
+
+ std::istream&
+ operator>> (std::istream&, qname&);
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_NAME_HXX
diff --git a/odb/odb/semantics/relational/primary-key.cxx b/odb/odb/semantics/relational/primary-key.cxx
new file mode 100644
index 0000000..235340f
--- /dev/null
+++ b/odb/odb/semantics/relational/primary-key.cxx
@@ -0,0 +1,80 @@
+// file : odb/semantics/relational/primary-key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/primary-key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ primary_key::
+ primary_key (primary_key const& k, uscope& s, graph& g)
+ : key (k, s, g), auto__ (k.auto__), extra_map_ (k.extra_map_)
+ {
+ }
+
+ primary_key::
+ primary_key (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ auto__ (p.attribute ("auto", false))
+ {
+ // All unhandled attributes go into the extra map.
+ //
+ typedef xml::parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
+ {
+ if (!i->second.handled)
+ extra_map_[i->first.name ()] = i->second.value;
+ }
+ }
+
+ primary_key& primary_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<primary_key> (*this, s, g);
+ }
+
+ void primary_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "primary-key");
+ key::serialize_attributes (s);
+
+ if (auto_ ())
+ s.attribute ("auto", true);
+
+ for (extra_map::const_iterator i (extra_map_.begin ());
+ i != extra_map_.end (); ++i)
+ s.attribute (i->first, i->second);
+
+ key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map_["primary-key"] =
+ &unameable::parser_impl<primary_key>;
+
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (primary_key));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/primary-key.hxx b/odb/odb/semantics/relational/primary-key.hxx
new file mode 100644
index 0000000..114f682
--- /dev/null
+++ b/odb/odb/semantics/relational/primary-key.hxx
@@ -0,0 +1,62 @@
+// file : odb/semantics/relational/primary-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX
+#define ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX
+
+#include <map>
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class primary_key: public key
+ {
+ public:
+ bool
+ auto_ () const {return auto__;}
+
+ // Extra information.
+ //
+ public:
+ typedef std::map<string, string> extra_map;
+
+ extra_map&
+ extra () {return extra_map_;}
+
+ extra_map const&
+ extra () const {return extra_map_;}
+
+ public:
+ primary_key (bool auto_)
+ : key (""), // Primary key has the implicit empty id.
+ auto__ (auto_)
+ {
+ }
+
+ primary_key (primary_key const&, uscope&, graph&);
+ primary_key (xml::parser&, uscope&, graph&);
+
+ virtual primary_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "primary key";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ private:
+ bool auto__;
+ extra_map extra_map_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX
diff --git a/odb/odb/semantics/relational/table.cxx b/odb/odb/semantics/relational/table.cxx
new file mode 100644
index 0000000..3bf763d
--- /dev/null
+++ b/odb/odb/semantics/relational/table.cxx
@@ -0,0 +1,183 @@
+// file : odb/semantics/relational/table.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/table.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // table
+ //
+ table::
+ table (table const& t, qscope& s, graph& g, bool b)
+ : qnameable (t, g),
+ uscope (t, (b ? s.lookup<table, drop_table> (t.name ()) : 0), g),
+ options_ (t.options_),
+ extra_map_ (t.extra_map_)
+ {
+ }
+
+ table::
+ table (xml::parser& p, qscope& s, graph& g, bool b)
+ : qnameable (p, g),
+ uscope (
+ p,
+ (b ? s.lookup<table, drop_table> (
+ p.attribute<qnameable::name_type> ("name")) : 0),
+ g),
+ options_ (p.attribute ("options", string ()))
+ {
+ // All unhandled attributes go into the extra map.
+ //
+ typedef xml::parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
+ {
+ if (!i->second.handled)
+ extra_map_[i->first.name ()] = i->second.value;
+ }
+ }
+
+ table& table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<table> (*this, s, g);
+ }
+
+ void table::
+ serialize_attributes (xml::serializer& s) const
+ {
+ qnameable::serialize_attributes (s);
+
+ if (!options_.empty ())
+ s.attribute ("options", options_);
+
+ for (extra_map::const_iterator i (extra_map_.begin ());
+ i != extra_map_.end (); ++i)
+ s.attribute (i->first, i->second);
+ }
+
+ void table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "table");
+ serialize_attributes (s);
+ uscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // add_table
+ //
+ add_table& add_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<add_table> (*this, s, g);
+ }
+
+ void add_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-table");
+ table::serialize_attributes (s);
+ table::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_table
+ //
+ drop_table::
+ drop_table (xml::parser& p, qscope&, graph& g)
+ : qnameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_table& drop_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<drop_table> (*this, s, g);
+ }
+
+ void drop_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-table");
+ qnameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // alter_table
+ //
+ alter_table& alter_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<alter_table> (*this, s, g);
+ }
+
+ void alter_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "alter-table");
+ table::serialize_attributes (s);
+ table::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ qnameable::parser_map& m (qnameable::parser_map_);
+
+ m["table"] = &qnameable::parser_impl<table>;
+ m["add-table"] = &qnameable::parser_impl<add_table>;
+ m["drop-table"] = &qnameable::parser_impl<drop_table>;
+ m["alter-table"] = &qnameable::parser_impl<alter_table>;
+
+ using compiler::type_info;
+
+ // table
+ //
+ {
+ type_info ti (typeid (table));
+ ti.add_base (typeid (qnameable));
+ ti.add_base (typeid (uscope));
+ insert (ti);
+ }
+
+ // add_table
+ //
+ {
+ type_info ti (typeid (add_table));
+ ti.add_base (typeid (table));
+ insert (ti);
+ }
+
+ // drop_table
+ //
+ {
+ type_info ti (typeid (drop_table));
+ ti.add_base (typeid (qnameable));
+ insert (ti);
+ }
+
+ // alter_table
+ //
+ {
+ type_info ti (typeid (alter_table));
+ ti.add_base (typeid (table));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/table.hxx b/odb/odb/semantics/relational/table.hxx
new file mode 100644
index 0000000..1c4efcf
--- /dev/null
+++ b/odb/odb/semantics/relational/table.hxx
@@ -0,0 +1,115 @@
+// file : odb/semantics/relational/table.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_TABLE_HXX
+#define ODB_SEMANTICS_RELATIONAL_TABLE_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class table: public qnameable, public uscope
+ {
+ public:
+ virtual string const&
+ options () const {return options_;}
+
+ virtual void
+ options (string const& o) {options_ = o;}
+
+ // Extra information.
+ //
+ public:
+ typedef std::map<string, string> extra_map;
+
+ extra_map&
+ extra () {return extra_map_;}
+
+ extra_map const&
+ extra () const {return extra_map_;}
+
+ public:
+ table (string const& id): qnameable (id) {}
+ table (table const&, qscope&, graph&, bool base = false);
+ table (xml::parser&, qscope&, graph&, bool base = false);
+
+ virtual table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ // Resolve ambiguity.
+ //
+ using qnameable::scope;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ protected:
+ string options_;
+ extra_map extra_map_;
+ };
+
+ class add_table: public table
+ {
+ public:
+ add_table (string const& id): table (id) {}
+ add_table (table const& t, qscope& s, graph& g): table (t, s, g) {}
+ add_table (xml::parser& p, qscope& s, graph& g): table (p, s, g) {}
+
+ virtual add_table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_table: public qnameable
+ {
+ public:
+ drop_table (string const& id): qnameable (id) {}
+ drop_table (drop_table const& t, qscope&, graph& g): qnameable (t, g) {}
+ drop_table (xml::parser&, qscope&, graph&);
+
+ virtual drop_table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class alter_table: public table
+ {
+ public:
+ alter_table (string const& id): table (id) {}
+ alter_table (alter_table const& at, qscope& s, graph& g)
+ : table (at, s, g, true) {}
+ alter_table (xml::parser& p, qscope& s, graph& g)
+ : table (p, s, g, true) {}
+
+ virtual alter_table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "alter table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_TABLE_HXX
diff --git a/odb/odb/semantics/template.cxx b/odb/odb/semantics/template.cxx
new file mode 100644
index 0000000..f492be0
--- /dev/null
+++ b/odb/odb/semantics/template.cxx
@@ -0,0 +1,88 @@
+// file : odb/semantics/template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/template.hxx>
+
+namespace semantics
+{
+ template_::
+ template_ ()
+ {
+ }
+
+ instantiates::
+ instantiates ()
+ {
+ }
+
+ instantiation::
+ instantiation ()
+ {
+ }
+
+ type_template::
+ type_template ()
+ {
+ }
+
+ type_instantiation::
+ type_instantiation ()
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // template_
+ //
+ {
+ type_info ti (typeid (template_));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // instantiates
+ //
+ {
+ type_info ti (typeid (instantiates));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // instantiation
+ //
+ {
+ type_info ti (typeid (instantiation));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // type_template
+ //
+ {
+ type_info ti (typeid (type_template));
+ ti.add_base (typeid (template_));
+ insert (ti);
+ }
+
+ // type_instantiation
+ //
+ {
+ type_info ti (typeid (type_instantiation));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (instantiation));
+ insert (ti);
+ }
+
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/template.hxx b/odb/odb/semantics/template.hxx
new file mode 100644
index 0000000..11fe340
--- /dev/null
+++ b/odb/odb/semantics/template.hxx
@@ -0,0 +1,146 @@
+// file : odb/semantics/template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_TEMPLATE_HXX
+#define ODB_SEMANTICS_TEMPLATE_HXX
+
+#include <vector>
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ //
+ class instantiates;
+
+ class template_: public virtual nameable
+ {
+ typedef std::vector<instantiates*> instantiated;
+
+ public:
+ typedef
+ pointer_iterator<instantiated::const_iterator>
+ instantiated_iterator;
+
+ instantiated_iterator
+ instantiated_begin () const
+ {
+ return instantiated_.begin ();
+ }
+
+ instantiated_iterator
+ instantiated_end () const
+ {
+ return instantiated_.end ();
+ }
+
+ public:
+ void
+ add_edge_right (instantiates& e)
+ {
+ instantiated_.push_back (&e);
+ }
+
+ using nameable::add_edge_right;
+
+ protected:
+ template_ ();
+
+ private:
+ instantiated instantiated_;
+ };
+
+ //
+ //
+ class instantiation;
+
+ class instantiates: public edge
+ {
+ public:
+ typedef semantics::template_ template_type;
+ typedef semantics::instantiation instantiation_type;
+
+ template_type&
+ template_ () const
+ {
+ return *template__;
+ }
+
+ instantiation_type&
+ instantiation () const
+ {
+ return *instantiation_;
+ }
+
+ public:
+ instantiates ();
+
+ void
+ set_left_node (instantiation_type& n)
+ {
+ instantiation_ = &n;
+ }
+
+ void
+ set_right_node (template_type& n)
+ {
+ template__ = &n;
+ }
+
+ private:
+ template_type* template__;
+ instantiation_type* instantiation_;
+ };
+
+ //
+ //
+ class instantiation: public virtual node
+ {
+ public:
+ typedef semantics::template_ template_type;
+ typedef semantics::instantiates instantiates_type;
+
+ template_type&
+ template_ () const
+ {
+ return instantiates_->template_ ();
+ }
+
+ instantiates_type&
+ instantiates () const
+ {
+ return *instantiates_;
+ }
+
+ public:
+ void
+ add_edge_left (instantiates_type& e)
+ {
+ instantiates_ = &e;
+ }
+
+ protected:
+ instantiation ();
+
+ private:
+ instantiates_type* instantiates_;
+ };
+
+ //
+ // Type template and instantiation.
+ //
+
+ class type_template: public template_
+ {
+ protected:
+ type_template ();
+ };
+
+ class type_instantiation: public virtual type, public instantiation
+ {
+ protected:
+ type_instantiation ();
+ };
+}
+
+#endif // ODB_SEMANTICS_TEMPLATE_HXX
diff --git a/odb/odb/semantics/union-template.cxx b/odb/odb/semantics/union-template.cxx
new file mode 100644
index 0000000..21fc9c0
--- /dev/null
+++ b/odb/odb/semantics/union-template.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/union-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/union-template.hxx>
+
+namespace semantics
+{
+ union_template::
+ union_template (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ union_instantiation::
+ union_instantiation (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // union_template
+ //
+ {
+ type_info ti (typeid (union_template));
+ ti.add_base (typeid (type_template));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+
+ // union_instantiation
+ //
+ {
+ type_info ti (typeid (union_instantiation));
+ ti.add_base (typeid (union_));
+ ti.add_base (typeid (type_instantiation));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/union-template.hxx b/odb/odb/semantics/union-template.hxx
new file mode 100644
index 0000000..3e719b7
--- /dev/null
+++ b/odb/odb/semantics/union-template.hxx
@@ -0,0 +1,33 @@
+// file : odb/semantics/union-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_UNION_TEMPLATE_HXX
+#define ODB_SEMANTICS_UNION_TEMPLATE_HXX
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/union.hxx>
+#include <odb/semantics/template.hxx>
+
+namespace semantics
+{
+ class union_template: public type_template, public scope
+ {
+ public:
+ union_template (path const&, size_t line, size_t column, tree);
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+ };
+
+ class union_instantiation: public union_, public type_instantiation
+ {
+ public:
+ union_instantiation (path const&, size_t line, size_t column, tree);
+
+ using union_::add_edge_left;
+ using type_instantiation::add_edge_left;
+ };
+}
+
+#endif // ODB_SEMANTICS_UNION_TEMPLATE_HXX
diff --git a/odb/odb/semantics/union.cxx b/odb/odb/semantics/union.cxx
new file mode 100644
index 0000000..007ef57
--- /dev/null
+++ b/odb/odb/semantics/union.cxx
@@ -0,0 +1,36 @@
+// file : odb/semantics/union.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/union.hxx>
+
+namespace semantics
+{
+ union_::
+ union_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // union_
+ //
+ {
+ type_info ti (typeid (union_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/union.hxx b/odb/odb/semantics/union.hxx
new file mode 100644
index 0000000..79adc42
--- /dev/null
+++ b/odb/odb/semantics/union.hxx
@@ -0,0 +1,27 @@
+// file : odb/semantics/union.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_UNION_HXX
+#define ODB_SEMANTICS_UNION_HXX
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ class union_: public virtual type, public scope
+ {
+ public:
+ union_ (path const&, size_t line, size_t column, tree);
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ protected:
+ union_ ()
+ {
+ }
+ };
+}
+
+#endif // ODB_SEMANTICS_UNION_HXX
diff --git a/odb/odb/semantics/unit.cxx b/odb/odb/semantics/unit.cxx
new file mode 100644
index 0000000..4f92aed
--- /dev/null
+++ b/odb/odb/semantics/unit.cxx
@@ -0,0 +1,42 @@
+// file : odb/semantics/unit.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/unit.hxx>
+
+namespace semantics
+{
+ unit::
+ unit (path const& file)
+ : node (file, 1, 1, global_namespace), graph_ (*this)
+ {
+ // Use a special edge to get this->name() return the global
+ // namespace name ("").
+ //
+ new_edge<global_names> (*this, *this);
+ node::unit (*this);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // unit
+ //
+ {
+ type_info ti (typeid (unit));
+ ti.add_base (typeid (namespace_));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/unit.hxx b/odb/odb/semantics/unit.hxx
new file mode 100644
index 0000000..cfccbff
--- /dev/null
+++ b/odb/odb/semantics/unit.hxx
@@ -0,0 +1,183 @@
+// file : odb/semantics/unit.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_UNIT_HXX
+#define ODB_SEMANTICS_UNIT_HXX
+
+#include <map>
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/namespace.hxx>
+
+namespace semantics
+{
+ class unit: public graph<node, edge>, public namespace_
+ {
+ public:
+ unit (path const&);
+
+ private:
+ unit (unit const&);
+ unit& operator= (unit const&);
+
+ // Mapping from tree nodes to semantic graph nodes.
+ //
+ public:
+ node*
+ find (tree key) const
+ {
+ tree_node_map::const_iterator i (tree_node_map_.find (key));
+ return i != tree_node_map_.end () ? i->second : 0;
+ }
+
+ void
+ insert (tree key, node& value)
+ {
+ tree_node_map_[key] = &value;
+ }
+
+ using namespace_::find;
+
+ // Mapping from tree nodes to name hints.
+ //
+ public:
+ names*
+ find_hint (tree key) const
+ {
+ name_hint_map::const_iterator i (name_hint_map_.find (key));
+ return i != name_hint_map_.end () ? i->second : 0;
+ }
+
+ void
+ insert_hint (tree key, names& name)
+ {
+ name_hint_map_[key] = &name;
+ }
+
+ public:
+ template <class T>
+ T&
+ new_node (path const& file, size_t line, size_t column)
+ {
+ T& r (graph_.new_node<T> (file, line, column));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0>
+ T&
+ new_node (path const& file, size_t line, size_t column, A0 const& a0)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1, class A2>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1, a2));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1, class A2, class A3>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1, a2, a3));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1, class A2, class A3, class A4>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1, a2, a3, a4));
+ r.unit (*this);
+ return r;
+ }
+
+ // For fundamental types.
+ //
+ template <class T>
+ T&
+ new_fund_node (tree tn)
+ {
+ T& r (graph_.new_node<T> (tn));
+ r.unit (*this);
+ return r;
+ }
+
+ protected:
+ // Special names edge for the global namespace.
+ //
+ class global_names: public names
+ {
+ public:
+ global_names ()
+ : names ("")
+ {
+ scope_ = 0;
+ }
+
+ void
+ set_left_node (unit&)
+ {
+ }
+
+ void
+ set_right_node (nameable& n)
+ {
+ named_ = &n;
+ }
+
+ void
+ clear_left_node (unit&)
+ {
+ }
+
+ void
+ clear_right_node (nameable& n)
+ {
+ assert (named_ == &n);
+ named_ = 0;
+ }
+ };
+
+ public:
+ void
+ add_edge_left (global_names&)
+ {
+ }
+
+ using namespace_::add_edge_right;
+
+ private:
+ graph<node, edge>& graph_;
+
+ typedef std::map<tree, node*> tree_node_map;
+ tree_node_map tree_node_map_;
+
+ typedef std::map<tree, names*> name_hint_map;
+ name_hint_map name_hint_map_;
+ };
+}
+
+#endif // ODB_SEMANTICS_UNIT_HXX
diff --git a/odb/odb/source.cxx b/odb/odb/source.cxx
new file mode 100644
index 0000000..b2a39be
--- /dev/null
+++ b/odb/odb/source.cxx
@@ -0,0 +1,151 @@
+// file : odb/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+#include <odb/diagnostics.hxx>
+
+using namespace std;
+
+namespace source
+{
+ struct class_: traversal::class_, virtual context
+ {
+ class_ ()
+ : typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+}
+
+void source::class_::
+traverse_object (type& c)
+{
+ bool poly (polymorphic (c));
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // query_columns
+ //
+ if (options.generate_query ())
+ query_columns_type_->traverse (c);
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_common >");
+
+ os << "const " << traits << "::" << endl
+ << "function_table_type*" << endl
+ << traits << "::" << endl
+ << "function_table[database_count];"
+ << endl;
+}
+
+void source::class_::
+traverse_view (type& c)
+{
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ if (c.get<size_t> ("object-count") != 0)
+ view_query_columns_type_->traverse (c);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_common >");
+
+ os << "const " << traits << "::" << endl
+ << "function_table_type*" << endl
+ << traits << "::" << endl
+ << "function_table[database_count];"
+ << endl;
+}
+
+namespace source
+{
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+}
diff --git a/odb/odb/sql-lexer.cxx b/odb/odb/sql-lexer.cxx
new file mode 100644
index 0000000..ab6f549
--- /dev/null
+++ b/odb/odb/sql-lexer.cxx
@@ -0,0 +1,250 @@
+// file : odb/sql-lexer.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <odb/sql-lexer.hxx>
+
+using namespace std;
+
+sql_lexer::
+sql_lexer ()
+ : loc_ ("C"), buf_ (0, 0, 0)
+{
+}
+
+sql_lexer::
+sql_lexer (std::string const& sql)
+ : loc_ ("C"), buf_ (0, 0, 0)
+{
+ lex (sql);
+}
+
+void sql_lexer::
+lex (std::string const& sql)
+{
+ is_.str (sql);
+ is_.clear ();
+
+ l_ = c_ = 1;
+ buf_ = xchar (0, 0, 0);
+ eos_ = false;
+ unget_ = false;
+}
+
+sql_lexer::xchar sql_lexer::
+peek ()
+{
+ if (unget_)
+ return buf_;
+ else
+ {
+ if (eos_)
+ return xchar (xchar::traits_type::eof (), l_, c_);
+ else
+ {
+ xchar::int_type i (is_.peek ());
+
+ if (i == xchar::traits_type::eof ())
+ eos_ = true;
+
+ return xchar (i, l_, c_);
+ }
+ }
+}
+
+sql_lexer::xchar sql_lexer::
+get ()
+{
+ if (unget_)
+ {
+ unget_ = false;
+ return buf_;
+ }
+ else
+ {
+ // When is_.get () returns eof, the failbit is also set (stupid,
+ // isn't?) which may trigger an exception. To work around this
+ // we will call peek() first and only call get() if it is not
+ // eof. But we can only call peek() on eof once; any subsequent
+ // calls will spoil the failbit (even more stupid).
+ //
+ xchar c (peek ());
+
+ if (!is_eos (c))
+ {
+ is_.get ();
+
+ if (c == '\n')
+ {
+ l_++;
+ c_ = 1;
+ }
+ else
+ c_++;
+ }
+
+ return c;
+ }
+}
+
+void sql_lexer::
+unget (xchar c)
+{
+ // Because iostream::unget cannot work once eos is reached,
+ // we have to provide our own implementation.
+ //
+ buf_ = c;
+ unget_ = true;
+}
+
+sql_token sql_lexer::
+next ()
+{
+ skip_spaces ();
+
+ xchar c (get ());
+
+ if (is_eos (c))
+ return sql_token ();
+
+ switch (c)
+ {
+ case '\'':
+ {
+ return string_literal (c);
+ }
+ case '\"':
+ {
+ return string_literal (c);
+ }
+ case '`':
+ {
+ return string_literal (c);
+ }
+ case ';':
+ {
+ return sql_token (sql_token::p_semi);
+ }
+ case ',':
+ {
+ return sql_token (sql_token::p_comma);
+ }
+ case '(':
+ {
+ return sql_token (sql_token::p_lparen);
+ }
+ case ')':
+ {
+ return sql_token (sql_token::p_rparen);
+ }
+ case '=':
+ {
+ return sql_token (sql_token::p_eq);
+ }
+ case '-':
+ {
+ return int_literal (get (), true);
+ }
+ case '+':
+ {
+ return int_literal (get (), false);
+ }
+ }
+
+ if (is_alpha (c) || c == '_')
+ {
+ return identifier (c);
+ }
+
+ if (is_dec_digit (c))
+ {
+ return int_literal (c);
+ }
+
+ ostringstream msg;
+ msg << "unexpected character '" << c << "'";
+ throw invalid_input (c.line (), c.column (), msg.str ());
+}
+
+void sql_lexer::
+skip_spaces ()
+{
+ for (xchar c (peek ());; c = peek ())
+ {
+ if (is_eos (c) || !is_space (c))
+ break;
+
+ get ();
+ }
+}
+
+sql_token sql_lexer::
+identifier (xchar c)
+{
+ string lexeme;
+ lexeme += c;
+
+ for (c = peek ();
+ !is_eos (c) && (is_alnum (c) || c == '_');
+ c = peek ())
+ {
+ get ();
+ lexeme += c;
+ }
+
+ return sql_token (sql_token::t_identifier, lexeme);
+}
+
+sql_token sql_lexer::
+int_literal (xchar c, bool neg, size_t /*ml*/, size_t /*mc*/)
+{
+ //size_t ln (neg ? ml : c.line ()), cl (neg ? mc : c.column ());
+ string lexeme;
+
+ if (neg)
+ lexeme += '-';
+
+ lexeme += c;
+
+ for (c = peek (); !is_eos (c) && is_dec_digit (c); c = peek ())
+ {
+ get ();
+ lexeme += c;
+ }
+
+ return sql_token (sql_token::t_int_lit, lexeme);
+}
+
+sql_token sql_lexer::
+string_literal (xchar c)
+{
+ //size_t ln (c.line ()), cl (c.column ());
+ char q (c);
+ string lexeme;
+ lexeme += c;
+
+ while (true)
+ {
+ xchar c = get ();
+
+ if (is_eos (c))
+ throw invalid_input (
+ c.line (), c.column (), "unterminated quoted string");
+
+ lexeme += c;
+
+ // In SQL, double-quote is used to encode a single quote inside
+ // a string.
+ //
+ if (c == q)
+ {
+ if (peek () == q)
+ get ();
+ else
+ break;
+ }
+ }
+
+ return sql_token (sql_token::t_string_lit, lexeme);
+}
diff --git a/odb/odb/sql-lexer.hxx b/odb/odb/sql-lexer.hxx
new file mode 100644
index 0000000..9d24233
--- /dev/null
+++ b/odb/odb/sql-lexer.hxx
@@ -0,0 +1,129 @@
+// file : odb/sql-lexer.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SQL_LEXER_HXX
+#define ODB_SQL_LEXER_HXX
+
+#include <string>
+#include <locale>
+#include <cstddef> // std::size_t
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+
+class sql_lexer
+{
+public:
+ sql_lexer ();
+ sql_lexer (std::string const& sql);
+
+ void lex (std::string const& sql);
+
+ struct invalid_input
+ {
+ invalid_input (std::size_t l, std::size_t c, std::string const& m)
+ : line (l), column (c), message (m)
+ {
+ }
+
+ std::size_t line;
+ std::size_t column;
+ std::string const message;
+ };
+
+ sql_token
+ next ();
+
+protected:
+ class xchar
+ {
+ public:
+ typedef std::char_traits<char> traits_type;
+ typedef traits_type::int_type int_type;
+ typedef traits_type::char_type char_type;
+
+ xchar (int_type v, std::size_t l, std::size_t c);
+
+ operator char_type () const;
+
+ int_type
+ value () const;
+
+ std::size_t
+ line () const;
+
+ std::size_t
+ column () const;
+
+ private:
+ int_type v_;
+ std::size_t l_;
+ std::size_t c_;
+ };
+
+ xchar
+ peek ();
+
+ xchar
+ get ();
+
+ void
+ unget (xchar);
+
+protected:
+ void
+ skip_spaces ();
+
+ sql_token
+ identifier (xchar);
+
+ sql_token
+ int_literal (xchar,
+ bool neg = false,
+ std::size_t ml = 0,
+ std::size_t mc = 0);
+
+ sql_token
+ string_literal (xchar);
+
+protected:
+ bool
+ is_alpha (char c) const;
+
+ bool
+ is_oct_digit (char c) const;
+
+ bool
+ is_dec_digit (char c) const;
+
+ bool
+ is_hex_digit (char c) const;
+
+ bool
+ is_alnum (char c) const;
+
+ bool
+ is_space (char c) const;
+
+ bool
+ is_eos (xchar const& c) const;
+
+ char
+ to_upper (char c) const;
+
+private:
+ std::locale loc_;
+ std::istringstream is_;
+ std::size_t l_;
+ std::size_t c_;
+
+ bool eos_;
+ bool include_;
+
+ xchar buf_;
+ bool unget_;
+};
+
+#include <odb/sql-lexer.ixx>
+
+#endif // ODB_SQL_LEXER_HXX
diff --git a/odb/odb/sql-lexer.ixx b/odb/odb/sql-lexer.ixx
new file mode 100644
index 0000000..9179804
--- /dev/null
+++ b/odb/odb/sql-lexer.ixx
@@ -0,0 +1,84 @@
+// file : odb/sql-lexer.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+// sql_lexer::xchar
+//
+inline sql_lexer::xchar::
+xchar (int_type v, std::size_t l, std::size_t c)
+ : v_ (v), l_ (l), c_ (c)
+{
+}
+
+inline sql_lexer::xchar::
+operator char_type () const
+{
+ return traits_type::to_char_type (v_);
+}
+
+inline sql_lexer::xchar::int_type sql_lexer::xchar::
+value () const
+{
+ return v_;
+}
+
+inline std::size_t sql_lexer::xchar::
+line () const
+{
+ return l_;
+}
+
+inline std::size_t sql_lexer::xchar::
+column () const
+{
+ return c_;
+}
+
+// lexer
+//
+inline bool sql_lexer::
+is_alpha (char c) const
+{
+ return std::isalpha (c, loc_);
+}
+
+inline bool sql_lexer::
+is_oct_digit (char c) const
+{
+ return std::isdigit (c, loc_) && c != '8' && c != '9';
+}
+
+inline bool sql_lexer::
+is_dec_digit (char c) const
+{
+ return std::isdigit (c, loc_);
+}
+
+inline bool sql_lexer::
+is_hex_digit (char c) const
+{
+ return std::isxdigit (c, loc_);
+}
+
+inline bool sql_lexer::
+is_alnum (char c) const
+{
+ return std::isalnum (c, loc_);
+}
+
+inline bool sql_lexer::
+is_space (char c) const
+{
+ return std::isspace (c, loc_);
+}
+
+inline bool sql_lexer::
+is_eos (xchar const& c) const
+{
+ return c.value () == xchar::traits_type::eof ();
+}
+
+inline char sql_lexer::
+to_upper (char c) const
+{
+ return std::toupper (c, loc_);
+}
diff --git a/odb/odb/sql-token.cxx b/odb/odb/sql-token.cxx
new file mode 100644
index 0000000..da9ecb2
--- /dev/null
+++ b/odb/odb/sql-token.cxx
@@ -0,0 +1,44 @@
+// file : odb/sql-token.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+
+#include <odb/sql-token.hxx>
+
+using namespace std;
+
+static char punctuation_literals[] = {';', ',', '(', ')', '='};
+
+string sql_token::
+string () const
+{
+ switch (type ())
+ {
+ case sql_token::t_eos:
+ {
+ return "<end-of-stream>";
+ }
+ case sql_token::t_identifier:
+ {
+ return identifier ();
+ }
+ case sql_token::t_punctuation:
+ {
+ return std::string (1, punctuation_literals[punctuation ()]);
+ }
+ case sql_token::t_string_lit:
+ case sql_token::t_int_lit:
+ case sql_token::t_float_lit:
+ {
+ return literal ();
+ }
+ }
+
+ return "";
+}
+
+ostream&
+operator<< (ostream& os, sql_token const& t)
+{
+ return os << t.string ();
+}
diff --git a/odb/odb/sql-token.hxx b/odb/odb/sql-token.hxx
new file mode 100644
index 0000000..c34de21
--- /dev/null
+++ b/odb/odb/sql-token.hxx
@@ -0,0 +1,89 @@
+// file : odb/sql-token.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SQL_TOKEN_HXX
+#define ODB_SQL_TOKEN_HXX
+
+#include <string>
+#include <iosfwd>
+#include <cstddef> // std::size_t
+
+class sql_token
+{
+public:
+ enum token_type
+ {
+ t_eos,
+ t_identifier,
+ t_punctuation,
+ t_string_lit,
+ t_int_lit,
+ t_float_lit,
+ };
+
+ token_type
+ type () const;
+
+ // Identifier
+ //
+public:
+ std::string const&
+ identifier () const;
+
+ // Punctuation
+ //
+public:
+ enum punctuation_type
+ {
+ // Keep synched with punctuation_literals in source file.
+ //
+ p_semi,
+ p_comma,
+ p_lparen,
+ p_rparen,
+ p_eq,
+ p_invalid
+ };
+
+ // Return the punctuation id if type is t_punctuation and p_invalid
+ // otherwise.
+ //
+ punctuation_type
+ punctuation () const;
+
+ // Literals.
+ //
+public:
+ std::string const&
+ literal () const;
+
+ // Human-readable string representation.
+ //
+public:
+ std::string
+ string () const;
+
+ // C-tors.
+ //
+public:
+ // EOS and punctuations.
+ //
+ sql_token ();
+ sql_token (punctuation_type p);
+
+ // Identifier and literals.
+ //
+ sql_token (token_type t, std::string const& s);
+
+private:
+ token_type type_;
+ punctuation_type punctuation_;
+ std::string str_;
+};
+
+std::ostream&
+operator<< (std::ostream&, sql_token const&);
+
+#include <odb/sql-token.ixx>
+
+#endif // ODB_SQL_TOKEN_HXX
diff --git a/odb/odb/sql-token.ixx b/odb/odb/sql-token.ixx
new file mode 100644
index 0000000..5118c9f
--- /dev/null
+++ b/odb/odb/sql-token.ixx
@@ -0,0 +1,44 @@
+// file : odb/sql-token.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+inline sql_token::token_type sql_token::
+type () const
+{
+ return type_;
+}
+
+inline std::string const& sql_token::
+identifier () const
+{
+ return str_;
+}
+
+inline sql_token::punctuation_type sql_token::
+punctuation () const
+{
+ return type_ == t_punctuation ? punctuation_ : p_invalid;
+}
+
+inline std::string const& sql_token::
+literal () const
+{
+ return str_;
+}
+
+inline sql_token::
+sql_token ()
+ : type_ (t_eos)
+{
+}
+
+inline sql_token::
+sql_token (punctuation_type p)
+ : type_ (t_punctuation), punctuation_ (p)
+{
+}
+
+inline sql_token::
+sql_token (token_type t, std::string const& s)
+ : type_ (t), str_ (s)
+{
+}
diff --git a/odb/odb/traversal.hxx b/odb/odb/traversal.hxx
new file mode 100644
index 0000000..7f421b1
--- /dev/null
+++ b/odb/odb/traversal.hxx
@@ -0,0 +1,19 @@
+// file : odb/traversal.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_HXX
+#define ODB_TRAVERSAL_HXX
+
+#include <odb/traversal/class.hxx>
+#include <odb/traversal/class-template.hxx>
+#include <odb/traversal/derived.hxx>
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/enum.hxx>
+#include <odb/traversal/fundamental.hxx>
+#include <odb/traversal/namespace.hxx>
+#include <odb/traversal/template.hxx>
+#include <odb/traversal/union.hxx>
+#include <odb/traversal/union-template.hxx>
+#include <odb/traversal/unit.hxx>
+
+#endif // ODB_TRAVERSAL_HXX
diff --git a/odb/odb/traversal/class-template.cxx b/odb/odb/traversal/class-template.cxx
new file mode 100644
index 0000000..b04b625
--- /dev/null
+++ b/odb/odb/traversal/class-template.cxx
@@ -0,0 +1,62 @@
+// file : odb/traversal/class-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/class-template.hxx>
+
+namespace traversal
+{
+ //
+ //
+ void class_template::
+ traverse (type& c)
+ {
+ inherits (c);
+ names (c);
+ }
+
+ void class_template::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_template::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+
+ //
+ //
+ void class_instantiation::
+ traverse (type& c)
+ {
+ instantiates (c);
+ inherits (c);
+ names (c);
+ }
+
+ void class_instantiation::
+ instantiates (type& c)
+ {
+ instantiates (c, *this);
+ }
+
+ void class_instantiation::
+ instantiates (type& c, edge_dispatcher& d)
+ {
+ d.dispatch (c.instantiates ());
+ }
+
+ void class_instantiation::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_instantiation::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+}
diff --git a/odb/odb/traversal/class-template.hxx b/odb/odb/traversal/class-template.hxx
new file mode 100644
index 0000000..18e1e5b
--- /dev/null
+++ b/odb/odb/traversal/class-template.hxx
@@ -0,0 +1,45 @@
+// file : odb/traversal/class-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_CLASS_TEMPLATE_HXX
+#define ODB_TRAVERSAL_CLASS_TEMPLATE_HXX
+
+#include <odb/semantics/class-template.hxx>
+
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/class.hxx>
+
+namespace traversal
+{
+ struct class_template: scope_template<semantics::class_template>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+ };
+
+ struct class_instantiation: scope_template<semantics::class_instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+
+ virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_CLASS_TEMPLATE_HXX
diff --git a/odb/odb/traversal/class.cxx b/odb/odb/traversal/class.cxx
new file mode 100644
index 0000000..80c8b80
--- /dev/null
+++ b/odb/odb/traversal/class.cxx
@@ -0,0 +1,34 @@
+// file : odb/traversal/class.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/class.hxx>
+
+namespace traversal
+{
+ void inherits::
+ traverse (type& i)
+ {
+ dispatch (i.base ());
+ }
+
+ //
+ //
+ void class_::
+ traverse (type& c)
+ {
+ inherits (c);
+ names (c);
+ }
+
+ void class_::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+}
diff --git a/odb/odb/traversal/class.hxx b/odb/odb/traversal/class.hxx
new file mode 100644
index 0000000..de86cc0
--- /dev/null
+++ b/odb/odb/traversal/class.hxx
@@ -0,0 +1,40 @@
+// file : odb/traversal/class.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_CLASS_HXX
+#define ODB_TRAVERSAL_CLASS_HXX
+
+#include <odb/semantics/class.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct inherits: edge<semantics::inherits>
+ {
+ inherits ()
+ {
+ }
+
+ inherits (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct class_: scope_template<semantics::class_>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_CLASS_HXX
diff --git a/odb/odb/traversal/derived.cxx b/odb/odb/traversal/derived.cxx
new file mode 100644
index 0000000..a0acab8
--- /dev/null
+++ b/odb/odb/traversal/derived.cxx
@@ -0,0 +1,111 @@
+// file : odb/traversal/derived.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/derived.hxx>
+
+namespace traversal
+{
+ void qualifies::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ void points::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ void references::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ void contains::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ //
+ //
+ void qualifier::
+ traverse (type& q)
+ {
+ qualifies (q);
+ }
+
+ void qualifier::
+ qualifies (type& q)
+ {
+ qualifies (q, *this);
+ }
+
+ void qualifier::
+ qualifies (type& q, edge_dispatcher& d)
+ {
+ d.dispatch (q.qualifies ());
+ }
+
+ //
+ //
+ void pointer::
+ traverse (type& p)
+ {
+ points (p);
+ }
+
+ void pointer::
+ points (type& p)
+ {
+ points (p, *this);
+ }
+
+ void pointer::
+ points (type& p, edge_dispatcher& d)
+ {
+ d.dispatch (p.points ());
+ }
+
+ //
+ //
+ void reference::
+ traverse (type& r)
+ {
+ references (r);
+ }
+
+ void reference::
+ references (type& r)
+ {
+ references (r, *this);
+ }
+
+ void reference::
+ references (type& r, edge_dispatcher& d)
+ {
+ d.dispatch (r.references ());
+ }
+
+ //
+ //
+ void array::
+ traverse (type& a)
+ {
+ contains (a);
+ }
+
+ void array::
+ contains (type& a)
+ {
+ contains (a, *this);
+ }
+
+ void array::
+ contains (type& a, edge_dispatcher& d)
+ {
+ d.dispatch (a.contains ());
+ }
+}
diff --git a/odb/odb/traversal/derived.hxx b/odb/odb/traversal/derived.hxx
new file mode 100644
index 0000000..b7648aa
--- /dev/null
+++ b/odb/odb/traversal/derived.hxx
@@ -0,0 +1,130 @@
+// file : odb/traversal/derived.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_DERIVED_HXX
+#define ODB_TRAVERSAL_DERIVED_HXX
+
+#include <odb/semantics/derived.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ //
+ // Edges.
+ //
+ struct qualifies: edge<semantics::qualifies>
+ {
+ qualifies ()
+ {
+ }
+
+ qualifies (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct points: edge<semantics::points>
+ {
+ points ()
+ {
+ }
+
+ points (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct references: edge<semantics::references>
+ {
+ references ()
+ {
+ }
+
+ references (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct contains: edge<semantics::contains>
+ {
+ contains ()
+ {
+ }
+
+ contains (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+
+ //
+ // Nodes.
+ //
+ struct derived_type: node<semantics::derived_type> {};
+
+ struct qualifier: node<semantics::qualifier>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ qualifies (type&);
+
+ virtual void
+ qualifies (type&, edge_dispatcher&);
+ };
+
+ struct pointer: node<semantics::pointer>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ points (type&);
+
+ virtual void
+ points (type&, edge_dispatcher&);
+ };
+
+ struct reference: node<semantics::reference>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ references (type&);
+
+ virtual void
+ references (type&, edge_dispatcher&);
+ };
+
+ struct array: node<semantics::array>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ contains (type&);
+
+ virtual void
+ contains (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_DERIVED_HXX
diff --git a/odb/odb/traversal/elements.cxx b/odb/odb/traversal/elements.cxx
new file mode 100644
index 0000000..f95917a
--- /dev/null
+++ b/odb/odb/traversal/elements.cxx
@@ -0,0 +1,77 @@
+// file : odb/traversal/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ void names::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void declares::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void defines::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void typedefs::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void belongs::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ // instance
+ //
+ void instance::
+ traverse (type& i)
+ {
+ belongs (i);
+ }
+
+ void instance::
+ belongs (type& i)
+ {
+ belongs (i, *this);
+ }
+
+ void instance::
+ belongs (type& i, edge_dispatcher& d)
+ {
+ d.dispatch (i.belongs ());
+ }
+
+ // data_member
+ //
+ void data_member::
+ traverse (type& m)
+ {
+ belongs (m);
+ }
+
+ void data_member::
+ belongs (type& m)
+ {
+ belongs (m, *this);
+ }
+
+ void data_member::
+ belongs (type& m, edge_dispatcher& d)
+ {
+ d.dispatch (m.belongs ());
+ }
+}
diff --git a/odb/odb/traversal/elements.hxx b/odb/odb/traversal/elements.hxx
new file mode 100644
index 0000000..d67a6d8
--- /dev/null
+++ b/odb/odb/traversal/elements.hxx
@@ -0,0 +1,238 @@
+// file : odb/traversal/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_ELEMENTS_HXX
+#define ODB_TRAVERSAL_ELEMENTS_HXX
+
+#include <libcutl/compiler/traversal.hxx>
+#include <odb/semantics/elements.hxx>
+
+namespace traversal
+{
+ using namespace cutl;
+
+ //
+ //
+ typedef compiler::dispatcher<semantics::node> node_dispatcher;
+ typedef compiler::dispatcher<semantics::edge> edge_dispatcher;
+
+ //
+ //
+ struct node_base: node_dispatcher, edge_dispatcher
+ {
+ void
+ edge_traverser (edge_dispatcher& d)
+ {
+ edge_dispatcher::traverser (d);
+ }
+
+ edge_dispatcher&
+ edge_traverser ()
+ {
+ return *this;
+ }
+
+ using node_dispatcher::dispatch;
+ using edge_dispatcher::dispatch;
+
+ using edge_dispatcher::iterate_and_dispatch;
+ };
+
+ struct edge_base: edge_dispatcher, node_dispatcher
+ {
+ void
+ node_traverser (node_dispatcher& d)
+ {
+ node_dispatcher::traverser (d);
+ }
+
+ node_dispatcher&
+ node_traverser ()
+ {
+ return *this;
+ }
+
+ using edge_dispatcher::dispatch;
+ using node_dispatcher::dispatch;
+
+ using node_dispatcher::iterate_and_dispatch;
+ };
+
+ inline edge_base&
+ operator>> (node_base& n, edge_base& e)
+ {
+ n.edge_traverser (e);
+ return e;
+ }
+
+ inline node_base&
+ operator>> (edge_base& e, node_base& n)
+ {
+ e.node_traverser (n);
+ return n;
+ }
+
+ //
+ //
+ template <typename X>
+ struct node: compiler::traverser_impl<X, semantics::node>,
+ virtual node_base
+ {
+ };
+
+ template <typename X>
+ struct edge: compiler::traverser_impl<X, semantics::edge>,
+ virtual edge_base
+ {
+ };
+
+ //
+ // Edges
+ //
+
+ struct names: edge<semantics::names>
+ {
+ names ()
+ {
+ }
+
+ names (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct declares: edge<semantics::declares>
+ {
+ declares ()
+ {
+ }
+
+ declares (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct defines: edge<semantics::defines>
+ {
+ defines ()
+ {
+ }
+
+ defines (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct typedefs: edge<semantics::typedefs>
+ {
+ typedefs ()
+ {
+ }
+
+ typedefs (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct belongs: edge<semantics::belongs>
+ {
+ belongs ()
+ {
+ }
+
+ belongs (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ //
+ // Nodes
+ //
+
+ struct nameable: node<semantics::nameable> {};
+
+ //
+ //
+ template <typename T>
+ struct scope_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& s)
+ {
+ names (s);
+ }
+
+ virtual void
+ names (T& s)
+ {
+ names (s, *this);
+ }
+
+ virtual void
+ names (T& s, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
+ }
+ };
+
+ struct scope: scope_template<semantics::scope> {};
+
+ //
+ //
+ struct type: node<semantics::type> {};
+
+ //
+ //
+ struct instance: node<semantics::instance>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ belongs (type&, edge_dispatcher&);
+ };
+
+ //
+ //
+ struct data_member: node<semantics::data_member>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ belongs (type&, edge_dispatcher&);
+ };
+
+ //
+ //
+ struct unsupported_type: node<semantics::unsupported_type> {};
+}
+
+#endif // ODB_TRAVERSAL_ELEMENTS_HXX
diff --git a/odb/odb/traversal/enum.cxx b/odb/odb/traversal/enum.cxx
new file mode 100644
index 0000000..fa33f92
--- /dev/null
+++ b/odb/odb/traversal/enum.cxx
@@ -0,0 +1,54 @@
+// file : odb/traversal/enum.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/enum.hxx>
+
+namespace traversal
+{
+ void enumerates::
+ traverse (type& e)
+ {
+ dispatch (e.enumerator ());
+ }
+
+ //
+ //
+ void underlies::
+ traverse (type& u)
+ {
+ dispatch (u.type ());
+ }
+
+ //
+ //
+ void enum_::
+ traverse (type& e)
+ {
+ underlied (e);
+ enumerates (e);
+ }
+
+ void enum_::
+ underlied (type& e)
+ {
+ underlied (e, *this);
+ }
+
+ void enum_::
+ underlied (type& e, edge_dispatcher& d)
+ {
+ d.dispatch (e.underlied ());
+ }
+
+ void enum_::
+ enumerates (type& e)
+ {
+ enumerates (e, *this);
+ }
+
+ void enum_::
+ enumerates (type& e, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (e.enumerates_begin (), e.enumerates_end (), d);
+ }
+}
diff --git a/odb/odb/traversal/enum.hxx b/odb/odb/traversal/enum.hxx
new file mode 100644
index 0000000..cd141f2
--- /dev/null
+++ b/odb/odb/traversal/enum.hxx
@@ -0,0 +1,57 @@
+// file : odb/traversal/enum.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_ENUM_HXX
+#define ODB_TRAVERSAL_ENUM_HXX
+
+#include <odb/semantics/enum.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct enumerates: edge<semantics::enumerates>
+ {
+ enumerates () {}
+ enumerates (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct enumerator: node<semantics::enumerator> {};
+
+ struct underlies: edge<semantics::underlies>
+ {
+ underlies () {}
+ underlies (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct enum_: node<semantics::enum_>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ underlied (type&);
+
+ virtual void
+ underlied (type&, edge_dispatcher&);
+
+ virtual void
+ enumerates (type&);
+
+ virtual void
+ enumerates (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_ENUM_HXX
diff --git a/odb/odb/traversal/fundamental.hxx b/odb/odb/traversal/fundamental.hxx
new file mode 100644
index 0000000..974e74f
--- /dev/null
+++ b/odb/odb/traversal/fundamental.hxx
@@ -0,0 +1,39 @@
+// file : odb/traversal/fundamental.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_FUNDAMENTAL_HXX
+#define ODB_TRAVERSAL_FUNDAMENTAL_HXX
+
+#include <odb/semantics/fundamental.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct fund_type: node<semantics::fund_type> {};
+
+ struct fund_void: node<semantics::fund_void> {};
+ struct fund_bool: node<semantics::fund_bool> {};
+
+ // Integral.
+ //
+ struct fund_char: node<semantics::fund_char> {};
+ struct fund_wchar: node<semantics::fund_wchar> {};
+ struct fund_signed_char: node<semantics::fund_signed_char> {};
+ struct fund_unsigned_char: node<semantics::fund_unsigned_char> {};
+ struct fund_short: node<semantics::fund_short> {};
+ struct fund_unsigned_short: node<semantics::fund_unsigned_short> {};
+ struct fund_int: node<semantics::fund_int> {};
+ struct fund_unsigned_int: node<semantics::fund_unsigned_int> {};
+ struct fund_long: node<semantics::fund_long> {};
+ struct fund_unsigned_long: node<semantics::fund_unsigned_long> {};
+ struct fund_long_long: node<semantics::fund_long_long> {};
+ struct fund_unsigned_long_long: node<semantics::fund_unsigned_long_long> {};
+
+ // Real.
+ //
+ struct fund_float: node<semantics::fund_float> {};
+ struct fund_double: node<semantics::fund_double> {};
+ struct fund_long_double: node<semantics::fund_long_double> {};
+}
+
+#endif // ODB_TRAVERSAL_FUNDAMENTAL_HXX
diff --git a/odb/odb/traversal/namespace.hxx b/odb/odb/traversal/namespace.hxx
new file mode 100644
index 0000000..223322b
--- /dev/null
+++ b/odb/odb/traversal/namespace.hxx
@@ -0,0 +1,15 @@
+// file : odb/traversal/namespace.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_NAMESPACE_HXX
+#define ODB_TRAVERSAL_NAMESPACE_HXX
+
+#include <odb/semantics/namespace.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct namespace_: scope_template<semantics::namespace_> {};
+}
+
+#endif // ODB_TRAVERSAL_NAMESPACE_HXX
diff --git a/odb/odb/traversal/relational.hxx b/odb/odb/traversal/relational.hxx
new file mode 100644
index 0000000..a78e26b
--- /dev/null
+++ b/odb/odb/traversal/relational.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_HXX
+#define ODB_TRAVERSAL_RELATIONAL_HXX
+
+#include <odb/traversal/relational/changelog.hxx>
+#include <odb/traversal/relational/changeset.hxx>
+#include <odb/traversal/relational/column.hxx>
+#include <odb/traversal/relational/elements.hxx>
+#include <odb/traversal/relational/foreign-key.hxx>
+#include <odb/traversal/relational/index.hxx>
+#include <odb/traversal/relational/key.hxx>
+#include <odb/traversal/relational/model.hxx>
+#include <odb/traversal/relational/primary-key.hxx>
+#include <odb/traversal/relational/table.hxx>
+
+#endif // ODB_TRAVERSAL_RELATIONAL_HXX
diff --git a/odb/odb/traversal/relational/changelog.cxx b/odb/odb/traversal/relational/changelog.cxx
new file mode 100644
index 0000000..149d93d
--- /dev/null
+++ b/odb/odb/traversal/relational/changelog.cxx
@@ -0,0 +1,63 @@
+// file : odb/traversal/relational/changelog.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/relational/changelog.hxx>
+#include <odb/traversal/relational/model.hxx>
+#include <odb/traversal/relational/changeset.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ // contains_model
+ //
+ void contains_model::
+ traverse (type& c)
+ {
+ dispatch (c.model ());
+ }
+
+ // contains_changeset
+ //
+ void contains_changeset::
+ traverse (type& c)
+ {
+ dispatch (c.changeset ());
+ }
+
+ // changelog
+ //
+ void changelog::
+ traverse (type& cl)
+ {
+ contains_model (cl);
+ contains_changeset (cl);
+ }
+
+ void changelog::
+ contains_model (type& cl)
+ {
+ contains_model (cl, *this);
+ }
+
+ void changelog::
+ contains_model (type& cl, edge_dispatcher& d)
+ {
+ d.dispatch (cl.contains_model ());
+ }
+
+ void changelog::
+ contains_changeset (type& cl)
+ {
+ contains_changeset (cl, *this);
+ }
+
+ void changelog::
+ contains_changeset (type& cl, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (cl.contains_changeset_begin (),
+ cl.contains_changeset_end (),
+ d);
+ }
+ }
+}
diff --git a/odb/odb/traversal/relational/changelog.hxx b/odb/odb/traversal/relational/changelog.hxx
new file mode 100644
index 0000000..4b7f18f
--- /dev/null
+++ b/odb/odb/traversal/relational/changelog.hxx
@@ -0,0 +1,53 @@
+// file : odb/traversal/relational/changelog.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_CHANGELOG_HXX
+#define ODB_TRAVERSAL_RELATIONAL_CHANGELOG_HXX
+
+#include <odb/semantics/relational/changelog.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct contains_model: edge<semantics::relational::contains_model>
+ {
+ contains_model () {}
+ contains_model (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct contains_changeset: edge<semantics::relational::contains_changeset>
+ {
+ contains_changeset () {}
+ contains_changeset (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct changelog: node<semantics::relational::changelog>
+ {
+ public:
+ virtual void
+ traverse (type&);
+
+ virtual void
+ contains_model (type&);
+
+ virtual void
+ contains_model (type&, edge_dispatcher&);
+
+ virtual void
+ contains_changeset (type&);
+
+ virtual void
+ contains_changeset (type&, edge_dispatcher&);
+ };
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_CHANGELOG_HXX
diff --git a/odb/odb/traversal/relational/changeset.hxx b/odb/odb/traversal/relational/changeset.hxx
new file mode 100644
index 0000000..3cc522a
--- /dev/null
+++ b/odb/odb/traversal/relational/changeset.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational/changeset.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_CHANGESET_HXX
+#define ODB_TRAVERSAL_RELATIONAL_CHANGESET_HXX
+
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct changeset: scope_template<semantics::relational::changeset> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_CHANGESET_HXX
diff --git a/odb/odb/traversal/relational/column.hxx b/odb/odb/traversal/relational/column.hxx
new file mode 100644
index 0000000..b9c586d
--- /dev/null
+++ b/odb/odb/traversal/relational/column.hxx
@@ -0,0 +1,21 @@
+// file : odb/traversal/relational/column.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX
+#define ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX
+
+#include <odb/semantics/relational/column.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct column: node<semantics::relational::column> {};
+ struct add_column: node<semantics::relational::add_column> {};
+ struct drop_column: node<semantics::relational::drop_column> {};
+ struct alter_column: node<semantics::relational::alter_column> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX
diff --git a/odb/odb/traversal/relational/elements.hxx b/odb/odb/traversal/relational/elements.hxx
new file mode 100644
index 0000000..2b43ab0
--- /dev/null
+++ b/odb/odb/traversal/relational/elements.hxx
@@ -0,0 +1,162 @@
+// file : odb/traversal/relational/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
+#define ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
+
+#include <libcutl/compiler/traversal.hxx>
+#include <odb/semantics/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ using namespace cutl;
+
+ //
+ //
+ typedef compiler::dispatcher<semantics::relational::node> node_dispatcher;
+ typedef compiler::dispatcher<semantics::relational::edge> edge_dispatcher;
+
+ //
+ //
+ struct node_base: node_dispatcher, edge_dispatcher
+ {
+ void
+ edge_traverser (edge_dispatcher& d)
+ {
+ edge_dispatcher::traverser (d);
+ }
+
+ edge_dispatcher&
+ edge_traverser ()
+ {
+ return *this;
+ }
+
+ using node_dispatcher::dispatch;
+ using edge_dispatcher::dispatch;
+
+ using edge_dispatcher::iterate_and_dispatch;
+ };
+
+ struct edge_base: edge_dispatcher, node_dispatcher
+ {
+ void
+ node_traverser (node_dispatcher& d)
+ {
+ node_dispatcher::traverser (d);
+ }
+
+ node_dispatcher&
+ node_traverser ()
+ {
+ return *this;
+ }
+
+ using edge_dispatcher::dispatch;
+ using node_dispatcher::dispatch;
+
+ using node_dispatcher::iterate_and_dispatch;
+ };
+
+ inline edge_base&
+ operator>> (node_base& n, edge_base& e)
+ {
+ n.edge_traverser (e);
+ return e;
+ }
+
+ inline node_base&
+ operator>> (edge_base& e, node_base& n)
+ {
+ e.node_traverser (n);
+ return n;
+ }
+
+ //
+ //
+ template <typename X>
+ struct node: compiler::traverser_impl<X, semantics::relational::node>,
+ virtual node_base
+ {
+ virtual void
+ traverse (X&) {}
+ };
+
+ template <typename X>
+ struct edge: compiler::traverser_impl<X, semantics::relational::edge>,
+ virtual edge_base
+ {
+ };
+
+ //
+ // Edges
+ //
+
+ template <typename N>
+ struct names: edge<semantics::relational::names<N> >
+ {
+ names ()
+ {
+ }
+
+ names (node_dispatcher& n)
+ {
+ this->node_traverser (n);
+ }
+
+ virtual void
+ traverse (semantics::relational::names<N>& e)
+ {
+ this->dispatch (e.nameable ());
+ }
+ };
+
+ typedef names<semantics::relational::uname> unames;
+ typedef names<semantics::relational::qname> qnames;
+
+ //
+ // Nodes
+ //
+
+ template <typename N>
+ struct nameable: node<semantics::relational::nameable<N> > {};
+
+ typedef nameable<semantics::relational::uname> unameable;
+ typedef nameable<semantics::relational::qname> qnameable;
+
+ //
+ //
+ template <typename T>
+ struct scope_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& s)
+ {
+ names (s);
+ }
+
+ virtual void
+ names (T& s)
+ {
+ names (s, *this);
+ }
+
+ virtual void
+ names (T& s, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
+ }
+ };
+
+ template <typename N>
+ struct scope: scope_template<semantics::relational::scope<N> > {};
+
+ typedef scope<semantics::relational::uname> uscope;
+ typedef scope<semantics::relational::qname> qscope;
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/odb/traversal/relational/foreign-key.hxx b/odb/odb/traversal/relational/foreign-key.hxx
new file mode 100644
index 0000000..b8ccba3
--- /dev/null
+++ b/odb/odb/traversal/relational/foreign-key.hxx
@@ -0,0 +1,21 @@
+// file : odb/traversal/relational/foreign-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX
+#define ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX
+
+#include <odb/semantics/relational/foreign-key.hxx>
+#include <odb/traversal/relational/key.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct foreign_key: key_template<semantics::relational::foreign_key> {};
+ struct add_foreign_key:
+ key_template<semantics::relational::add_foreign_key> {};
+ struct drop_foreign_key: node<semantics::relational::drop_foreign_key> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX
diff --git a/odb/odb/traversal/relational/index.hxx b/odb/odb/traversal/relational/index.hxx
new file mode 100644
index 0000000..2277fee
--- /dev/null
+++ b/odb/odb/traversal/relational/index.hxx
@@ -0,0 +1,20 @@
+// file : odb/traversal/relational/index.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_INDEX_HXX
+#define ODB_TRAVERSAL_RELATIONAL_INDEX_HXX
+
+#include <odb/semantics/relational/index.hxx>
+#include <odb/traversal/relational/key.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct index: key_template<semantics::relational::index> {};
+ struct add_index: key_template<semantics::relational::add_index> {};
+ struct drop_index: node<semantics::relational::drop_index> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_INDEX_HXX
diff --git a/odb/odb/traversal/relational/key.cxx b/odb/odb/traversal/relational/key.cxx
new file mode 100644
index 0000000..15a131c
--- /dev/null
+++ b/odb/odb/traversal/relational/key.cxx
@@ -0,0 +1,17 @@
+// file : odb/traversal/relational/key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/relational/key.hxx>
+#include <odb/traversal/relational/column.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ void contains::
+ traverse (type& c)
+ {
+ dispatch (c.column ());
+ }
+ }
+}
diff --git a/odb/odb/traversal/relational/key.hxx b/odb/odb/traversal/relational/key.hxx
new file mode 100644
index 0000000..d0ba2d7
--- /dev/null
+++ b/odb/odb/traversal/relational/key.hxx
@@ -0,0 +1,50 @@
+// file : odb/traversal/relational/key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_KEY_HXX
+#define ODB_TRAVERSAL_RELATIONAL_KEY_HXX
+
+#include <odb/semantics/relational/key.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct contains: edge<semantics::relational::contains>
+ {
+ contains () {}
+ contains (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
+ template <typename T>
+ struct key_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& k)
+ {
+ contains (k);
+ }
+
+ virtual void
+ contains (T& k)
+ {
+ contains (k, *this);
+ }
+
+ virtual void
+ contains (T& k, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (k.contains_begin (), k.contains_end (), d);
+ }
+ };
+
+ struct key: key_template<semantics::relational::key> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_KEY_HXX
diff --git a/odb/odb/traversal/relational/model.hxx b/odb/odb/traversal/relational/model.hxx
new file mode 100644
index 0000000..03d70b1
--- /dev/null
+++ b/odb/odb/traversal/relational/model.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational/model.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_MODEL_HXX
+#define ODB_TRAVERSAL_RELATIONAL_MODEL_HXX
+
+#include <odb/semantics/relational/model.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct model: scope_template<semantics::relational::model> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_MODEL_HXX
diff --git a/odb/odb/traversal/relational/primary-key.hxx b/odb/odb/traversal/relational/primary-key.hxx
new file mode 100644
index 0000000..dcefab6
--- /dev/null
+++ b/odb/odb/traversal/relational/primary-key.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational/primary-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX
+#define ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX
+
+#include <odb/semantics/relational/primary-key.hxx>
+#include <odb/traversal/relational/key.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct primary_key: key_template<semantics::relational::primary_key> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX
diff --git a/odb/odb/traversal/relational/table.hxx b/odb/odb/traversal/relational/table.hxx
new file mode 100644
index 0000000..d80e37e
--- /dev/null
+++ b/odb/odb/traversal/relational/table.hxx
@@ -0,0 +1,21 @@
+// file : odb/traversal/relational/table.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_TABLE_HXX
+#define ODB_TRAVERSAL_RELATIONAL_TABLE_HXX
+
+#include <odb/semantics/relational/table.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct table: scope_template<semantics::relational::table> {};
+ struct add_table: scope_template<semantics::relational::add_table> {};
+ struct drop_table: node<semantics::relational::drop_table> {};
+ struct alter_table: scope_template<semantics::relational::alter_table> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_TABLE_HXX
diff --git a/odb/odb/traversal/template.cxx b/odb/odb/traversal/template.cxx
new file mode 100644
index 0000000..21a846d
--- /dev/null
+++ b/odb/odb/traversal/template.cxx
@@ -0,0 +1,53 @@
+// file : odb/traversal/template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/template.hxx>
+
+namespace traversal
+{
+ void instantiates::
+ traverse (type& i)
+ {
+ dispatch (i.template_ ());
+ }
+
+ //
+ //
+ void instantiation::
+ traverse (type& i)
+ {
+ instantiates (i);
+ }
+
+ void instantiation::
+ instantiates (type& i)
+ {
+ instantiates (i, *this);
+ }
+
+ void instantiation::
+ instantiates (type& i, edge_dispatcher& d)
+ {
+ d.dispatch (i.instantiates ());
+ }
+
+ //
+ //
+ void type_instantiation::
+ traverse (type& i)
+ {
+ instantiates (i);
+ }
+
+ void type_instantiation::
+ instantiates (type& i)
+ {
+ instantiates (i, *this);
+ }
+
+ void type_instantiation::
+ instantiates (type& i, edge_dispatcher& d)
+ {
+ d.dispatch (i.instantiates ());
+ }
+}
diff --git a/odb/odb/traversal/template.hxx b/odb/odb/traversal/template.hxx
new file mode 100644
index 0000000..28a64d5
--- /dev/null
+++ b/odb/odb/traversal/template.hxx
@@ -0,0 +1,56 @@
+// file : odb/traversal/template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_TEMPLATE_HXX
+#define ODB_TRAVERSAL_TEMPLATE_HXX
+
+#include <odb/semantics/template.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct instantiates: edge<semantics::instantiates>
+ {
+ instantiates ()
+ {
+ }
+
+ instantiates (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct template_: node<semantics::template_> {};
+
+ struct instantiation: node<semantics::instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+ };
+
+ struct type_template: node<semantics::type_template> {};
+
+ struct type_instantiation: node<semantics::type_instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_TEMPLATE_HXX
diff --git a/odb/odb/traversal/union-template.cxx b/odb/odb/traversal/union-template.cxx
new file mode 100644
index 0000000..9d72fcd
--- /dev/null
+++ b/odb/odb/traversal/union-template.cxx
@@ -0,0 +1,26 @@
+// file : odb/traversal/union-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/union-template.hxx>
+
+namespace traversal
+{
+ void union_instantiation::
+ traverse (type& u)
+ {
+ instantiates (u);
+ names (u);
+ }
+
+ void union_instantiation::
+ instantiates (type& u)
+ {
+ instantiates (u, *this);
+ }
+
+ void union_instantiation::
+ instantiates (type& u, edge_dispatcher& d)
+ {
+ d.dispatch (u.instantiates ());
+ }
+}
diff --git a/odb/odb/traversal/union-template.hxx b/odb/odb/traversal/union-template.hxx
new file mode 100644
index 0000000..10d1d49
--- /dev/null
+++ b/odb/odb/traversal/union-template.hxx
@@ -0,0 +1,29 @@
+// file : odb/traversal/union-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_UNION_TEMPLATE_HXX
+#define ODB_TRAVERSAL_UNION_TEMPLATE_HXX
+
+#include <odb/semantics/union-template.hxx>
+
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/union.hxx>
+
+namespace traversal
+{
+ struct union_template: scope_template<semantics::union_template> {};
+
+ struct union_instantiation: scope_template<semantics::union_instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_UNION_TEMPLATE_HXX
diff --git a/odb/odb/traversal/union.hxx b/odb/odb/traversal/union.hxx
new file mode 100644
index 0000000..2d1a7af
--- /dev/null
+++ b/odb/odb/traversal/union.hxx
@@ -0,0 +1,15 @@
+// file : odb/traversal/union.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_UNION_HXX
+#define ODB_TRAVERSAL_UNION_HXX
+
+#include <odb/semantics/union.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct union_: scope_template<semantics::union_> {};
+}
+
+#endif // ODB_TRAVERSAL_UNION_HXX
diff --git a/odb/odb/traversal/unit.hxx b/odb/odb/traversal/unit.hxx
new file mode 100644
index 0000000..a90418a
--- /dev/null
+++ b/odb/odb/traversal/unit.hxx
@@ -0,0 +1,15 @@
+// file : odb/traversal/unit.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_UNIT_HXX
+#define ODB_TRAVERSAL_UNIT_HXX
+
+#include <odb/semantics/unit.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct unit: scope_template<semantics::unit> {};
+}
+
+#endif // ODB_TRAVERSAL_UNIT_HXX
diff --git a/odb/odb/validator.cxx b/odb/odb/validator.cxx
new file mode 100644
index 0000000..bf9aa6b
--- /dev/null
+++ b/odb/odb/validator.cxx
@@ -0,0 +1,1912 @@
+// file : odb/validator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <set>
+#include <iostream>
+
+#include <odb/traversal.hxx>
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/diagnostics.hxx>
+#include <odb/validator.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/relational/validator.hxx>
+
+using namespace std;
+
+namespace
+{
+ // Resolve null overrides.
+ //
+ static void
+ override_null (semantics::node& n, string const& prefix = "")
+ {
+ string p (prefix.empty () ? prefix : prefix + '-');
+
+ if (n.count (p + "null") && n.count (p + "not-null"))
+ {
+ if (n.get<location_t> (p + "null-location") <
+ n.get<location_t> (p + "not-null-location"))
+ {
+ n.remove (p + "null");
+ n.remove (p + "null-location");
+ }
+ else
+ {
+ n.remove (p + "not-null");
+ n.remove (p + "not-null-location");
+ }
+ }
+ }
+
+ //
+ // Pass 1.
+ //
+
+ struct data_member1: traversal::data_member, context
+ {
+ data_member1 (bool& valid)
+ : valid_ (valid)
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+ bool obj (object (c));
+
+ // If the class is marked transient, then mark each non-virtual
+ // data member as transient.
+ //
+ {
+ bool t (transient (m));
+
+ if (!t && c.count ("transient") && !m.count ("virtual"))
+ {
+ m.set ("transient", true);
+ t = true;
+ }
+
+ if (t)
+ return;
+ }
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (t.fq_anonymous (hint))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unnamed type in data member declaration" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use 'typedef' to name this type" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure id or inverse member is not marked readonly since we
+ // depend on these three sets not having overlaps.
+ //
+ if (m.count ("readonly")) // context::readonly() also checks the class.
+ {
+ if (id (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: object id should not be declared readonly" << endl;
+
+ valid_ = false;
+ }
+
+ if (inverse (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse object pointer should not be declared "
+ << "readonly" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Make sure a member of a section is an immediate member of an object.
+ // The same for the section member itself.
+ //
+ bool section (t.fq_name () == "::odb::section");
+
+ if (!obj)
+ {
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: data member belonging to a section can only be a " <<
+ "direct member of a persistent class" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: section data member can only be a direct member of a " <<
+ "persistent class" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Make sure the load and update pragmas are only specified on
+ // section members.
+ //
+ if (!section)
+ {
+ if (m.count ("section-load"))
+ {
+ location_t loc (m.get<location_t> ("section-load-location"));
+ error (loc) << "'#pragma db load' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+
+ if (m.count ("section-update"))
+ {
+ location_t loc (m.get<location_t> ("section-update-location"));
+ error (loc) << "'#pragma db update' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Check that the addition version makes sense.
+ //
+ unsigned long long av (m.get<unsigned long long> ("added", 0));
+ if (av != 0)
+ {
+ location_t l (m.get<location_t> ("added-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "added data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (av > mv.current)
+ {
+ error (l) << "addition version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (av <= mv.base)
+ {
+ error (l) << "addition version is less than or equal to the " <<
+ "base model version" << endl;
+ info (l) << "delete this pragma since migration to version " <<
+ av << " is no longer possible" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check that the deletion version makes sense.
+ //
+ unsigned long long dv (m.get<unsigned long long> ("deleted", 0));
+ if (dv != 0)
+ {
+ location_t l (m.get<location_t> ("deleted-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "deleted data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (dv > mv.current)
+ {
+ error (l) << "deletion version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (dv <= mv.base)
+ {
+ error (l) << "deletion version is less than or equal to the " <<
+ "base model version" << endl;
+ info (c.location ()) << "delete this data member since " <<
+ "migration to version " << dv << " is no longer possible" <<
+ endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Make sure that addition and deletion versions are properly ordered.
+ // We can have both the [av, dv] (added then deleted) and [dv, av]
+ // (deleted then re-added) intervals.
+ //
+ if (av != 0 && dv != 0 && av == dv)
+ {
+ location_t al (m.get<location_t> ("added-location"));
+ location_t dl (m.get<location_t> ("deleted-location"));
+
+ error (al) << "addition and deletion versions are the same" << endl;
+ info (dl) << "deletion version is specified here" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ return; // Section data member is transient.
+
+ // Resolve null overrides.
+ //
+ override_null (m);
+ override_null (m, "value");
+ }
+
+ bool& valid_;
+ };
+
+ // Find special members (id, version).
+ //
+ struct special_members: traversal::class_, context
+ {
+ special_members (class_kind_type kind,
+ bool& valid,
+ semantics::data_member*& id,
+ semantics::data_member*& optimistic)
+ : kind_ (kind), member_ (valid, id, optimistic)
+ {
+ if (kind != class_view)
+ *this >> inherits_ >> *this;
+
+ *this >> names_ >> member_;
+ }
+
+ virtual void
+ traverse (semantics::class_& c)
+ {
+ // Skip transient bases.
+ //
+ switch (kind_)
+ {
+ case class_object:
+ {
+ if (!object (c))
+ return;
+ break;
+ }
+ case class_view:
+ {
+ break;
+ }
+ case class_composite:
+ {
+ if (!composite (c))
+ return;
+ break;
+ }
+ case class_other:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ // Views don't have bases.
+ //
+ if (kind_ != class_view)
+ inherits (c);
+
+ names (c);
+ }
+
+ private:
+ struct member: traversal::data_member, context
+ {
+ member (bool& valid,
+ semantics::data_member*& id,
+ semantics::data_member*& optimistic)
+ : valid_ (valid), id_ (id), optimistic_ (optimistic)
+ {
+ }
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (m.count ("id"))
+ {
+ if (id_ == 0)
+ id_ = &m;
+ else
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple object id members" << endl;
+
+ os << id_->file () << ":" << id_->line () << ":" << id_->column ()
+ << ": info: previous id member is declared here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ if (version (m))
+ {
+ if (optimistic_ == 0)
+ optimistic_ = &m;
+ else
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple version members" << endl;
+
+ semantics::data_member& o (*optimistic_);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": info: previous version member is declared here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ semantics::data_member*& id_;
+ semantics::data_member*& optimistic_;
+ };
+
+ class_kind_type kind_;
+ member member_;
+ traversal::names names_;
+ traversal::inherits inherits_;
+ };
+
+ //
+ //
+ struct value_type: traversal::type, context
+ {
+ virtual void
+ traverse (semantics::type& t)
+ {
+ // Resolve null overrides.
+ //
+ override_null (t);
+ override_null (t, "value");
+ }
+ };
+
+ struct typedefs1: typedefs
+ {
+ typedefs1 (traversal::declares& d)
+ : typedefs (true), declares_ (d)
+ {
+ }
+
+ void
+ traverse (semantics::typedefs& t)
+ {
+ if (check (t))
+ traversal::typedefs::traverse (t);
+ else
+ declares_.traverse (t);
+ }
+
+ private:
+ traversal::declares& declares_;
+ };
+
+ //
+ //
+ struct class1: traversal::class_, context
+ {
+ class1 (bool& valid)
+ : valid_ (valid), typedefs_ (declares_), member_ (valid)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ *this >> declares_ >> vt_;
+
+ names_member_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck != class_other)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (class_kind (b) == class_other)
+ continue;
+
+ location_t cl;
+ location_t bl;
+
+ // If we are in the same file, then use real locations (as
+ // opposed to definition locations) since that's the order
+ // in which we will be generating the code.
+ //
+ if (class_file (c) == class_file (b))
+ {
+ cl = class_real_location (c);
+ bl = class_real_location (b);
+ }
+ else
+ {
+ cl = class_location (c);
+ bl = class_location (b);
+ }
+
+ if (cl < bl)
+ {
+ // We cannot use class_name() for b since it might not have
+ // yet been processed (tree-hint).
+ //
+ error (cl) << "base class " << b.name () << " must "
+ << "be defined before derived class " << class_name (c)
+ << endl;
+
+ info (bl) << "class " << b.name () << " is define here"
+ << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ switch (ck)
+ {
+ case class_object:
+ names (c);
+ traverse_object (c);
+ break;
+ case class_view:
+ names (c);
+ traverse_view (c);
+ break;
+ case class_composite:
+ names (c);
+ traverse_composite (c);
+ // Fall through.
+ case class_other:
+ vt_.dispatch (c);
+ break;
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Check that the deletion version makes sense.
+ //
+ if (unsigned long long v = c.get<unsigned long long> ("deleted", 0))
+ {
+ location_t l (c.get<location_t> ("deleted-location"));
+
+ if (!versioned ())
+ {
+ error (l) << "deleted class in a non-versioned object model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (v > mv.current)
+ {
+ error (l) << "deletion version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (v <= mv.base)
+ {
+ error (l) << "deletion version is less than or equal to the " <<
+ "base model version" << endl;
+ info (c.location ()) << "delete this class since migration to " <<
+ "version " << v << " is no longer possible" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl != error_mark_node && TREE_CODE (decl) == BASELINK)
+ {
+ // Figure out if we have a const version of the callback. OVL_*
+ // macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ if (DECL_CONST_MEMFUNC_P (f))
+ {
+ c.set ("callback-const", true);
+ break;
+ }
+ }
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+ else
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check bases.
+ //
+ type* poly_root (0);
+
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ if (type* r = polymorphic (b))
+ {
+ if (poly_root == 0)
+ {
+ poly_root = r;
+ c.set ("polymorphic-base", &static_cast<semantics::class_&> (b));
+ }
+ // If poly_root and r are the same, then we have virtual
+ // inheritance. Though we don't support it at the moment.
+ //
+ else //if (poly_root != r)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: persistent class '" << class_name (c) << "' "
+ << "derives from multiple polymorphic bases" << endl;
+
+ type& a (*poly_root);
+ os << a.file () << ":" << a.line () << ":" << a.column () << ":"
+ << " info: first polymorphic base defined here" << endl;
+
+ type& b (*r);
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: second polymorphic base defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else if (view (b) || composite (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or value type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object types cannot derive from view or value "
+ << "types"
+ << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check special members.
+ //
+ using semantics::data_member;
+
+ data_member* id (0);
+ data_member* optimistic (0);
+ {
+ special_members t (class_object, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id == 0)
+ {
+ // An object without an id should either be reuse-abstract
+ // or explicitly marked as such. We check that it is not
+ // polymorphic below.
+ //
+ if (!(c.count ("id") || abstract (c)))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no data member designated as an object id" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db id' to specify an object id member"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: or explicitly declare that this persistent class "
+ << "has no object id with '#pragma db object no_id'" << endl;
+
+ valid_ = false;
+ }
+ }
+ else
+ {
+ // Convert id to a member path. This has to happen early since
+ // a lot of code that runs next (e.g., processor, pass 1) depends
+ // on this information being available.
+ //
+ data_member_path& idp (c.set ("id-member", data_member_path ()));
+ idp.push_back (id);
+
+ // See if we have a member path that we need to resolve.
+ //
+ const string& name (id->get<string> ("id"));
+ location_t l (id->get<location_t> ("id-location"));
+
+ if (!name.empty ())
+ {
+ if (id->count ("auto"))
+ {
+ error (l) << "nested id cannot be automatically assigned" << endl;
+ valid_ = false;
+ }
+
+ if (semantics::class_* comp = utype (*id).is_a<semantics::class_> ())
+ {
+ try
+ {
+ resolve_data_members (idp, *comp, name, l, lex_);
+ }
+ catch (const operation_failed&) {valid_ = false;}
+ }
+ else
+ {
+ error (l) << "nested id requires composite member" << endl;
+ valid_ = false;
+ }
+
+ // Mark the whole member as readonly.
+ //
+ id->set ("readonly", true);
+ }
+
+ data_member* idf (idp.front ());
+ data_member* idb (idp.back ());
+
+ // Complain if an id member has a default value (default value
+ // for the id's type is ok -- we will ignore it).
+ //
+ if (idb->count ("default"))
+ {
+ error (l) << "object id member cannot have default value" << endl;
+ valid_ = false;
+ }
+
+ // Complain if an id member is in a section.
+ //
+ if (idf->count ("section-member"))
+ {
+ error (l) << "object id member cannot be in a section" << endl;
+ valid_ = false;
+ }
+
+ // Automatically mark the id member as not null. If we already have
+ // an explicit null pragma for this member, issue an error.
+ //
+ if (idb->count ("null"))
+ {
+ error (l) << "object id member cannot be null" << endl;
+ valid_ = false;
+ }
+ else
+ idf->set ("not-null", true);
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& m (*optimistic);
+
+ // Make sure we have the class declared optimistic.
+ //
+ if (&m.scope () == &c && !c.count ("optimistic"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version data member in a class not declared "
+ << "optimistic" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db optimistic' to declare this "
+ << "class optimistic" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure we have object id.
+ //
+ if (id == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure id and version members are in the same class. The
+ // current architecture relies on that.
+ //
+ if (id != 0 && &id->scope () != &m.scope ())
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: object id and version members are in different "
+ << "classes" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object id and version members must be in the same "
+ << "class" << endl;
+
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": info: object id member is declared here" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version member is declared here" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure this class is not readonly.
+ //
+ if (readonly (c))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class cannot be readonly" << endl;
+
+ valid_ = false;
+ }
+
+ // Complain if the version member is in a section.
+ //
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: version member cannot be in a section" << endl;
+ valid_ = false;
+ }
+
+ // This takes care of also marking derived classes as optimistic.
+ //
+ c.set ("optimistic-member", optimistic);
+ }
+ else
+ {
+ // Make sure there is a version member if the class declared
+ // optimistic.
+ //
+ if (c.count ("optimistic"))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without a version member" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db version' to declare on of the "
+ << "data members as a version" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Polymorphic inheritance.
+ //
+ if (c.count ("polymorphic") && poly_root == 0)
+ {
+ // Root of the hierarchy.
+ //
+
+ if (id == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: polymorphic class without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (!TYPE_POLYMORPHIC_P (c.tree_node ()))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: non-polymorphic class (class without virtual "
+ << "functions) cannot be declared polymorphic" << endl;
+
+ valid_ = false;
+ }
+
+ poly_root = &c;
+ }
+
+ if (poly_root != 0)
+ c.set ("polymorphic-root", poly_root);
+
+ // Sectionable objects.
+ //
+ if (c.count ("sectionable"))
+ {
+ if (optimistic == 0)
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class can be sectionable" << endl;
+ valid_ = false;
+ }
+ else if (&optimistic->scope () != &c && poly_root != &c)
+ {
+ location l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class that declares the version " <<
+ "data member or that is a root of a polymorphic hierarchy can " <<
+ "be sectionable" << endl;
+ info (optimistic->location ()) << "version member is declared " <<
+ "here" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Update features set based on this object.
+ //
+ if (options.at_once () || class_file (c) == unit.file ())
+ {
+ if (poly_root != 0)
+ features.polymorphic_object = true;
+ else if (id == 0 && !abstract (c))
+ features.no_id_object = true;
+ else
+ features.simple_object = true;
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // Views require query support.
+ //
+ if (!options.generate_query ())
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: query support is required when using views"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use the --generate-query option to enable query "
+ << "support"
+ << endl;
+
+ valid_ = false;
+ }
+
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
+
+ valid_ = false;
+ }
+
+ // No const version for views.
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+
+ // Check bases.
+ //
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b) || view (b) || composite (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is an object, "
+ << "view, or value type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: view types cannot derive from view, object or "
+ << "value types"
+ << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check id.
+ //
+ semantics::data_member* id (0);
+ semantics::data_member* optimistic (0);
+ {
+ special_members t (class_view, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id != 0)
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: view type data member cannot be designated as an "
+ << "object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& o (*optimistic);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: view type data member cannot be designated as a "
+ << "version" << endl;
+
+ valid_ = false;
+ }
+
+ // Update features set based on this view.
+ //
+ if (options.at_once () || class_file (c) == unit.file ())
+ {
+ features.view = true;
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b) || view (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or object "
+ << "type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: composite value types cannot derive from object "
+ << "or view types" << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check id.
+ //
+ semantics::data_member* id (0);
+ semantics::data_member* optimistic (0);
+ {
+ special_members t (class_composite, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id != 0)
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: value type data member cannot be designated as an "
+ << "object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& o (*optimistic);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: value type data member cannot be designated as a "
+ << "version" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ bool& valid_;
+
+ traversal::defines defines_;
+ traversal::declares declares_;
+ typedefs1 typedefs_;
+
+ value_type vt_;
+ data_member1 member_;
+ traversal::names names_member_;
+
+ cxx_string_lexer lex_;
+ };
+
+ //
+ // Pass 2.
+ //
+
+ struct data_member2: traversal::data_member, context
+ {
+ data_member2 (bool& valid): valid_ (valid) {}
+
+ // Verify that composite value 'c' that is used by data member
+ // 'm' is defined before (or inside) class 's'.
+ //
+ void
+ verify_composite_location (semantics::class_& c,
+ semantics::class_& s,
+ semantics::data_member& m)
+ {
+ if (c.in_scope (s))
+ return;
+
+ location_t cl;
+ location_t sl;
+
+ // If we are in the same file, then use real locations (as
+ // opposed to definition locations) since that's the order
+ // in which we will be generating the code.
+ //
+ if (class_file (c) == class_file (s))
+ {
+ cl = class_real_location (c);
+ sl = class_real_location (s);
+ }
+ else
+ {
+ cl = class_location (c);
+ sl = class_location (s);
+ }
+
+ if (sl < cl)
+ {
+ const string& cn (class_name (c));
+
+ error (sl)
+ << "composite value type " << class_fq_name (c) << " must "
+ << "be defined before being used in class " << class_fq_name (s)
+ << endl;
+
+ info (m.location ())
+ << "data member " << m.name () << " uses " << cn << endl;
+
+ info (cl) << "class " << cn << " is define here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ using semantics::class_;
+
+ semantics::type& t (utype (m));
+ class_& s (dynamic_cast<class_&> (m.scope ()));
+
+ // Validate pointed-to objects.
+ //
+ class_* c;
+ if ((c = object_pointer (t)) || (c = points_to (m)))
+ {
+ bool poly (polymorphic (*c));
+ bool abst (abstract (*c));
+
+ // Make sure the pointed-to class is complete.
+ //
+ if (!c->complete ())
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "is incomplete" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is declared here"
+ << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: consider including its definition with the "
+ << "--odb-epilogue option" << endl;
+
+ valid_ = false;
+ return;
+ }
+
+ // Make sure the pointed-to class is not reuse-abstract.
+ //
+ if (abst && !poly)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "is abstract" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Make sure the pointed-to class has object id unless it is in a
+ // view where we can load no-id objects.
+ //
+ if (data_member_path* id = id_member (*c))
+ {
+ semantics::type& idt (utype (*id));
+
+ // Make sure composite id is defined before this pointer. Failed
+ // that we get non-obvious C++ compiler errors in generated code.
+ //
+ if (class_* comp = composite_wrapper (idt))
+ verify_composite_location (*comp, s, m);
+ }
+ else if (!view_member (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "has no object id" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ valid_ = false;
+ return;
+ }
+
+ // Make sure the pointed-to class has a default ctor. Since we will
+ // use database::load() in the generated code, lack of a default ctor
+ // will lead to uncompilable generated code. Poly-abstract is Ok.
+ //
+ if (!c->default_ctor () && !(abst && poly))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "has no default constructor" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ valid_ = false;
+ return;
+ }
+ }
+
+ // Make sure composite type is defined before (or inside)
+ // this class. Failed that we get non-obvious C++ compiler
+ // errors in generated code.
+ //
+ if (class_* comp = composite_wrapper (t))
+ verify_composite_location (*comp, s, m);
+
+ // Check that containers with composite value element types don't have
+ // any nested containers.
+ //
+ if (semantics::type* c = container (m))
+ {
+ class_* vt (0);
+ class_* kt (0);
+
+ switch (container_kind (*c))
+ {
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = composite_wrapper (container_kt (m));
+ }
+ // Fall through.
+ default:
+ {
+ vt = composite_wrapper (container_vt (m));
+ }
+ }
+
+ if (vt != 0 && has_a (*vt, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: containers of containers not supported" << endl;
+
+ os << vt->file () << ":" << vt->line () << ":" << vt->column ()
+ << ": info: composite element value " << class_fq_name (*vt)
+ << " contains container(s)" << endl;
+
+ valid_ = false;
+ }
+
+ if (kt != 0 && has_a (*kt, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: containers of containers not supported" << endl;
+
+ os << kt->file () << ":" << kt->line () << ":" << kt->column ()
+ << ": info: composite element key " << class_fq_name (*kt)
+ << " contains container(s)" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ };
+
+ // Make sure soft-delete versions make sense for dependent entities.
+ // We don't seem to need anything for soft-add since if an entity is
+ // not added (e.g., an object), then we cannot reference it in the
+ // C++ sense (e.g., a pointer).
+ //
+ struct version_dependencies: object_members_base
+ {
+ version_dependencies (bool& valid): valid_ (valid) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // For reuse inheritance we allow the base to be soft-deleted. In
+ // a sense, it becomes like an abstract class with the added
+ // functionality of being able to load old data.
+ //
+ // For polymorphism inheritance things are a lot more complicated
+ // and we don't allow a base to be soft-deleted since there is a
+ // link (foreign key) from the derived table.
+ //
+ semantics::class_* poly (polymorphic (c));
+ if (poly != 0 && poly != &c)
+ {
+ semantics::class_& base (polymorphic_base (c));
+ check_strict (
+ c, base, "polymorphic derived object", "polymorphic base");
+ }
+
+ // Don't go into bases.
+ //
+ names (c);
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+
+ switch (class_kind (c))
+ {
+ case class_object:
+ {
+ // Member shouldn't be deleted after the object that contains it.
+ //
+ check_lax (m, c, "data member", "object");
+ break;
+ }
+ // We leave it up to the user to make sure the view is consistent
+ // with the database schema it is being run on.
+ //
+ default:
+ break;
+ }
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (m != 0)
+ traverse_simple (*m); // Do simple value tests.
+ else
+ names (c); // Don't go into bases.
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ traverse_simple (m); // Do simple value tests.
+
+ // Pointer must be deleted before the pointed-to object.
+ //
+ check_strict (m, c, "object pointer", "pointed-to object");
+
+ // Inverse pointer must be deleted before the direct side.
+ //
+ if (data_member_path* imp = inverse (m))
+ check_strict (m, *imp, "inverse object pointer", "direct pointer");
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ traverse_simple (m); // Do simple value tests.
+
+ if (semantics::class_* c = object_pointer (container_vt (m)))
+ {
+ // Pointer must be deleted before the pointed-to object.
+ //
+ check_strict (m, *c, "object pointer", "pointed-to object");
+
+ // Inverse pointer must be deleted before the direct side.
+ //
+ if (data_member_path* imp = inverse (m, "value"))
+ check_strict (m, *imp, "inverse object pointer", "direct pointer");
+ }
+ }
+
+ private:
+ // Check for harmless things like a data member deleted after the
+ // object.
+ //
+ template <typename D, typename P>
+ void
+ check_lax (D& dep, P& pre, char const* dname, char const* pname)
+ {
+ unsigned long long dv (deleted (dep));
+ unsigned long long pv (deleted (pre));
+
+ if (pv == 0 || // Prerequisite never deleted.
+ dv == 0 || // Dependent never deleted.
+ dv <= pv) // Dependent deleted before prerequisite.
+ return;
+
+ location_t dl (dep.template get<location_t> ("deleted-location"));
+ location_t pl (pre.template get<location_t> ("deleted-location"));
+
+ error (dl) << dname << " is deleted after " << dname << endl;
+ info (pl) << pname << " deletion version is specified here" << endl;
+
+ valid_ = false;
+ }
+
+ // Check for harmful things that will result in an invalid database
+ // schema, like a pointer pointing to a deleted object.
+ //
+ template <typename D, typename P>
+ void
+ check_strict (D& dep, P& pre, char const* dname, char const* pname)
+ {
+ location_t dl (0), pl (0);
+ unsigned long long dv (deleted (dep, &dl));
+ unsigned long long pv (deleted (pre, &pl));
+
+ if (pv == 0) // Prerequisite never deleted.
+ return;
+
+ if (dv == 0) // Dependent never deleted.
+ {
+ location dl (dep.location ());
+
+ error (dl) << dname << " is not deleted" << endl;
+ info (pl) << pname << " is deleted here" << endl;
+
+ valid_ = false;
+ }
+ else if (pv > dv) // Prerequisite deleted before dependent.
+ {
+ error (dl) << dname << " is deleted after " << pname << endl;
+ info (pl) << pname << " deletion version is specified here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ private:
+ bool& valid_;
+ };
+
+ struct class2: traversal::class_, context
+ {
+ class2 (bool& valid)
+ : valid_ (valid), has_lt_operator_ (0), typedefs_ (true), member_ (valid)
+ {
+ // Find the has_lt_operator function template.
+ //
+ tree odb (
+ lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false));
+
+ if (odb != error_mark_node)
+ {
+ tree compiler (
+ lookup_qualified_name (
+ odb, get_identifier ("compiler"), false, false));
+
+ if (compiler != error_mark_node)
+ {
+ has_lt_operator_ = lookup_qualified_name (
+ compiler, get_identifier ("has_lt_operator"), false, false);
+
+ if (has_lt_operator_ != error_mark_node)
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ has_lt_operator_ = OVL_FIRST (has_lt_operator_);
+#else
+ has_lt_operator_ = OVL_CURRENT (has_lt_operator_);
+#endif
+ }
+ else
+ {
+ os << unit.file () << ": error: unable to resolve has_lt_operator "
+ << "function template inside odb::compiler" << endl;
+ has_lt_operator_ = 0;
+ }
+ }
+ else
+ os << unit.file () << ": error: unable to resolve compiler "
+ << "namespace inside odb" << endl;
+ }
+ else
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ if (has_lt_operator_ == 0)
+ valid_ = false;
+
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ names_member_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other)
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: return;
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check version dependencies.
+ //
+ {
+ version_dependencies vd (valid_);
+ vd.traverse (c);
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ bool abst (abstract (c));
+ bool poly (polymorphic (c));
+
+ // Make sure we have no empty or pointless sections unless we
+ // are reuse-abstract or polymorphic.
+ //
+ if (!poly && !abst)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ // Skip the special version update section (we always treat it
+ // as abstract in reuse inheritance).
+ //
+ if (s.special == user_section::special_version)
+ continue;
+
+ semantics::data_member& m (*s.member);
+ location const& l (m.location ());
+
+ if (s.total == 0 && !s.containers)
+ {
+ error (l) << "empty section" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ continue;
+ }
+
+ // Eager-loaded section with readonly members.
+ //
+ if (s.load == user_section::load_eager && s.update_empty ())
+ {
+ error (l) << "eager-loaded section with readonly members is " <<
+ "pointless" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ if (data_member_path* id = id_member (c))
+ {
+ semantics::type& t (utype (*id));
+
+ // If this is a session object, make sure that the id type can
+ // be compared.
+ //
+ if (session (c) && has_lt_operator_ != 0)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = t.tree_node ();
+
+ tree inst (
+ instantiate_template (
+ has_lt_operator_, args, tf_none));
+
+ bool v (inst != error_mark_node);
+
+ if (v &&
+ DECL_TEMPLATE_INSTANTIATION (inst) &&
+ !DECL_TEMPLATE_INSTANTIATED (inst))
+ {
+ // Instantiate this function template to see if the value type
+ // provides operator<. Unfortunately, GCC instantiate_decl()
+ // does not provide any control over the diagnostics it issues
+ // in case of an error. To work around this, we are going to
+ // temporarily redirect diagnostics to /dev/null, which is
+ // where asm_out_file points to (see plugin.cxx).
+ //
+ // Needless to say, this is very hacky and we should quickly fail
+ // (as we do below) if there were errors.
+ //
+ int ec (errorcount);
+ FILE* s (global_dc->printer->buffer->stream);
+ global_dc->printer->buffer->stream = asm_out_file;
+
+ instantiate_decl (inst, false, false);
+
+ global_dc->printer->buffer->stream = s;
+ v = (ec == errorcount);
+ }
+
+ if (!v)
+ {
+ semantics::data_member& idm (*id->front ());
+
+ os << t.file () << ":" << t.line () << ":" << t.column ()
+ << ": error: value type that is used as object id in "
+ << "persistent class with session support does not define "
+ << "the less than (<) comparison" << endl;
+
+ os << t.file () << ":" << t.line () << ":" << t.column ()
+ << ": info: provide operator< for this value type" << endl;
+
+ os << idm.file () << ":" << idm.line () << ":" << idm.column ()
+ << ": info: id member is defined here" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column ()
+ << ": info: persistent class is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else
+ {
+ // Make sure an object without id has no sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ if (!uss.empty ())
+ {
+ semantics::data_member& m (*uss.front ().member);
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: object without id cannot have sections" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Allow all the members to be deleted as long as there is no
+ // schema for this class.
+ //
+ column_count_type const& cc (column_count (c));
+ size_t cont (has_a (c, test_container));
+ size_t dcont (cont - has_a (c, test_container | exclude_deleted));
+
+ if ((cc.total == 0 && cont == 0) ||
+ (cc.total == cc.deleted && cont == dcont && !(abst || deleted (c))))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // We don't check for the column count here since we may want to
+ // allow certain kinds of empty views. Instead, this is handled
+ // in relational::validation.
+
+ // See if any of the associated objects are polymorphic. If so,
+ // we need to include polymorphism-specific headers. Also, if the
+ // view is loading the object, then we will need the corresponding
+ // statements.
+ //
+ if (c.count ("objects"))
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind == view_object::object)
+ {
+ if (polymorphic (*i->obj))
+ features.polymorphic_object = true;
+ else if (i->ptr != 0)
+ {
+ (id_member (*i->obj) != 0
+ ? features.simple_object
+ : features.no_id_object) = true;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ // Allow all the members to be deleted.
+ //
+ column_count_type const& cc (column_count (c));
+ size_t cont (has_a (c, test_container));
+
+ if (cc.total == 0 && cont == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+
+ bool& valid_;
+ tree has_lt_operator_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member2 member_;
+ traversal::names names_member_;
+ };
+}
+
+void
+validate (options const& ops,
+ features& f,
+ semantics::unit& u,
+ semantics::path const& p,
+ unsigned short pass)
+{
+ bool valid (true);
+ database db (ops.database ()[0]);
+
+ // Validate options.
+ //
+ if (ops.generate_schema_only () &&
+ ops.schema_format ()[db].count (schema_format::embedded))
+ {
+ cerr << "error: --generate-schema-only is only valid when generating " <<
+ "schema as a standalone SQL or separate C++ file" << endl;
+ valid = false;
+ }
+
+ // Multi-database support options.
+ //
+ if (ops.multi_database () == multi_database::dynamic &&
+ ops.default_database_specified () &&
+ ops.default_database () != database::common)
+ {
+ cerr << "error: when dynamic multi-database support is used, the " <<
+ "default database can only be 'common'" << endl;
+ valid = false;
+ }
+
+ if (db == database::common &&
+ ops.multi_database () == multi_database::disabled)
+ {
+ cerr << "error: 'common' database is only valid with multi-database " <<
+ "support enabled" << endl;
+ valid = false;
+ }
+
+ // Changelog options.
+ //
+ if (ops.changelog_in ().count (db) != ops.changelog_out ().count (db))
+ {
+ cerr << "error: both --changelog-in and --changelog-out must be " <<
+ "specified" << endl;
+ valid = false;
+ }
+
+ if (!valid)
+ throw validator_failed ();
+
+ unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ if (pass == 1)
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::declares unit_declares;
+ typedefs1 unit_typedefs (unit_declares);
+ traversal::namespace_ ns;
+ value_type vt;
+ class1 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_declares >> vt;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ traversal::declares ns_declares;
+ typedefs1 ns_typedefs (ns_declares);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_declares >> vt;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+
+ if (!valid)
+ throw validator_failed ();
+
+ switch (db)
+ {
+ case database::common:
+ {
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ try
+ {
+ relational::validate (ops, f, u, p, pass);
+ }
+ catch (operation_failed const&)
+ {
+ throw validator_failed ();
+ }
+
+ break;
+ }
+ }
+}
diff --git a/odb/odb/validator.hxx b/odb/odb/validator.hxx
new file mode 100644
index 0000000..d0aeac9
--- /dev/null
+++ b/odb/odb/validator.hxx
@@ -0,0 +1,23 @@
+// file : odb/validator.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_VALIDATOR_HXX
+#define ODB_VALIDATOR_HXX
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+class validator_failed {};
+
+// The first pass is performed after processing pass 1 but before
+// processing pass 2. The second -- after pass 2.
+//
+void
+validate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&,
+ unsigned short pass);
+
+#endif // ODB_VALIDATOR_HXX
diff --git a/odb/odb/version.hxx b/odb/odb/version.hxx
new file mode 100644
index 0000000..4ad389a
--- /dev/null
+++ b/odb/odb/version.hxx
@@ -0,0 +1,37 @@
+// file : odb/version.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_VERSION_HXX
+#define ODB_VERSION_HXX
+
+// Version format is AABBCCDD where
+//
+// AA - major version number
+// BB - minor version number
+// CC - bugfix version number
+// DD - alpha / beta (DD + 50) version number
+//
+// When DD is not 00, 1 is subtracted from AABBCC. For example:
+//
+// Version AABBCCDD
+// 2.0.0 02000000
+// 2.1.0 02010000
+// 2.1.1 02010100
+// 2.2.0.a1 02019901
+// 3.0.0.b2 02999952
+//
+
+// ODB interface version: minor, major, and alpha/beta versions.
+//
+#define ODB_VERSION 20476
+#define ODB_VERSION_STR "2.5-b.26"
+
+// ODB compiler version: interface version plus the bugfix version.
+//
+// NOTE: remember to update metadata to full version when switching to
+// version.hxx.in.
+//
+#define ODB_COMPILER_VERSION 2049976
+#define ODB_COMPILER_VERSION_STR "2.5.0-b.26"
+
+#endif // ODB_VERSION_HXX
diff --git a/odb/tests/.gitignore b/odb/tests/.gitignore
new file mode 100644
index 0000000..35ec43f
--- /dev/null
+++ b/odb/tests/.gitignore
@@ -0,0 +1,2 @@
+test/
+test-*/
diff --git a/odb/tests/build/.gitignore b/odb/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/odb/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/odb/tests/build/bootstrap.build b/odb/tests/build/bootstrap.build
new file mode 100644
index 0000000..5e48571
--- /dev/null
+++ b/odb/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v3; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/odb/tests/build/root.build b/odb/tests/build/root.build
new file mode 100644
index 0000000..0995b7b
--- /dev/null
+++ b/odb/tests/build/root.build
@@ -0,0 +1,16 @@
+# file : tests/build/root.build
+# license : GNU GPL v3; see accompanying LICENSE file
+
+# We need to configure C++ module to obtain the target information (see
+# below).
+#
+using cxx.config
+
+# Import the ODB compiler we will be testing.
+#
+import! [metadata] odb = odb%exe{odb}
+testscript{*}: test = $odb
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/odb/tests/buildfile b/odb/tests/buildfile
new file mode 100644
index 0000000..80a4e67
--- /dev/null
+++ b/odb/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v3; see accompanying LICENSE file
+
+./: testscript $odb
diff --git a/odb/tests/testscript b/odb/tests/testscript
new file mode 100644
index 0000000..5448eca
--- /dev/null
+++ b/odb/tests/testscript
@@ -0,0 +1,21 @@
+# file : tests/testscript
+# license : GNU GPL v3; see accompanying LICENSE file
+
+: version
+:
+$* --version >>~/EOO/
+/ODB object-relational mapping \(ORM\) compiler for C\+\+ .+/
+/.*/*
+EOO
+
+: plugin
+:
+: To actually compile anything we need libodb. However, we can smoke-test the
+: plugin loading and invocation using the --trace option that suppresses the
+: standard prologue/epilogue (which includes the libodb headers).
+:
+cat <'// dummy' >=dummy.hxx;
+$* -d sqlite --trace dummy.hxx 2>>~/EOE/ != 0
+/.*/*
+dummy.hxx: error: unable to resolve odb namespace
+EOE
diff --git a/packages.manifest b/packages.manifest
new file mode 100644
index 0000000..411d17c
--- /dev/null
+++ b/packages.manifest
@@ -0,0 +1,2 @@
+: 1
+location: odb/
diff --git a/repositories.manifest b/repositories.manifest
new file mode 100644
index 0000000..e0c2961
--- /dev/null
+++ b/repositories.manifest
@@ -0,0 +1,14 @@
+: 1
+summary: ODB compiler repository
+
+:
+role: prerequisite
+location: https://git.codesynthesis.com/libcutl/libcutl.git##HEAD
+
+:
+role: prerequisite
+location: https://git.codesynthesis.com/libstudxml/libstudxml.git##HEAD
+
+:
+role: prerequisite
+location: https://git.codesynthesis.com/cli/cli.git##HEAD