diff options
Diffstat (limited to 'odb')
-rw-r--r-- | odb/.gitignore | 28 | ||||
-rw-r--r-- | odb/GPLv3 | 674 | ||||
-rw-r--r-- | odb/INSTALL | 36 | ||||
-rw-r--r-- | odb/LICENSE | 21 | ||||
-rw-r--r-- | odb/Makefile.am | 34 | ||||
l--------- | odb/NEWS | 1 | ||||
-rw-r--r-- | odb/README | 20 | ||||
-rw-r--r-- | odb/build/.gitignore | 3 | ||||
-rw-r--r-- | odb/build/bootstrap.build | 10 | ||||
-rw-r--r-- | odb/build/export.build | 9 | ||||
-rw-r--r-- | odb/build/root.build | 110 | ||||
-rw-r--r-- | odb/buildfile | 140 | ||||
-rw-r--r-- | odb/doc/.gitignore | 5 | ||||
-rw-r--r-- | odb/doc/buildfile | 191 | ||||
-rw-r--r-- | odb/doc/default.css | 323 | ||||
-rw-r--r-- | odb/doc/manual.html2ps | 69 | ||||
-rw-r--r-- | odb/doc/manual.xhtml | 27073 | ||||
-rw-r--r-- | odb/doc/odb-arch.png | bin | 0 -> 16883 bytes | |||
-rw-r--r-- | odb/doc/odb-arch.svg | 410 | ||||
-rw-r--r-- | odb/doc/odb-epilogue.1 | 153 | ||||
-rw-r--r-- | odb/doc/odb-epilogue.xhtml | 126 | ||||
-rw-r--r-- | odb/doc/odb-flow.png | bin | 0 -> 39092 bytes | |||
-rw-r--r-- | odb/doc/odb-flow.svg | 822 | ||||
-rw-r--r-- | odb/doc/odb-prologue.1 | 84 | ||||
-rw-r--r-- | odb/doc/odb-prologue.xhtml | 88 | ||||
-rw-r--r-- | odb/doc/pregenerated/odb.1 | 799 | ||||
-rw-r--r-- | odb/doc/pregenerated/odb.xhtml | 978 | ||||
-rw-r--r-- | odb/makefile | 317 | ||||
-rw-r--r-- | odb/manifest | 34 | ||||
-rw-r--r-- | odb/odb/.gitignore | 2 | ||||
-rw-r--r-- | odb/odb/buildfile | 172 | ||||
-rw-r--r-- | odb/odb/common-query.cxx (renamed from odb/common-query.cxx) | 41 | ||||
-rw-r--r-- | odb/odb/common-query.hxx (renamed from odb/common-query.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/common.cxx (renamed from odb/common.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/common.hxx (renamed from odb/common.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/context.cxx (renamed from odb/context.cxx) | 118 | ||||
-rw-r--r-- | odb/odb/context.hxx (renamed from odb/context.hxx) | 12 | ||||
-rw-r--r-- | odb/odb/context.ixx (renamed from odb/context.ixx) | 0 | ||||
-rw-r--r-- | odb/odb/cxx-lexer.cxx (renamed from odb/cxx-lexer.cxx) | 4 | ||||
-rw-r--r-- | odb/odb/cxx-lexer.hxx (renamed from odb/cxx-lexer.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/cxx-token.hxx (renamed from odb/cxx-token.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/diagnostics.cxx (renamed from odb/diagnostics.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/diagnostics.hxx (renamed from odb/diagnostics.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/emitter.cxx (renamed from odb/emitter.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/emitter.hxx (renamed from odb/emitter.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/features.hxx (renamed from odb/features.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/gcc-fwd.hxx (renamed from odb/gcc-fwd.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/gcc.hxx (renamed from odb/gcc.hxx) | 20 | ||||
-rw-r--r-- | odb/odb/generate.hxx (renamed from odb/generate.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/generator.cxx (renamed from odb/generator.cxx) | 22 | ||||
-rw-r--r-- | odb/odb/generator.hxx (renamed from odb/generator.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/header.cxx (renamed from odb/header.cxx) | 4 | ||||
-rw-r--r-- | odb/odb/include.cxx (renamed from odb/include.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/inline.cxx (renamed from odb/inline.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/instance.cxx (renamed from odb/instance.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/instance.hxx (renamed from odb/instance.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/location.cxx (renamed from odb/location.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/location.hxx (renamed from odb/location.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/lookup.cxx (renamed from odb/lookup.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/lookup.hxx (renamed from odb/lookup.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/odb.cxx (renamed from odb/odb.cxx) | 336 | ||||
-rw-r--r-- | odb/odb/option-functions.cxx (renamed from odb/option-functions.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/option-functions.hxx (renamed from odb/option-functions.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/option-parsers.hxx (renamed from odb/option-parsers.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/option-types.cxx (renamed from odb/option-types.cxx) | 5 | ||||
-rw-r--r-- | odb/odb/option-types.hxx (renamed from odb/option-types.hxx) | 3 | ||||
-rw-r--r-- | odb/odb/options.cli (renamed from odb/options.cli) | 6 | ||||
-rw-r--r-- | odb/odb/parser.cxx (renamed from odb/parser.cxx) | 88 | ||||
-rw-r--r-- | odb/odb/parser.hxx (renamed from odb/parser.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/plugin.cxx (renamed from odb/plugin.cxx) | 4 | ||||
-rw-r--r-- | odb/odb/pragma.cxx (renamed from odb/pragma.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/pragma.hxx (renamed from odb/pragma.hxx) | 6 | ||||
-rw-r--r-- | odb/odb/pregenerated/odb/options.cxx (renamed from odb/options.cxx) | 215 | ||||
-rw-r--r-- | odb/odb/pregenerated/odb/options.hxx (renamed from odb/options.hxx) | 50 | ||||
-rw-r--r-- | odb/odb/pregenerated/odb/options.ixx (renamed from odb/options.ixx) | 461 | ||||
-rw-r--r-- | odb/odb/processor.cxx (renamed from odb/processor.cxx) | 60 | ||||
-rw-r--r-- | odb/odb/processor.hxx (renamed from odb/processor.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/profile.cxx (renamed from odb/profile.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/profile.hxx (renamed from odb/profile.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/relational/changelog.cxx (renamed from odb/relational/changelog.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/common-query.cxx (renamed from odb/relational/common-query.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/common-query.hxx (renamed from odb/relational/common-query.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/common.cxx (renamed from odb/relational/common.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/common.hxx (renamed from odb/relational/common.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/common.txx (renamed from odb/relational/common.txx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/context.cxx (renamed from odb/relational/context.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/context.hxx (renamed from odb/relational/context.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/context.ixx (renamed from odb/relational/context.ixx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/generate.hxx (renamed from odb/relational/generate.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/relational/header.cxx (renamed from odb/relational/header.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/header.hxx (renamed from odb/relational/header.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/relational/inline.cxx (renamed from odb/relational/inline.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/inline.hxx (renamed from odb/relational/inline.hxx) | 4 | ||||
-rw-r--r-- | odb/odb/relational/model.cxx (renamed from odb/relational/model.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/model.hxx (renamed from odb/relational/model.hxx) | 8 | ||||
-rw-r--r-- | odb/odb/relational/mssql/common.cxx (renamed from odb/relational/mssql/common.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/common.hxx (renamed from odb/relational/mssql/common.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/context.cxx (renamed from odb/relational/mssql/context.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/context.hxx (renamed from odb/relational/mssql/context.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/header.cxx (renamed from odb/relational/mssql/header.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/inline.cxx (renamed from odb/relational/mssql/inline.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/model.cxx (renamed from odb/relational/mssql/model.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/schema.cxx (renamed from odb/relational/mssql/schema.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mssql/source.cxx (renamed from odb/relational/mssql/source.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/common.cxx (renamed from odb/relational/mysql/common.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/common.hxx (renamed from odb/relational/mysql/common.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/context.cxx (renamed from odb/relational/mysql/context.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/context.hxx (renamed from odb/relational/mysql/context.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/header.cxx (renamed from odb/relational/mysql/header.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/inline.cxx (renamed from odb/relational/mysql/inline.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/model.cxx (renamed from odb/relational/mysql/model.cxx) | 34 | ||||
-rw-r--r-- | odb/odb/relational/mysql/schema.cxx (renamed from odb/relational/mysql/schema.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/mysql/source.cxx (renamed from odb/relational/mysql/source.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/common.cxx (renamed from odb/relational/oracle/common.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/common.hxx (renamed from odb/relational/oracle/common.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/context.cxx (renamed from odb/relational/oracle/context.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/context.hxx (renamed from odb/relational/oracle/context.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/header.cxx (renamed from odb/relational/oracle/header.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/inline.cxx (renamed from odb/relational/oracle/inline.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/model.cxx (renamed from odb/relational/oracle/model.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/schema.cxx (renamed from odb/relational/oracle/schema.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/oracle/source.cxx (renamed from odb/relational/oracle/source.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/common.cxx (renamed from odb/relational/pgsql/common.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/common.hxx (renamed from odb/relational/pgsql/common.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/context.cxx (renamed from odb/relational/pgsql/context.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/context.hxx (renamed from odb/relational/pgsql/context.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/header.cxx (renamed from odb/relational/pgsql/header.cxx) | 14 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/inline.cxx (renamed from odb/relational/pgsql/inline.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/model.cxx (renamed from odb/relational/pgsql/model.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/schema.cxx (renamed from odb/relational/pgsql/schema.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/pgsql/source.cxx (renamed from odb/relational/pgsql/source.cxx) | 6 | ||||
-rw-r--r-- | odb/odb/relational/processor.cxx (renamed from odb/relational/processor.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/relational/processor.hxx (renamed from odb/relational/processor.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/schema-source.cxx (renamed from odb/relational/schema-source.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/schema-source.hxx (renamed from odb/relational/schema-source.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/schema.cxx (renamed from odb/relational/schema.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/schema.hxx (renamed from odb/relational/schema.hxx) | 3 | ||||
-rw-r--r-- | odb/odb/relational/source.cxx (renamed from odb/relational/source.cxx) | 139 | ||||
-rw-r--r-- | odb/odb/relational/source.hxx (renamed from odb/relational/source.hxx) | 20 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/common.cxx (renamed from odb/relational/sqlite/common.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/common.hxx (renamed from odb/relational/sqlite/common.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/context.cxx (renamed from odb/relational/sqlite/context.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/context.hxx (renamed from odb/relational/sqlite/context.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/header.cxx (renamed from odb/relational/sqlite/header.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/inline.cxx (renamed from odb/relational/sqlite/inline.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/model.cxx (renamed from odb/relational/sqlite/model.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/schema.cxx (renamed from odb/relational/sqlite/schema.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/sqlite/source.cxx (renamed from odb/relational/sqlite/source.cxx) | 3 | ||||
-rw-r--r-- | odb/odb/relational/validator.cxx (renamed from odb/relational/validator.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/relational/validator.hxx (renamed from odb/relational/validator.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics.hxx (renamed from odb/semantics.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/class-template.cxx (renamed from odb/semantics/class-template.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/class-template.hxx (renamed from odb/semantics/class-template.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/class.cxx (renamed from odb/semantics/class.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/class.hxx (renamed from odb/semantics/class.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/derived.cxx (renamed from odb/semantics/derived.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/derived.hxx (renamed from odb/semantics/derived.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/elements.cxx (renamed from odb/semantics/elements.cxx) | 6 | ||||
-rw-r--r-- | odb/odb/semantics/elements.hxx (renamed from odb/semantics/elements.hxx) | 10 | ||||
-rw-r--r-- | odb/odb/semantics/elements.ixx (renamed from odb/semantics/elements.ixx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/enum.cxx (renamed from odb/semantics/enum.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/enum.hxx (renamed from odb/semantics/enum.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/fundamental.cxx (renamed from odb/semantics/fundamental.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/fundamental.hxx (renamed from odb/semantics/fundamental.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/namespace.cxx (renamed from odb/semantics/namespace.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/namespace.hxx (renamed from odb/semantics/namespace.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational.hxx (renamed from odb/semantics/relational.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/changelog.cxx (renamed from odb/semantics/relational/changelog.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/changelog.hxx (renamed from odb/semantics/relational/changelog.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/changeset.cxx (renamed from odb/semantics/relational/changeset.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/changeset.hxx (renamed from odb/semantics/relational/changeset.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/column.cxx (renamed from odb/semantics/relational/column.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/column.hxx (renamed from odb/semantics/relational/column.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/deferrable.cxx (renamed from odb/semantics/relational/deferrable.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/deferrable.hxx (renamed from odb/semantics/relational/deferrable.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/elements.cxx (renamed from odb/semantics/relational/elements.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/elements.hxx (renamed from odb/semantics/relational/elements.hxx) | 12 | ||||
-rw-r--r-- | odb/odb/semantics/relational/elements.txx (renamed from odb/semantics/relational/elements.txx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/foreign-key.cxx (renamed from odb/semantics/relational/foreign-key.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/foreign-key.hxx (renamed from odb/semantics/relational/foreign-key.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/index.cxx (renamed from odb/semantics/relational/index.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/index.hxx (renamed from odb/semantics/relational/index.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/key.cxx (renamed from odb/semantics/relational/key.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/key.hxx (renamed from odb/semantics/relational/key.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/model.cxx (renamed from odb/semantics/relational/model.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/model.hxx (renamed from odb/semantics/relational/model.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/name.cxx (renamed from odb/semantics/relational/name.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/name.hxx (renamed from odb/semantics/relational/name.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/primary-key.cxx (renamed from odb/semantics/relational/primary-key.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/primary-key.hxx (renamed from odb/semantics/relational/primary-key.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/relational/table.cxx (renamed from odb/semantics/relational/table.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/relational/table.hxx (renamed from odb/semantics/relational/table.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/template.cxx (renamed from odb/semantics/template.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/template.hxx (renamed from odb/semantics/template.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/union-template.cxx (renamed from odb/semantics/union-template.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/union-template.hxx (renamed from odb/semantics/union-template.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/union.cxx (renamed from odb/semantics/union.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/union.hxx (renamed from odb/semantics/union.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/semantics/unit.cxx (renamed from odb/semantics/unit.cxx) | 2 | ||||
-rw-r--r-- | odb/odb/semantics/unit.hxx (renamed from odb/semantics/unit.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/source.cxx (renamed from odb/source.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/sql-lexer.cxx (renamed from odb/sql-lexer.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/sql-lexer.hxx (renamed from odb/sql-lexer.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/sql-lexer.ixx (renamed from odb/sql-lexer.ixx) | 0 | ||||
-rw-r--r-- | odb/odb/sql-token.cxx (renamed from odb/sql-token.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/sql-token.hxx (renamed from odb/sql-token.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/sql-token.ixx (renamed from odb/sql-token.ixx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal.hxx (renamed from odb/traversal.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/class-template.cxx (renamed from odb/traversal/class-template.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/class-template.hxx (renamed from odb/traversal/class-template.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/class.cxx (renamed from odb/traversal/class.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/class.hxx (renamed from odb/traversal/class.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/derived.cxx (renamed from odb/traversal/derived.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/derived.hxx (renamed from odb/traversal/derived.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/elements.cxx (renamed from odb/traversal/elements.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/elements.hxx (renamed from odb/traversal/elements.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/traversal/enum.cxx (renamed from odb/traversal/enum.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/enum.hxx (renamed from odb/traversal/enum.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/fundamental.hxx (renamed from odb/traversal/fundamental.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/namespace.hxx (renamed from odb/traversal/namespace.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational.hxx (renamed from odb/traversal/relational.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/changelog.cxx (renamed from odb/traversal/relational/changelog.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/changelog.hxx (renamed from odb/traversal/relational/changelog.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/changeset.hxx (renamed from odb/traversal/relational/changeset.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/column.hxx (renamed from odb/traversal/relational/column.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/elements.hxx (renamed from odb/traversal/relational/elements.hxx) | 2 | ||||
-rw-r--r-- | odb/odb/traversal/relational/foreign-key.hxx (renamed from odb/traversal/relational/foreign-key.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/index.hxx (renamed from odb/traversal/relational/index.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/key.cxx (renamed from odb/traversal/relational/key.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/key.hxx (renamed from odb/traversal/relational/key.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/model.hxx (renamed from odb/traversal/relational/model.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/primary-key.hxx (renamed from odb/traversal/relational/primary-key.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/relational/table.hxx (renamed from odb/traversal/relational/table.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/template.cxx (renamed from odb/traversal/template.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/template.hxx (renamed from odb/traversal/template.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/union-template.cxx (renamed from odb/traversal/union-template.cxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/union-template.hxx (renamed from odb/traversal/union-template.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/union.hxx (renamed from odb/traversal/union.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/traversal/unit.hxx (renamed from odb/traversal/unit.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/validator.cxx (renamed from odb/validator.cxx) | 5 | ||||
-rw-r--r-- | odb/odb/validator.hxx (renamed from odb/validator.hxx) | 0 | ||||
-rw-r--r-- | odb/odb/version.hxx | 0 | ||||
-rw-r--r-- | odb/odb/version.hxx.in | 54 | ||||
-rw-r--r-- | odb/tests/.gitignore | 2 | ||||
-rw-r--r-- | odb/tests/build/.gitignore | 3 | ||||
-rw-r--r-- | odb/tests/build/bootstrap.build | 8 | ||||
-rw-r--r-- | odb/tests/build/root.build | 16 | ||||
-rw-r--r-- | odb/tests/buildfile | 4 | ||||
-rw-r--r-- | odb/tests/testscript | 21 | ||||
-rw-r--r-- | odb/version.hxx | 37 |
250 files changed, 33593 insertions, 1067 deletions
diff --git a/odb/.gitignore b/odb/.gitignore index 7e97b78..1c363a0 100644 --- a/odb/.gitignore +++ b/odb/.gitignore @@ -1,3 +1,25 @@ -odb -odb.so -#options.?xx +# 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..8f76d96 --- /dev/null +++ b/odb/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2009-2024 Code Synthesis. + +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/Makefile.am b/odb/Makefile.am deleted file mode 100644 index 04d8ccf..0000000 --- a/odb/Makefile.am +++ /dev/null @@ -1,34 +0,0 @@ -# file : odb/Makefile.am -# license : GNU GPL v3; see accompanying LICENSE file - -plugindir = @plugindir@ - -bin_PROGRAMS = odb -plugin_LTLIBRARIES = odb.la - -AM_CPPFLAGS = -I'$(top_builddir)' -I'$(top_srcdir)' - -# Note: not passed by libtool when linking odb.so. Seems to be harmless for -# now. -# -AM_CXXFLAGS = -std=c++0x - -EXTRA_DIST = __path__(headers) __path__(extra_dist) - -# Plugin. -# -odb_la_SOURCES = __path__(plugin_sources) __path__(common_sources) -odb_la_LDFLAGS = -module -shrext .so -avoid-version - -# Remove the .la file from the final install. -# -install-data-hook: - rm -f '$(DESTDIR)$(plugindir)/odb.la' - -# Driver. -# -odb_SOURCES = __path__(driver_sources) __path__(common_sources) - -# Make sure common sources are compiled differently. -# -odb_CXXFLAGS = $(AM_CXXFLAGS) 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 index 8e931ad..78b5d08 100644 --- a/odb/buildfile +++ b/odb/buildfile @@ -1,139 +1,9 @@ -# file : odb/buildfile +# file : buildfile # license : GNU GPL v3; see accompanying LICENSE file -define plugin: libs +./: {*/ -build/ -m4/} doc{INSTALL NEWS README} legal{GPLv3 LICENSE} manifest -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/ - -# Unless cross-compiling, pass the C++ compiler's recall path as the g++ -# name. At some point we should also make it configurable. -# -# 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) - gxx_name = $regex.replace($gxx_name, '\\', '\\\\') # Escape back slashes. -} -else - gxx_name = g++ - -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. -# -exe{odb}: -{ - export.metadata = 1 odb - odb.name = [string] odb - odb.version = $version - odb.checksum = $version -} - -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} {hxx ixx cxx}{options} $libs - -# Build options. -# -cxx.poptions += "-I$plugin_dir/include" "-DODB_GXX_NAME=\"$gxx_name\"" -cxx.poptions += -DODB_BUILD2 # @@ TMP while supporting other build systems. - -# Pass the copyright notice extracted from the LICENSE file. -# -copyright = $process.run_regex(cat $src_root/LICENSE, \ - 'Copyright \(c\) (.+)\.', \ - '\1') - -obj{odb}: cxx.poptions += -DODB_COPYRIGHT=\"$copyright\" - -# Generated options parser. -# -# @@ TMP: currently generated code is committed to allow building from git. -# -if $cli.configured -{ - cli.cxx{options}: cli{options} - - cli.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.cxx{*}: - { - # Include the generated cli files into the distribution and don't remove - # them when cleaning in src (so that clean results in a state identical to - # distributed). - # - dist = true - clean = ($src_root != $out_root) - - # We keep the generated code in the repository so copy it back to src in - # case of a forwarded configuration. - # - backlink = overwrite - } -} - -# Don't install any of the plugin's headers. +# Don't install tests or the INSTALL file. # -{hxx ixx txx}{*}: install = false +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..65f0c9d --- /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> </h1> + <h1> </h1> + <h1> </h1> + <h1> </h1> + <h1> </h1> + <h1> </h1> + <h1> </h1> + <h1> </h1> +</div> + <p>Copyright © 2009-2020 Code Synthesis.</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..a004cd7 --- /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="© 2009-2020 Code Synthesis"/> + <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 © 2009-2020 Code Synthesis</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 + <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-<database></code>, where <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 <string> + +class person +{ +public: + person (const std::string& first, + const std::string& last, + unsigned short age); + + const std::string& first () const; + const std::string& 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 <string> + +#include <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><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 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 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& email () const; + void email (const std::string&); + + const std::string& get_name () const; + std::string& 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 < 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 <memory> // std::auto_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <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<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& e) + { + cerr << e.what () << 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><odb/database.hxx></code> + and <code><odb/transaction.hxx></code> which define database + system-independent <code>odb::database</code> and + <code>odb::transaction</code> interfaces. Then we include + <code><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 < 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<person> query; + typedef odb::result<person> result; + + // Say hello to those over 30. + // + { + transaction t (db->begin ()); + + result r (db->query<person> (query::age > 30)); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + cout << "Hello, " << i->first () << "!" << endl; + } + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << 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 < 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 << "Hello, " << i->first () << " (" << i->id () << ")!" << 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<person> joe (db->load<person> (joe_id)); + joe->age (joe->age () + 1); + db->update (*joe); + + t.commit (); + } + + // Say hello to those over 30. + // + { + ... + } + } + catch (const odb::exception& e) + { + cerr << e.what () << 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 < 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<person> joe ( + db->query_one<person> (query::first == "Joe" && + 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<person_stat> ()); + + cout << "count : " << ps.count << endl + << "min age: " << ps.min_age << endl + << "max age: " << ps.max_age << 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<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<person> john ( + db->query_one<person> (query::first == "John" && + 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 < 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 < 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::<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<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::<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 <string> +#include <memory> // std::auto_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <odb/mysql/database.hxx> +#include <odb/pgsql/database.hxx> + +#include "person.hxx" +#include "person-odb.hxx" + +using namespace std; +using namespace odb::core; + +auto_ptr<database> +create_database (int argc, char* argv[]) +{ + auto_ptr<database> r; + + if (argc < 2) + { + cerr << "error: database system name expected" << endl; + return r; + } + + string db (argv[1]); + + if (db == "mysql") + r.reset (new odb::mysql::database (argc, argv)); + else if (db == "pgsql") + r.reset (new odb::pgsql::database (argc, argv)); + else + cerr << "error: unknown database system " << db << endl; + + return r; +} + +int +main (int argc, char* argv[]) +{ + try + { + auto_ptr<database> db (create_database (argc, argv)); + + if (db.get () == 0) + return 1; // Diagnostics has already been issued. + + ... + </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 < 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 — 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 — 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 — 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 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 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><odb/core.hxx></code> header, a + friend of this object class. For example:</p> + + <pre class="cxx"> +#include <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 <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& get_name () const; + std::string& 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 get</code> + and <code>db 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 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 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 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 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 <odb/database.hxx> +#include <odb/mysql/database.hxx> + +auto_ptr<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><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 <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><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&, + const std::string& name = "", + bool drop = true); + + static void + drop_schema (database&, const std::string& name = ""); + + static bool + exists (database_id, const std::string& name = ""); + + static bool + exists (const database&, const std::string& 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 table</code>, <code>db column</code>, + and <code>db 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><odb/transaction.hxx></code> + header file to make this class available in your application. + For example:</p> + + <pre class="cxx"> +#include <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& + database (); + + connection_type& + connection (); + + bool + finilized () const; + + public: + static bool + has_current (); + + static transaction& + current (); + + static void + current (transaction&); + + 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 < 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& db, person& 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& db, unsigned long id) +{ + transaction t (db.begin ()); + + auto_ptr<person> p (db.load<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& db, person& 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><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& statement); + + unsigned long long + execute (const char* statement, std::size_t length); + + database_type& + database (); + }; + + typedef details::shared_ptr<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& 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& 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 <typename T> + typename object_traits<T>::id_type + persist (const T& object); + + template <typename T> + typename object_traits<T>::id_type + persist (const object_traits<T>::const_pointer_type& object); + + template <typename T> + typename object_traits<T>::id_type + persist (T& object); + + template <typename T> + typename object_traits<T>::id_type + persist (const object_traits<T>::pointer_type& object); + </pre> + + <p>Here and in the rest of the manual, + <code>object_traits<T>::pointer_type</code> and + <code>object_traits<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<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<person> jane (new person ("Jane", "Doe", 32)); + +transaction t (db.begin ()); + +db.persist (john); +unsigned long jane_id (db.persist (jane)); + +t.commit (); + +cerr << "Jane's id: " << jane_id << 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 <typename T> + typename object_traits<T>::pointer_type + load (const typename object_traits<T>::id_type& id); + + template <typename T> + void + load (const typename object_traits<T>::id_type& id, T& 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<person> jane (db.load<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 <typename T> + void + reload (T& object); + + template <typename T> + void + reload (const object_traits<T>::pointer_type& 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 <typename T> + typename object_traits<T>::pointer_type + find (const typename object_traits<T>::id_type& id); + + template <typename T> + bool + find (const typename object_traits<T>::id_type& id, T& 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 <typename T> + void + update (const T& object); + + template <typename T> + void + update (const object_traits<T>::const_pointer_type& object); + + template <typename T> + void + update (const object_traits<T>::pointer_type& 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& 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 () < 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<bank_account> from (db.load<bank_account> (from_acc)); + +if (from->balance () < amount) + throw insufficient_funds (); + +shared_ptr<bank_account> to (db.load<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 <typename T> + void + erase (const T& object); + + template <typename T> + void + erase (const object_traits<T>::const_pointer_type& object); + + template <typename T> + void + erase (const object_traits<T>::pointer_type& object); + + template <typename T> + void + erase (const typename object_traits<T>::id_type& 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& john = ... +shared_ptr<jane> jane = ... +unsigned long joe_id = ... + +transaction t (db.begin ()); + +db.erase (john); +db.erase (jane); +db.erase<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 <typename T> + unsigned long long + erase_query (); + + template <typename T> + unsigned long long + erase_query (const odb::query<T>&); + </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<person> query; + +transaction t (db.begin ()); + +db.erase_query<person> (query::last == "Doe" && query::age < 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<employee> query; + +transaction t (db.begin ()); + +employer& e = ... // Employer object to be deleted. + +db.erase_query<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& 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&); + + 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><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&, const statement&); + + virtual void + execute (connection&, const statement&); + + virtual void + execute (connection&, const char* statement) = 0; + + virtual void + deallocate (connection&, const statement&); + }; +} + </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::<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 <odb/pgsql/tracer.hxx> +#include <odb/pgsql/database.hxx> +#include <odb/pgsql/connection.hxx> +#include <odb/pgsql/statement.hxx> + +class pgsql_tracer: public odb::pgsql::tracer +{ + virtual void + prepare (odb::pgsql::connection& c, const odb::pgsql::statement& s) + { + cerr << c.database ().db () << ": PREPARE " << s.name () + << " AS " << s.text () << endl; + } + + virtual void + execute (odb::pgsql::connection& c, const odb::pgsql::statement& s) + { + cerr << c.database ().db () << ": EXECUTE " << s.name () << endl; + } + + virtual void + execute (odb::pgsql::connection& c, const char* statement) + { + cerr << c.database ().db () << ": " << statement << endl; + } + + virtual void + deallocate (odb::pgsql::connection& c, const odb::pgsql::statement& s) + { + cerr << c.database ().db () << ": DEALLOCATE " << s.name () << 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& db = ...; +db.tracer (tracer); // Compile error. + +odb::pgsql::database& 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& + 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 not_null</code> or + <code>db 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><odb/exception.hxx></code> header file. All the + concrete ODB exceptions are defined in + <code><odb/exceptions.hxx></code> which also includes + <code><odb/exception.hxx></code>. Normally you don't + need to include either of these two headers because they are + automatically included by <code><odb/database.hxx></code>. + However, if the source file that handles ODB exceptions + does not include <code><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<person> query; + typedef odb::result<person> result; + + unsigned short age; + query q (query::first == "John" && query::age < query::_ref (age)); + + for (age = 10; age < 100; age += 10) + { + result r (db.query<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 && query::agee < 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 < 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>&&</code> (AND), + <code>||</code> (OR), and <code>!</code> (NOT). For example:</p> + + <pre class="cxx"> + typedef odb::query<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><</code></td> + <td>less than</td> + <td><code>query::age < 31</code></td> + </tr> + + <tr> + <td><code>></code></td> + <td>greater than</td> + <td><code>query::age > 31</code></td> + </tr> + + <tr> + <td><code><=</code></td> + <td>less than or equal</td> + <td><code>query::age <= 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<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") && + query::age < 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<person> (q1); // Find John. + db.query<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 < age); // By value. + query q2 (query::age < query::_val (age)); // By value. + query q3 (query::age < query::_ref (age)); // By reference. + + query q4 ("age < " + age); // Error. + query q5 ("age < " + query::_val (age)); // By value. + query q6 ("age < " + 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 <typename T> + result<T> + query (bool cache = true); + + template <typename T> + result<T> + query (const odb::query<T>&, 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<person> query; + typedef odb::result<person> result; + + result all (db.query<person> ()); + result johns (db.query<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<person> (q)); + result r1 (db.query<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& db, const query& name_query) +{ + return db.query<person> (name_query && query::age < 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<person> query; + + auto_ptr<person> p ( + db.query_one<person> ( + query::email == "jon@example.com")); + </pre> + + <p>The shortcut query functions have the following signatures:</p> + + <pre class="cxx"> + template <typename T> + typename object_traits<T>::pointer_type + query_one (); + + template <typename T> + bool + query_one (T&); + + template <typename T> + T + query_value (); + + template <typename T> + typename object_traits<T>::pointer_type + query_one (const odb::query<T>&); + + template <typename T> + bool + query_one (const odb::query<T>&, T&); + + template <typename T> + T + query_value (const odb::query<T>&); + </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 — <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<person> p = db.query_one<person> ( + query::email == "jon@example.com")) + { + ... + } + + person p; + if (db.query_one<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<person> query; + typedef odb::result<person> result; + + result johns (db.query<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 <typename T> + class result + { + public: + typedef odb::result_iterator<T> iterator; + + public: + result (); + + result (const result&); + + result& + operator= (const result&); + + void + swap (result&) + + 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<T>::iterator</code> + type, for example:</p> + + <pre class="cxx"> + result r (db.query<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& 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 <typename T> + class result_iterator + { + public: + T* + operator-> () const; + + T& + operator* () const; + + typename object_traits<T>::pointer_type + load (); + + void + load (T& x); + + typename object_traits<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<person> (query::first == "John")); + + for (result::iterator i (r.begin ()); i != r.end ();) + { + cout << i->last () << endl; // Create an object. + person& p (*i); // Reference to the same object. + cout << p.age () << 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<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<person> (query::first == "John")); + + person p; + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + i.load (p); + cout << p.last () << endl; + cout << i.age () << 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<unsigned long> set = ...; // Persons of interest. + + result r (db.query<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 << i->first () << 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 <typename T> + prepared_query<T> + prepare_query (const char* name, const odb::query<T>&); + </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<person> query; +typedef odb::prepared_query<person> prep_query; + +prep_query pq ( + db.prepare_query<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 <typename T> + struct prepared_query + { + prepared_query (); + + prepared_query (const prepared_query&) + prepared_query& operator= (const prepared_query&) + + result<T> + execute (bool cache = true); + + typename object_traits<T>::pointer_type + execute_one (); + + bool + execute_one (T& object); + + T + execute_value (); + + const char* + name () const; + + statement& + 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<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<person> query; +typedef odb::prepared_query<person> prep_query; +typedef odb::result<person> result; + +transaction t (db.begin ()); + +unsigned short age; +query q (query::age > query::_ref (age)); +prep_query pq (db.prepare_query<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<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 <typename T> + void + cache_query (const prepared_query<T>&); + + template <typename T, typename P> + void + cache_query (const prepared_query<T>&, + std::[auto|unique]_ptr<P> params); + + template <typename T> + prepared_query<T> + lookup_query (const char* name); + + template <typename T, typename P> + prepared_query<T> + lookup_query (const char* name, P*& 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 < 5; ++i) +{ + transaction t (db.begin ()); + + prep_query pq (db.lookup_query<person> ("person-age-query")); + + if (!pq) + { + pq = db.prepare_query<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<person> ("person-age-query", age_param)); + + if (!pq) + { + auto_ptr<unsigned short> p (new unsigned short); + age_param = p.get (); + query q (query::age > query::_ref (*age_param)); + pq = db.prepare_query<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&)); + </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<void (const char* name, connection&)>); + </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& c) +{ + auto_ptr<params> p (new params); + query q (query::age > query::_ref (p->age) && + query::first == query::_ref (p->first)); + prep_query pq (c.prepare_query<person> (name, q)); + c.cache_query (pq, p); +} + +db.query_factory ("person-age-name-query", &query_factory); + +for (unsigned short age (90); age > 40; age -= 10) +{ + transaction t (db.begin ()); + + params* p; + prep_query pq (db.lookup_query<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& c) + { + unique_ptr<params> p (new params); + query q (query::age > query::_ref (p->age) && + query::first == query::_ref (p->first)); + prep_query pq (c.prepare_query<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<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&, const std::string&); + ... +private: + friend class odb::access; + name (); + ... +}; + +#pragma db object +class person +{ + ... +private: + std::vector<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<std::string> nicknames_; + ... +}; + </pre> + + <p>The resulting database table (called <code>person_nicknames</code>) will + contain the object id column of type <code>unsigned 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<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 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<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<std::string> emails_; + ... +}; + </pre> + + <p>The resulting database table (called <code>person_emails</code>) will + contain the object id column of type <code>unsigned 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<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<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 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<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<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<person> p1 (db.load<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&) + { + 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><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&</code>. In particular, this + means that we can use <code>odb::vector</code> instance + anywhere <code>const std::vector&</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 <class T, class A = std::allocator<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<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<T, A>::iterator base () const; + }; + + // Return std::vector iterators. The begin() functions mark + // all the elements as modified. + // + typename std::vector<T, A>::iterator mbegin (); + typename std::vector<T, A>::iterator mend (); + typename std::vector<T, A>::reverse_iterator mrbegin (); + typename std::vector<T, A>::reverse_iterator mrend (); + + // Interfacing with std::vector. + // + vector (const std::vector<T, A>&); + vector (std::vector<T, A>&&); // C++11 only. + + vector& operator= (const std::vector<T, A>&); + vector& operator= (std::vector<T, A>&&); // C++11 only. + + operator const std::vector<T, A>& () const; + std::vector<T, A>& base (); + const std::vector<T, A>& base (); + + // Change tracking. + // + bool _tracking () const; + void _start () const; + void _stop () const; + void _arm (transaction&) 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 <vector> +#include <odb/vector.hxx> + +void f (const std::vector<int>&); + +odb::vector<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<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<employer> current_employer_; + + #pragma db value_not_null + std::vector<shared_ptr<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<employer> er (new employer ("Example Inc")); + shared_ptr<employee> john (new employee ("John", "Doe")); + shared_ptr<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<employee> john (db.load<employee> (john_id)); + shared_ptr<employee> jane (db.load<employee> (jane_id)); + + cout << john->employer_->name_ << endl; + cout << jane->employer_->name_ << 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<employee> query; +typedef odb::result<employee> result; + +session s; +transaction t (db.begin ()); + +result r (db.query<employee> ( + query::employer->name == "Example Inc" && query::last == "Doe")); + +for (result::iterator i (r.begin ()); i != r.end (); ++i) + cout << i->first_ << " " << i->last_ << 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<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<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<shared_ptr<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<shared_ptr<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<employee> employee_; +}; + +#pragma db object +class employee +{ + ... + + #pragma db id + unsigned long id_; + + #pragma db not_null + shared_ptr<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<position> +{ + ... + + void + fill (shared_ptr<employee> e) + { + employee_ = e; + e->positions_ = shared_from_this (); + } + +private: + weak_ptr<employee> employee_; +}; + +#pragma db object +class employee +{ + ... + +private: + friend class position; + + #pragma db not_null + shared_ptr<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<position> p (db.load<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<position> p (db.load<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<employee> employee_; +}; + +#pragma db object +class employee +{ + ... + + #pragma db not_null + shared_ptr<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<employee> employee_; +}; + +#pragma db object +class employee +{ + ... + + #pragma db id + unsigned long id_; + + #pragma db not_null + shared_ptr<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<weak_ptr<employee> > employees_ +}; + +#pragma db object +class employee +{ + ... + + #pragma db id + unsigned long id_; + + #pragma db not_null + shared_ptr<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<weak_ptr<employee> > employees_; +}; + +#pragma db object +class employee +{ + ... + + #pragma db id + unsigned long id_; + + #pragma db value_not_null unordered + std::vector<shared_ptr<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<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<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<weak_ptr<employee> > employees_; +}; + +#pragma db object +class employee +{ + ... + + #pragma db id + unsigned long id_; + + #pragma db not_null + shared_ptr<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<employee> e (db.load<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><odb/tr1/lazy-ptr.hxx></code> + header while all the others — in + <code><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<lazy_weak_ptr<employee> > employees_; +}; + +#pragma db object +class employee +{ + ... + + #pragma db not_null + lazy_shared_ptr<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<employee> e (db.load<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 <class T> +class lazy_ptr +{ +public: + // + // The eager pointer interface. + // + + // Initialization/assignment from an eager pointer to a + // transient object. + // +public: + template <class Y> lazy_ptr (const eager_ptr<Y>&); + template <class Y> lazy_ptr& operator= (const eager_ptr<Y>&); + + // 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<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<T> get_eager () const; + + // Initialization with a persistent loaded object. + // + template <class Y> lazy_ptr (database&, Y*); + template <class Y> lazy_ptr (database&, const eager_ptr<Y>&); + + template <class Y> void reset (database&, Y*); + template <class Y> void reset (database&, const eager_ptr<Y>&); + + // Initialization with a persistent unloaded object. + // + template <class ID> lazy_ptr (database&, const ID&); + + template <class ID> void reset (database&, const ID&); + + // Query object id and database of a persistent object. + // + template <class O /* = T */> + // C++11: template <class O = T> + object_traits<O>::id_type object_id () const; + + odb::database& 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<lazy_weak_ptr<employee> > employees; + +session s; +transaction t (db.begin ()); + +shared_ptr<employer> er (db.load<employer> ("Example Inc")); +employees& 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<employee>& lwp (*i); + + if (lwp.object_id<employee> () < 100) + // C++11: if (lwp.object_id () < 100) + { + shared_ptr<employee> e (lwp.load ()); // Load and lock. + cout << e->first_ << " " << e->last_ << 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<employer> er (db.load<employer> ("Example Inc")); +shared_ptr<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<employer> er (db, std::string ("Example Inc")); +shared_ptr<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 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 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><odb/core.hxx></code> header, a friend of this value type. + For example:</p> + + <pre class="cxx"> +#include <odb/core.hxx> + +#pragma db value +class basic_name +{ +public: + basic_name (const std::string& first, const std::string& 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<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 <typename T> +struct point +{ + T x; + T y; + T z; +}; + +typedef point<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 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 <utility> // std::pair + +typedef std::pair<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<person> query; +typedef odb::result<person> result; + +transaction t (db.begin ()); + +result r (db.query<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<</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 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 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 column</code>, either the <code>db value_column</code> + (<a href="#14.4.36">Section 14.4.36, "<code>value_column</code>"</a>) or + <code>db 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<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 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<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 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 <odb/nullable.hxx> + +#pragma db object +class person +{ + ... + + std::string first_; // TEXT NOT NULL + odb::nullable<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><odb/nullable.hxx></code> header file and + has the following interface:</p> + + <pre class="cxx"> +namespace odb +{ + template <typename T> + class nullable + { + public: + typedef T value_type; + + nullable (); + nullable (const T&); + nullable (const nullable&); + template <typename Y> explicit nullable (const nullable<Y>&); + + nullable& operator= (const T&); + nullable& operator= (const nullable&); + template <typename Y> nullable& operator= (const nullable<Y>&); + + void swap (nullable&); + + // Accessor interface. + // + bool null () const; + + T& get (); + const T& get () const; + + // Pointer interface. + // + operator bool_convertible () const; + + T* operator-> (); + const T* operator-> () const; + + T& operator* (); + const T& 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<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 null</code> + pragma. For example:</p> + + <pre class="cxx"> +#pragma db object +class person +{ + ... + + std::string first_; + + #pragma db null + std::auto_ptr<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<std::string> middle_; + std::string last_; +}; + +#pragma db object +class person +{ + ... + odb::nullable<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<std::vector<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& first () const; + const std::string& 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 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 — 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& r1 (p); +person& r2 (e); + +auto_ptr<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 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<employee>. +class employee: public person +{ + ... +}; + +#pragma db object // Object pointer is std::shared_ptr<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<person> p1 (new employee (...)); + shared_ptr<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<person> p; + + transaction t (db.begin ()); + p = db.load<person> (id1); // Loads employee. + p = db.load<person> (id2); // Loads contractor. + t.commit (); +} + +// Query. +// +{ + typedef odb::query<person> query; + typedef odb::result<person> result; + + transaction t (db.begin ()); + + result r (db.query<person> (query::last == "Doe")); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + person& p (*i); // Can be employee or contractor. + } + + t.commit (); +} + +// Update. +// +{ + shared_ptr<person> p; + shared_ptr<employee> e; + + transaction t (db.begin ()); + + e = db.load<employee> (id1); + e->temporary (false); + p = e; + db.update (p); // Updates employee. + + t.commit (); +} + +// Erase. +// +{ + shared_ptr<person> p; + + transaction t (db.begin ()); + p = db.load<person> (id1); // Loads employee. + db.erase (p); // Erases employee. + db.erase<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<person> query; +typedef odb::result<person> result; + +transaction t (db.begin ()); + +result r (db.query<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<person> p; + + transaction t (db.begin ()); + p = db.load<person> (id); // Requires two statement. + p = db.load<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<person> p = ...; + +transaction t (db.begin ()); +db.erase<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<permanent_employee> manager_; +}; + +#pragma db object +class contractor: public temporary_employee +{ + shared_ptr<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<employee> query; + +transaction t (db.begin ()); +db.erase_query<employee> (query::permanent); // Ok. +db.erase_query<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 — 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 <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<person> p (db.load<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 load</code> and + <code>db 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 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<std::string> nicknames_; + + #pragma db section(extras_) + std::shared_ptr<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><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 <typename T> + void + load (T& object, section&); + + template <typename T> + void + update (const T& object, const section&); + </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<person> p (db.load<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& + keys () const {return keys_;} + + odb::section& + keys () {return keys_;} + +private: + odb::section keys_; + + ... +}; + +auto_ptr<person> p (db.load<person> (...)); + +section& 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<person> l ( + db.load<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<char, 1024> key_type; + + const key_type& + public_key () const {return public_key_;} + + void + public_key (const key_type& k) + { + public_key_ = k; + keys_.change (); + } + + const key_type& + private_key () const {return private_key_;} + + void + private_key (const key_type& 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<person> p; + +try +{ + transaction t (db.begin ()); + p = db.load<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&) +{ + // 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<std::string> employment_history_; +}; + +transaction t (db.begin ()); + +auto_ptr<person> p (db.load<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<manager> m (db.load<manager> (...)); + +person& p (*m); +employee& e (*m); +section& 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<person> p; + +{ + transaction t (db.begin ()); + p = db.load<person> (...); + t.commit (); +} + +{ + transaction t (db.begin ()); + + try + { + db.load (*p, p->extras_); // Throws if object state has changed. + } + catch (const object_changed&) + { + 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 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<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<employer> employer_; +}; + +transaction t (db.begin ()); + +auto_ptr<employee> e (db.load<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<std::string> nicknames_; +}; + +transaction t (db.begin ()); + +auto_ptr<person> p (db.load<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<country> residence_; + shared_ptr<country> nationality_; + + shared_ptr<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 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<employee_name> query; +typedef odb::result<employee_name> result; + +transaction t (db.begin ()); + +result r (db.query<employee_name> (query::age < 31)); + +for (result::iterator i (r.begin ()); i != r.end (); ++i) +{ + const employee_name& en (*i); + cout << en.first << " " << en.last << 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 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 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 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<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 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 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 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 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<view>::member</code> expressions. + This is similar to how we can refer to object members using the + <code>odb::query<object>::member</code> expressions when + querying the database for an object. For example:</p> + + <pre class="cxx"> +typedef odb::query<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<employee_count> (query::last == "Doe")); + +cout << ec.count << 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<view></code> scope. For instance, + in the above example, we referred to the last name member as + <code>odb::query<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<...>::employee::last</code> expression. + Similarly, to refer to the employer name, we use the + <code>odb::query<...>::employer::name</code> expression. + For example:</p> + + <pre class="cxx"> +typedef odb::result<employee_employer> result; +typedef odb::query<employee_employer> query; + +transaction t (db.begin ()); + +result r (db.query<employee_employer> ( + query::employee::last == "Doe" && + query::employer::name == "Simple Tech Ltd")); + +for (result::iterator i (r.begin ()); i != r.end (); ++i) + cout << i->first << " " << i->last << " " << i->employer_name << 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<employee_country> query; +typedef odb::result<employee_country> result; + +transaction t (db.begin ()); + +result r (db.query<employee_country> ( + query::res_country::name == query::nat_country::name)); + +for (result::iterator i (r.begin ()); i != r.end (); ++i) + cout << i->first << " " << i->last << " " << i->res_country_name << 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<employee_name> query; +typedef odb::result<employee_name> result; + +transaction t (db.begin ()); + +result r (db.query<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 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<employee> ee; + shared_ptr<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<employee_employer> query; + +transaction t (db.begin ()); + +for (const employee_employer& r: + db.query<employee_employer> (query::employee::age < 31)) +{ + cout << r.ee->age () << " " << r.er->name () << 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<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<employer_view> query; + +db.query<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<employer> e; + shared_ptr<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<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<country> res; + shared_ptr<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<employee> ee; + shared_ptr<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<employee_employer> query; + +transaction t (db.begin ()); +odb::session s; + +for (const employee_employer& r: + db.query<employee_employer> (query::employee::age < 31)) +{ + assert (r.ee->employed_by_ == r.er); + cout << r.ee->age () << " " << r.er->name () << 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(&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(&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& r, country& n): res (&r), nat (&n) {} + + country* res; + country* nat; +}; + </pre> + + <p>And here is how we can use this view:</p> + + <pre class="cxx"> +typedef odb::result<employee_country> result; + +transaction t (db.begin ()); + +result r (db.query<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;} // &er or NULL. + void set_er (shared_ptr<employer> p) {er_p = p.get ();} // From cache. + + #pragma db get(&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& x) + : er_p (x.er_p == &x.er ? &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 <odb/nullable.hxx> + +#pragma db view object(object) +struct view +{ + + odb::nullable<int> n; // If 'n' is NULL, then, logically, so is 'o'. + unique_ptr<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 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 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 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 table</code> pragma + is similar to the <code>db 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<employee_leave> query; +typedef odb::result<employee_leave> result; + +transaction t (db.begin ()); + +unsigned short v_min = ... +unsigned short l_min = ... + +result r (db.query<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<employee_vacation> query; +typedef odb::result<employee_vacation> result; + +transaction t (db.begin ()); + +result r (db.query<employee_vacation> ( + (query::last == "Doe") + "AND extra.vacation_days <> 0")); + +for (result::iterator i (r.begin ()); i != r.end (); ++i) + cout << i->first << " " << i->last << " " << i->vacation_days << 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<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 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<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<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 query</code> pragma with the varying expression + specified at the query execution time. To allow this, the + <code>db 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 && (?)) +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<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<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 <> 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 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 — 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 <> 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<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 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<sequence_value> query; +typedef odb::result<sequence_value> result; + +string seq_name = ... + +result l (db.query<sequence_value> ( + "SELECT lastval('" + seq_name + "')")); + +result n (db.query<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 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 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><odb/session.hxx></code> header file to make this class + available in your application. For example:</p> + + <pre class="cxx"> +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <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&); + session& operator= (const session&); + + // Current session interface. + // + public: + static session& + current (); + + static bool + has_current (); + + static void + current (session&); + + static void + reset_current (); + + static session* + current_pointer (); + + static void + current_pointer (session*); + + // Object cache interface. + // + public: + template <typename T> + struct cache_position {...}; + + template <typename T> + cache_position<T> + cache_insert (database&, + const object_traits<T>::id_type&, + const object_traits<T>::pointer_type&); + + template <typename T> + object_traits<T>::pointer_type + cache_find (database&, const object_traits<T>::id_type&) const; + + template <typename T> + void + cache_erase (const cache_position<T>&); + + template <typename T> + void + cache_erase (database&, const object_traits<T>::id_type&); + }; +} + </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<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<person> p1 (db.load<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& db, shared_ptr<const person> p) +{ + transaction t (db.begin ()); + db.persist (p); // Persisted as const pointer. + t.commit (); +} + +session s; + +shared_ptr<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<person> p (db.load<person> (id1)); // p == p1 + p->age (30); // Undefined behavior since p1 was created const. + t.commit (); +} + +shared_ptr<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<person> p (db.load<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 <typename T> + struct cache_position + { + ... + }; + + template <typename T> + static cache_position<T> + _cache_insert (odb::database&, + const typename odb::object_traits<T>::id_type&, + const typename odb::object_traits<T>::pointer_type&); + + template <typename T> + static typename odb::object_traits<T>::pointer_type + _cache_find (odb::database&, + const typename odb::object_traits<T>::id_type&); + + template <typename T> + static void + _cache_erase (const cache_position<T>&); + + // Notification functions. + // + template <typename T> + static void + _cache_persist (const cache_position<T>&); + + template <typename T> + static void + _cache_load (const cache_position<T>&); + + template <typename T> + static void + _cache_update (odb::database&, const T& obj); + + template <typename T> + static void + _cache_erase (odb::database&, + const typename odb::object_traits<T>::id_type&); +}; + </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 << "enter age for " << p.first () << " " << p.last () << 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 << "enter age for " << p.first () << " " << p.last () << endl; +unsigned short age; +cin >> age; +p.age (age); + +{ + transaction t (db.begin ()); + + try + { + db.update (p); + } + catch (const object_changed&) + { + 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 << "age is " << p.age () << ", delete?" << 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 << "age is " << p.age () << ", delete?" << endl; + else + cerr << "age changed to " << p.age () << ", still delete?" << endl; + + getline (cin, answer); + + if (answer == "yes") + { + transaction t (db.begin ()); + + try + { + db.erase (p); + done = true; + } + catch (const object_changed&) + { + 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 model 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 model 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 long 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 model 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 <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> — the changelog file. + Just for illustration, below are the contents of this changelog.</p> + + <pre class="xml"> +<changelog database="pgsql"> + <model version="1"> + <table name="person" kind="object"> + <column name="id" type="BIGINT" null="false"/> + <column name="first" type="TEXT" null="false"/> + <column name="last" type="TEXT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> + </pre> + + <p>Let's say we now would like to add another data member to the + <code>person</code> class — 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"> +<changelog database="pgsql"> + <changeset version="2"> + <alter-table name="person"> + <add-column name="middle" type="TEXT" null="false"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="person" kind="object"> + <column name="id" type="BIGINT" null="false"/> + <column name="first" type="TEXT" null="false"/> + <column name="last" type="TEXT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</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 @@ +<changelog database="pgsql"> +<span style="color: #009E00">+ <changeset version="2"> ++ <alter-table name="person"> ++ <add-column name="middle" type="TEXT" null="false"/> ++ </alter-table> ++ </changeset> ++</span> + <model version="1"> + <table name="person" kind="object"> + <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 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 — + <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 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 — 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 ==, !=, <, >, <=, and >= operators. + // Version ordering is as follows: {1,f} < {2,t} < {2,f} < {3,t}. + }; + + class database + { + public: + ... + + schema_version + schema_version (const std::string& name = "") const; + + bool + schema_migration (const std::string& name = "") const; + + const schema_version_migration& + schema_version_migration (const std::string& name = "") const; + + // Set schema version and migration state manually. + // + void + schema_version_migration (schema_version, + bool migration, + const std::string& name = ""); + + void + schema_version_migration (const schema_version_migration&, + const std::string& name = ""); + + // Set default schema version table for all schemas. + // + void + schema_version_table (const std::string& table_name); + + // Set schema version table for a specific schema. + // + void + schema_version_table (const std::string& table_name, + const std::string& 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&, + schema_version, + const std::string& name = ""); + + static void + migrate_schema_post (database&, + schema_version, + const std::string& name = ""); + + static void + migrate_schema (database&, + schema_version, + const std::string& name = ""); + + // Data migration. + // + // Discussed in the next section. + + + // Combined schema and data migration. + // + static void + migrate (database&, + schema_version = 0, + const std::string& name = ""); + + // Schema version information. + // + static schema_version + base_version (const database&, + const std::string& name = ""); + + static schema_version + base_version (database_id, + const std::string& name = ""); + + static schema_version + current_version (const database&, + const std::string& name = ""); + + static schema_version + current_version (database_id, + const std::string& name = ""); + + static schema_version + next_version (const database&, + schema_version = 0, + const std::string& name = ""); + + static schema_version + next_version (database_id, + schema_version, + const std::string& 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 < cv) +{ + // Old schema (and data) in the database, migrate them. + // + + if (v < bv) + { + // Error: migration from this version is no longer supported. + } + + for (v = schema_catalog::next_version (db, v); + v <= 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 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& 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& p: db.query<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& p: db.query<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&, + schema_version = 0, + const std::string& name = ""); + + typedef void data_migration_function_type (database&); + + // Common (for all the databases) data migration, C++98/03 version: + // + template <schema_version v, schema_version base> + static void + data_migration_function (data_migration_function_type*, + const std::string& name = ""); + + // Common (for all the databases) data migration, C++11 version: + // + template <schema_version v, schema_version base> + static void + data_migration_function (std::function<data_migration_function_type>, + const std::string& name = ""); + + // Database-specific data migration, C++98/03 version: + // + template <schema_version v, schema_version base> + static void + data_migration_function (database&, + data_migration_function_type*, + const std::string& name = ""); + + template <schema_version v, schema_version base> + static void + data_migration_function (database_id, + data_migration_function_type*, + const std::string& name = ""); + + // Database-specific data migration, C++11 version: + // + template <schema_version v, schema_version base> + static void + data_migration_function (database&, + std::function<data_migration_function_type>, + const std::string& name = ""); + + template <schema_version v, schema_version base> + static void + data_migration_function (database_id, + std::function<data_migration_function_type>, + const std::string& name = ""); + }; + + // Static data migration function registration, C++98/03 version: + // + template <schema_version v, schema_version base> + struct data_migration_entry + { + data_migration_entry (data_migration_function_type*, + const std::string& name = ""); + + data_migration_entry (database_id, + data_migration_function_type*, + const std::string& name = ""); + }; + + // Static data migration function registration, C++11 version: + // + template <schema_version v, schema_version base> + struct data_migration_entry + { + data_migration_entry (std::function<data_migration_function_type>, + const std::string& name = ""); + + data_migration_entry (database_id, + std::function<data_migration_function_type>, + const std::string& 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& 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& db) +{ + transaction t (db.begin ()); + + for (person& p: db.query<person> ()) + { + p.gender (guess_gender (p.first ())); + db.update (p); + } + + t.commit (); +} + +static const odb::data_migration_entry<3, MYAPP_BASE_VERSION> +migrate_gender_entry (&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 <schema_version v> +using migration_entry = odb::data_migration_entry<v, MYAPP_BASE_VERSION>; + +static const migration_entry<3> +migrate_gender_entry (&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<3> +migrate_gender_entry ( + [] (odb::database& db) + { + transaction t (db.begin ()); + + for (person& p: db.query<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& 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& db) +{ + // Assume we are already in a transaction. + // + for (person& p: db.query<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 <= 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 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 <odb/nullable.hxx> + +#pragma db object +class person +{ + ... + + odb::nullable<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 <odb/core.hxx> // odb::database +#include <odb/callback.hxx> // odb::callback_event +#include <odb/nullable.hxx> + +#pragma db object callback(migrate) +class person +{ + ... + + void + migrate (odb::callback_event e, odb::database&) + { + if (e == odb::callback_event::post_load) + { + // Guess gender if not assigned. + // + if (gender_.null ()) + gender_ = guess_gender (first_); + } + } + + odb::nullable<gender> gender_; +}; + </pre> + + <p>In particular, we don't have to touch any of the accessors + or modifiers or the application logic — 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& db) +{ + typedef odb::query<person> query; + + for (person& p: db.query<person> (query::gender.is_null ())) + { + p.gender (guess_gender (p.first ())); + db.update (p); + } +} + +static const odb::data_migration_entry<4, MYAPP_BASE_VERSION> +migrate_gender_entry (&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& db) +{ + for (person& p: db.query<person> ()) + { + p.name (p.first () + " " + + p.middle () + (p.middle ().empty () ? "" : " ") + + p.last ()); + db.update (p); + } +} + +static const odb::data_migration_entry<4, MYAPP_BASE_VERSION> +migrate_name_entry (&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&); + + static void + migrate_name (odb::database&); +}; + +void person:: +migrate_gender (odb::database& db) +{ + for (person& p: db.query<person> ()) + { + p.gender_ = guess_gender (p.first_); + db.update (p); + } +} + +static const odb::data_migration_entry<3, MYAPP_BASE_VERSION> +migrate_name_entry (&migrate_gender); + +void person:: +migrate_name (odb::database& db) +{ + for (person& p: db.query<person> ()) + { + p.name_ = p.first_ + " " + + p.middle_ + (p.middle_.empty () ? "" : " ") + + p.last_; + db.update (p); + } +} + +static const odb::data_migration_entry<4, MYAPP_BASE_VERSION> +migrate_name_entry (&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<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 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&); +}; + +void employee:: +migrate_person (odb::database& db) +{ + for (person& p: db.query<person> ()) + { + employee e (p.name (), p.gender ()); + db.persist (e); + } +} + +static const odb::data_migration_entry<5, MYAPP_BASE_VERSION> +migrate_person_entry (&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 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<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 <odb/callback.hxx> + +#pragma db object callback(init) +class person +{ + ... + + void + init (odb::callback_event, odb::database&); +}; + </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&); + +void +name (odb::callback_event, odb::database&) 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><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 — 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 — 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 <odb/core.hxx> +#include <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&) + { + 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 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> — <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<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<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<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<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 <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><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 <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 <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<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<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<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<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<shared_ptr<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<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<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<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<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<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<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<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<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 long 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& name () const; + void name (const std::string&); +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 std::string&</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&</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 — the new value + — 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& set_name (); // By-reference modifier. + void set_name (std::string const&); // 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<account>& acc () const; + void acc (std::unique_ptr<account>); + private: + #pragma db set(acc (std::move (?))) + std::unique_ptr<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<std::string> aliases_; +}; + +#pragma db object +class person +{ + ... + +public: + const name& name () const; + void name (const name&); +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<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<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<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<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& f, std::string const& 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& name () const; + void name (std::string const&); + + 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><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 <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<shared_ptr<person> > employees_; +}; + +#pragma db object pointer(shared_ptr) +class person +{ + ... + + #pragma db inverse(employee_) + weak_ptr<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_ = &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<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_ = &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 long 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<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<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<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<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<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<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<shared_ptr<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<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<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<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<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<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<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<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<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 test_</code> option, then the + <code>employee</code> class table will be called + <code>test_audit_hr_employees</code> and <code>employer</code> — + <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[("<name>")] \ + [unique|type("<type>")] \ + [method("<method>")] \ + [options("<index-options>")] \ + member(<name>[, "<column-options>"])... \ + members(<name>[,<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<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<int></code> would have been much more + appropriate. To add support for mapping + <code>std::vector<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<int></code>. Below is a sample + implementation:</p> + +<pre class="cxx"> +namespace odb +{ + namespace pgsql + { + template <> + class value_traits<std::vector<int>, id_string> + { + public: + typedef std::vector<int> value_type; + typedef value_type query_type; + typedef details::buffer image_type; + + static void + set_value (value_type& v, + const details::buffer& b, + std::size_t n, + bool is_null) + { + v.clear (); + + if (!is_null) + { + char c; + std::istringstream is (std::string (b.data (), n)); + + is >> c; // '{' + + for (c = static_cast<char> (is.peek ()); c != '}'; is >> c) + { + v.push_back (int ()); + is >> v.back (); + } + } + } + + static void + set_image (details::buffer& b, + std::size_t& n, + bool& is_null, + const value_type& v) + { + is_null = false; + std::ostringstream os; + + os << '{'; + + for (value_type::const_iterator i (v.begin ()), e (v.end ()); + i != e;) + { + os << *i; + + if (++i != e) + os << ','; + } + + os << '}'; + + const std::string& s (os.str ()); + n = s.size (); + + if (n > b.capacity ()) + b.capacity (n); + + std::memcpy (b.data (), s.c_str (), n); + } + }; + } +} +</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<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<int> array_; +}; +</pre> + + <p>If we wanted to always map <code>std::vector<int></code> + to PostgreSQL <code>INTEGER[]</code>, then we could instead + write:</p> + +<pre class="cxx"> +typedef std::vector<int> int_vector; +#pragma db value(int_vector) type("INTEGER[]") + +#pragma db object +class object +{ + ... + + std::vector<int> array_; // Mapped to INTEGER[]. +}; +</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><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><odb/core.hxx></code>, instead of using + <code>#pragma 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 <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 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 <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 <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 <odb/callback.hxx> +#include <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&) 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_ = &transaction::current (); + tran_->callback_register (&rollback, + const_cast<object*> (this), + transaction::event_rollback, + 0, + &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& o (*static_cast<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 <typename T> +class person +{ + ... + + T first_; + T last_; +}; + +typedef person<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 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 <utility> // std::pair + +typedef std::pair<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<person> (p); // Ok. +db.reload<person> (p); // Ok. +db.erase<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 <typename T> +class base: public base_common +{ + ... + + T value; +}; + +typedef base<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 <typename I> +void +persist (I begin, I end, bool continue_failed = true); + +template <typename I> +void +update (I begin, I end, bool continue_failed = true); + +template <typename I> +void +erase (I obj_begin, I obj_end, bool continue_failed = true); + +template <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<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] {&p1, &p2}; + +db.persist (pa, pa + sizeof(pa) / sizeof(pa[0])); + + +// Vector of raw pointers to objects. +// +std::vector<person*> pv {&p1, &p2}; + +db.persist (pv.begin (), pv.end ()); + + +// Vector of smart (shared) pointers to objects. +// +std::vector<std::shared_ptr<person>> sv { + std::make_shared<person> ("John", "Doe"), + std::make_shared<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<person_entry> people; + +void +persist (odb::database& db, people& p) +{ + std::vector<person*> tmp; + tmp.reserve (p.size ()); + std::for_each (p.begin (), + p.end (), + [&tmp] (person_entry& pe) + { + tmp.push_back (&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<unsigned long> ids {1, 2}; + +db.erase<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 — 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& + exception () const; + + bool + maybe () const; + }; + + // Iteration. + // + typedef std::set<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& + 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<person> objs {{"John", "Doe"}, {"Jane", "Doe"}}; + +try +{ + db.persist (objs.begin (), objs.end ()); +} +catch (const odb::multiple_exceptions& me) +{ + for (const auto& v: me) + { + size_t p (v.position ()); + + try + { + throw v.exception (); + } + catch (const odb::object_already_persistent&) + { + cerr << p << ": duplicate id: " << objs[p].id () << endl; + } + catch (const odb::exception& e) + { + cerr << p << ": " << e.what () << 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 + <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-<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-<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& db = ... + +typedef odb::query<person> query; +typedef odb::result<person> result; + +odb::transaction t (db.begin ()); +result r (db.query<person> (query::age < 30)); +... +t.commit (); + </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& db = ... + +typedef odb::sqlite::query<person> query; +typedef odb::result<person> result; // odb:: not odb::sqlite:: + +odb::sqlite::transaction t (db.begin ()); +result r (db.query<person> (query::age < 30)); +... +t.commit (); + </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& db = ... + +{ + using namespace odb::core; + + typedef query<person> person_query; + typedef result<person> person_result; + + transaction t (db.begin ()); + person_result r (db.query<person> (person_query::age < 30)); + ... + t.commit (); +} + </pre> + + <p>A similar mechanism is available in multi-database support. Each + database runtime defines the <code>odb::<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& db = ... + +{ + using namespace odb::sqlite::core; + + typedef query<person> person_query; + typedef result<person> person_result; + + transaction t (db.begin ()); + person_result r (db.query<person> (person_query::age < 30)); + ... + t.commit (); +} + </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& pg_db = ... +sl::database& sl_db = ... + +typedef pg::query<person> pg_query; +typedef sl::query<person> sl_query; +typedef odb::result<person> result; + +// First check the local cache. +// +odb::transaction t (sl_db.begin ()); // Note: using common transaction. +result r (sl_db.query<person> (sl_query::age < 30)); + +// If no hits, try the remote database. +// +if (r.empty ()) +{ + t.commit (); // End the SQLite transaction. + t.reset (pg_db.begin ()); // Start the PostgreSQL transaction. + + r = pg_db.query<person> (pg_query::age < 30); +} + +// Handle the result. +// +... + +t.commit (); + </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-<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<person> +load (odb::database& db, const std::string& name) +{ + odb::transaction t (db.begin ()); + std::unique_ptr<person> p (db.find (name)); + t.commit (); + return p; +} + +odb::pgsql::database& pg_db = ... +odb::sqlite::database& sl_db = ... + +// First try the local cache. +// +std::unique_ptr<person> p (load (sl_db, "John Doe")); + +// If not found, try the remote database. +// +if (p == 0) + p = load (pg_db, "John Doe"); + +... + </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-<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& db, unsigned short age, unsigned long limit) +{ + typedef odb::query<person> query; + typedef odb::result<person> result; + + odb::transaction t (db.begin ()); + + query q (query::age == age); + result r; + + if (db.id () == odb::id_pgsql) + { + // We are using PostgreSQL. Drop down to the static mode and + // add the LIMIT clause to the query. + // + namespace pg = odb::pgsql; + typedef pg::query<person> pg_query; + + pg::database& pg_db (static_cast<pg::database&> (db)); + pg_query pg_q (pg_query (q) + "LIMIT" + pg_query::_val (limit)); + r = pg_db.query<person> (pg_q); + } + else + r = db.query<person> (q); + + // Handle the result up to the limit elements. + // + ... + + t.commit (); +} + +odb::pgsql::database& pg_db = ... +odb::sqlite::database& sl_db = ... + +print (sl_db, 30, 100); +print (sl_db, 30, 100); + </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::<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-<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 mysql</code>" + (or "<code>-d 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 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<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 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<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<char></code>, + <code>std::vector<unsigned char></code>, + <code>char[N]</code>, <code>unsigned char[N]</code>, + <code>std::array<char, N></code>, and + <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::mysql::id_blob> (u)); +db.query<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<connection_factory> = 0); + + database (const std::string& user, + const std::string& passwd, + const std::string& db, + const std::string& host = "", + unsigned int port = 0, + const std::string* socket = 0, + const std::string& charset = "", + unsigned long client_flags = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string* passwd, + const std::string& db, + const std::string& host = "", + unsigned int port = 0, + const std::string* socket = 0, + const std::string& charset = "", + unsigned long client_flags = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string& passwd, + const std::string& db, + const std::string& host, + unsigned int port, + const std::string& socket, + const std::string& charset = "", + unsigned long client_flags = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string* passwd, + const std::string& db, + const std::string& host, + unsigned int port, + const std::string& socket, + const std::string& charset = "", + unsigned long client_flags = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (int& argc, + char* argv[], + bool erase = false, + const std::string& charset = "", + unsigned long client_flags = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + static void + print_usage (std::ostream&); + + 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><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 <login> + --password <password> + --database <name> + --host <host> + --port <integer> + --socket <socket> + --options-file <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&); + connection (database&, MYSQL*); + + MYSQL* + handle (); + }; + + typedef details::shared_ptr<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&) = 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><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&); + pooled_connection (database_type&, MYSQL*); + }; + + typedef details::shared_ptr<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 <odb/database.hxx> + +#include <odb/mysql/database.hxx> +#include <odb/mysql/connection-factory.hxx> + +int +main (int argc, char* argv[]) +{ + auto_ptr<odb::mysql::connection_factory> f ( + new odb::mysql::connection_pool_factory (20)); + + auto_ptr<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& + sqlstate () const; + + const std::string& + 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><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 <= 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<person_range> query; +typedef odb::result<person_range> result; + +transaction t (db.begin ()); + +result r ( + db.query<person_range> ( + query::_val (1) + "," + query::_val (18))); + +for (result::iterator i (r.begin ()); i != r.end (); ++i) + cerr << i->first << " " << i->last << " " << i->age << 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<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<person_min_max_age> ( + query::_val (0) + "," + query::_val (0))); + +cerr << mma.min_age << " " << mma.max_age << 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 sqlite</code>" + (or "<code>-d 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 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 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 long</code> and + <code>unsigned long 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<char, N></code> and, on Windows, + <code>std::array<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 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<char, 128> name_; +}; + </pre> + + <p>Alternatively, this can be done on the per-type basis, for example:</p> + + <pre class="cxx"> +typedef std::array<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<char, N></code>, + <code>wchar_t[N]</code>, and <code>std::array<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<char></code>, + <code>std::vector<unsigned char></code>, + <code>char[N]</code>, <code>unsigned char[N]</code>, + <code>std::array<char, N></code>, and + <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::sqlite::id_blob> (u)); +db.query<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 <odb/sqlite/blob.hxx> +#include <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& db () const; + const std::string& table () const; + const std::string& 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 <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 <odb/sqlite/blob-stream.hxx> + +namespace odb +{ + namespace sqlite + { + class blob_stream: public stream + { + public: + blob_stream (const blob&, bool rw); + }; + } +} + +#include <odb/sqlite/text-stream.hxx> + +namespace odb +{ + namespace sqlite + { + class text_stream: public stream + { + public: + text_stream (const text&, 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 <odb/sqlite/blob-stream.hxx> +#include <odb/sqlite/text-stream.hxx> + +string txt (1024 * 1024, 't'); +vector<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<object> p (db.load<object> (o.id)); + + text_stream ts (p->t, false); // Open for reading. + vector<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<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<load_b> query; + +transaction tx (db.begin ()); + +for (load_b& lb: db.query<load_b> (query::t == "test")) +{ + blob_stream bs (lb.b, false); + vector<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<object> p (db.load<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& name, + int flags = SQLITE_OPEN_READWRITE, + bool foreign_keys = true, + const std::string& vfs = "", + std::[auto|unique]_ptr<connection_factory> = 0); + +#ifdef _WIN32 + database (const std::wstring& name, + int flags = SQLITE_OPEN_READWRITE, + bool foreign_keys = true, + const std::string& vfs = "", + std::[auto|unique]_ptr<connection_factory> = 0); +#endif + + database (int& argc, + char* argv[], + bool erase = false, + int flags = SQLITE_OPEN_READWRITE, + bool foreign_keys = true, + const std::string& vfs = "", + std::[auto|unique]_ptr<connection_factory> = 0); + + static void + print_usage (std::ostream&); + + public: + const std::string& + 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><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<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 <name> + --create + --read-only + --options-file <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&, int extra_flags = 0); + connection (database&, sqlite3*); + + transaction + begin_immediate (); + + transaction + begin_exclusive (); + + sqlite3* + handle (); + }; + + typedef details::shared_ptr<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&) = 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><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&, int extra_flags = 0); + single_connection (database&, sqlite3*); + }; + + typedef details::shared_ptr<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&, int extra_flags = 0); + pooled_connection (database_type&, sqlite3*); + }; + + typedef details::shared_ptr<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 <odb/database.hxx> + +#include <odb/sqlite/database.hxx> +#include <odb/sqlite/connection-factory.hxx> + +int +main (int argc, char* argv[]) +{ + auto_ptr<odb::sqlite::connection_factory> f ( + new odb::sqlite::connection_pool_factory (20)); + + auto_ptr<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& + 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><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 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 <odb/connection.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +odb::database& 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 pgsql</code>" + (or "<code>-d 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 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 short</code>, <code>unsigned int</code>, and + <code>unsigned long</code>/<code>unsigned long 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<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 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<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<char></code>, + <code>std::vector<unsigned char></code>, + <code>char[N]</code>, <code>unsigned char[N]</code>, + <code>std::array<char, N></code>, and + <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::pgsql::id_uuid> (u)); +db.query<object> ("buf = " + query::_val<odb::pgsql::id_bytea> (u)); +db.query<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& user, + const std::string& password, + const std::string& db, + const std::string& host = "", + unsigned int port = 0, + const std::string& extra_conninfo = "", + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string& password, + const std::string& db, + const std::string& host, + const std::string& socket_ext, + const std::string& extra_conninfo = "", + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& conninfo, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (int& argc, + char* argv[], + bool erase = false, + const std::string& extra_conninfo = "", + std::[auto|unique]_ptr<connection_factory> = 0); + + static void + print_usage (std::ostream&); + + public: + const std::string& + user () const; + + const std::string& + password () const; + + const std::string& + db () const; + + const std::string& + host () const; + + unsigned int + port () const; + + const std::string& + socket_ext () const; + + const std::string& + extra_conninfo () const; + + const std::string& + conninfo () const; + + public: + connection_ptr + connection (); + }; + } +} + </pre> + + <p>You will need to include the <code><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 <login> | --username <login> + --password <password> + --database <name> | --dbname <name> + --host <host> + --port <integer> + --options-file <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&); + connection (database&, PGconn*); + + PGconn* + handle (); + }; + + typedef details::shared_ptr<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&) = 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><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&); + pooled_connection (database_type&, PGconn*); + }; + + typedef details::shared_ptr<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 <odb/database.hxx> + +#include <odb/pgsql/database.hxx> +#include <odb/pgsql/connection-factory.hxx> + +int +main (int argc, char* argv[]) +{ + auto_ptr<odb::pgsql::connection_factory> f ( + new odb::pgsql::connection_pool_factory (20)); + + auto_ptr<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& + message () const; + + const std::string& + 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><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 oracle</code>" + (or "<code>-d 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 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 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<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 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<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<char></code>, + <code>std::vector<unsigned char></code>, + <code>char[N]</code>, <code>unsigned char[N]</code>, + <code>std::array<char, N></code>, and + <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::oracle::id_raw> (u)); +db.query<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& user, + const std::string& password, + const std::string& db, + ub2 charset = 0, + ub2 ncharset = 0, + OCIEnv* environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string& password, + const std::string& service, + const std::string& host, + unsigned int port = 0, + ub2 charset = 0, + ub2 ncharset = 0, + OCIEnv* environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (int& argc, + char* argv[], + bool erase = false, + ub2 charset = 0, + ub2 ncharset = 0, + OCIEnv* environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + static void + print_usage (std::ostream&); + + public: + const std::string& + user () const; + + const std::string& + password () const; + + const std::string& + db () const; + + const std::string& + service () const; + + const std::string& + 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><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 <login> + --password <password> + --database <connect-id> + --service <name> + --host <host> + --port <integer> + --options-file <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&); + connection (database&, OCISvcCtx*); + + OCISvcCtx* + handle (); + + OCIError* + error_handle (); + + details::buffer& + lob_buffer (); + }; + + typedef details::shared_ptr<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&) = 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><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&); + pooled_connection (database_type&, OCISvcCtx*); + }; + + typedef details::shared_ptr<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 <odb/database.hxx> + +#include <odb/oracle/database.hxx> +#include <odb/oracle/connection-factory.hxx> + +int +main (int argc, char* argv[]) +{ + auto_ptr<odb::oracle::connection_factory> f ( + new odb::oracle::connection_pool_factory (20)); + + auto_ptr<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& + message () const; + }; + + typedef std::vector<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><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 table</code> and <code>db 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<int> long_container_x_; + std::vector<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<int> long_container_x_; + + #pragma db table("long_class_name_cont_y") + std::vector<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 mssql</code>" + (or "<code>-d 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 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> — + to <code>NVARCHAR(256)</code>. Otherwise, <code>std::string</code> + is mapped to <code>VARCHAR(512)</code> and <code>std::wstring</code> + — to <code>NVARCHAR(512)</code>. Note also that you can + always change this mapping using the <code>db 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 short</code>, <code>unsigned int</code>, and + <code>unsigned long</code>/<code>unsigned long 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<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<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 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<char, N></code>, + <code>wchar_t[N]</code>, and <code>std::array<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<char></code>, + <code>std::vector<unsigned char></code>, + <code>char[N]</code>, <code>unsigned char[N]</code>, + <code>std::array<char, N></code>, and <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::mssql::id_binary> (u)); +db.query<object> ( + "uuid = " + query::_val<odb::mssql::id_uniqueidentifier> (u)); +db.query<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 — <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<object> query; +typedef odb::result<object> result; + +transaction t (db.begin ()); + +result r (db.query<object> (query::num < 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& user, + const std::string& password, + const std::string& db, + const std::string& server, + const std::string& driver = "", + const std::string& extra_connect_string = "", + transaction_isolation_type = isolation_read_committed, + SQLHENV environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string& password, + const std::string& db, + protocol_type protocol = protocol_auto, + const std::string& host = "", + const std::string& instance = "", + const std::string& driver = "", + const std::string& extra_connect_string = "", + transaction_isolation_type = isolation_read_committed, + SQLHENV environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string& password, + const std::string& db, + const std::string& host, + unsigned int port, + const std::string& driver = "", + const std::string& extra_connect_string = "", + transaction_isolation_type = isolation_read_committed, + SQLHENV environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (const std::string& connect_string, + transaction_isolation_type = isolation_read_committed, + SQLHENV environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + database (int& argc, + char* argv[], + bool erase = false, + const std::string& extra_connect_string = "", + transaction_isolation_type = isolation_read_committed, + SQLHENV environment = 0, + std::[auto|unique]_ptr<connection_factory> = 0); + + static void + print_usage (std::ostream&); + + public: + const std::string& + user () const; + + const std::string& + password () const; + + const std::string& + db () const; + + protocol_type + protocol () const; + + const std::string& + host () const; + + const std::string& + instance () const; + + unsigned int + port () const; + + const std::string& + server () const; + + const std::string& + driver () const; + + const std::string& + extra_connect_string () const; + + transaction_isolation_type + transaction_isolation () const; + + const std::string& + connect_string () const; + + SQLHENV + environment (); + + public: + connection_ptr + connection (); + }; + } +} + </pre> + + <p>You will need to include the <code><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 <login> + --password | -P <password> + --database | -d <name> + --server | -S <address> + --driver <name> + --options-file <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&); + connection (database&, SQLHDBC handle); + + SQLHDBC + handle (); + + details::buffer& + long_data_buffer (); + }; + + typedef details::shared_ptr<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&) = 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><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&); + pooled_connection (database_type&, SQLHDBC handle); + }; + + typedef details::shared_ptr<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 <odb/database.hxx> + +#include <odb/mssql/database.hxx> +#include <odb/mssql/connection-factory.hxx> + +int +main (int argc, char* argv[]) +{ + auto_ptr<odb::mssql::connection_factory> f ( + new odb::mssql::connection_pool_factory (20)); + + auto_ptr<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& + sqlstate () const; + + const std::string& + message () const; + }; + + typedef std::vector<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><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 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 <= @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<person_range> query; +typedef odb::result<person_range> result; + +transaction t (db.begin ()); + +result r ( + db.query<person_range> ( + query::_val (1) + "," + query::_val (18))); + +for (result::iterator i (r.begin ()); i != r.end (); ++i) + cerr << i->first << " " << i->last << " " << i->age << 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<person_age_range> ()); +cerr << ar.min_age << " " << ar.max_age << 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<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 + <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><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<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><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<employee> employee_; +}; + +#pragma db object +class employee +{ + ... + + #pragma db not_null + boost::shared_ptr<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<std::string> 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< + std::string, + mi::indexed_by< + mi::sequenced<>, + mi::ordered_unique<mi::identity<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< (const name&, const name&); + +#pragma db object +class person +{ + ... + + typedef + mi::multi_index_container< + name, + mi::indexed_by< + mi::ordered_unique<mi::identity<name> > + mi::ordered_non_unique< + mi::member<name, std::string, &name::first> + >, + mi::ordered_non_unique< + mi::member<name, std::string, &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 <boost/optional.hpp> + +#pragma db object +class person +{ + ... + + std::string first_; // TEXT NOT NULL + boost::optional<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><odb/boost/date-time/exceptions.hxx></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 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 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 20:45:54 GMT and + 2038-01-19 03:14:07 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 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 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><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 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 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 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 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<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><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<Employee> employee_; +}; + +#pragma db object +class Employee +{ + ... + + #pragma db not_null + QSharedPointer<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<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><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&</code>. In particular, this + means that we can use <code>QOdbList</code> instance + anywhere <code>const QList&</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 <typename T> +class QOdbList +{ + ... + + // Element access. + // + + //T& operator[] (int); + T& modify (int); + + //T& first(); + T& modifyFirst(); + + //T& last(); + T& modifyLast(); + + //T& front(); + T& modify_front(); + + //T& back(); + T& modify_back(); + + // Iterators. + // + typedef typename QList<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<T>::iterator base () const; + }; + + // Return QList iterators. The begin() functions mark all + // the elements as modified. + // + typename QList<T>::iterator mbegin (); + typename QList<T>::iterator modifyBegin (); + typename QList<T>::iterator mend (); + typename QList<T>::iterator modifyEnd (); + + // Interfacing with QList. + // + QOdbList (const QList<T>&); + QOdbList (QList<T>&&); // C++11 only. + + QOdbList& operator= (const QList<T>&); + QOdbList& operator= (QList<T>&&); + + operator const QList<T>& () const; + QList<T>& base (); + const QList<T>& base () const; + + // Change tracking. + // + bool _tracking () const; + void _start () const; + void _stop () const; + void _arm (transaction&) 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 <QtCore/QList> +#include <odb/qt/list.hxx> + +void f (const QList<int>&); + +QOdbList<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><odb/qt/list-iterator.hxx></code> and + <code><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><odb/qt/date-time/exceptions.hxx></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 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 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 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 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 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 Binary files differnew file mode 100644 index 0000000..511b198 --- /dev/null +++ b/odb/doc/odb-arch.png 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>" "</b></code> or + <code><b>' '</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>" "</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>' '</b></code> instead. + Unfortunately, Windows shell does not remove <code><b>' '</b></code> + from arguments when they are passed to applications. As a result you + may have to use <code><b>' '</b></code> for POSIX and + <code><b>" "</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 © $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 Binary files differnew file mode 100644 index 0000000..0063317 --- /dev/null +++ b/odb/doc/odb-flow.png 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="© $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..c440ca1 --- /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. + +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..8abce64 --- /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="© 2009-2024 Code Synthesis"/> + <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<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><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 + <...></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 (<>) 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>" "</b></code> or + <code><b>' '</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>" "</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>' '</b></code> instead. + Unfortunately, Windows shell does not remove <code><b>' '</b></code> + from arguments when they are passed to applications. As a result you + may have to use <code><b>' '</b></code> for POSIX and + <code><b>" "</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 © 2009-2024 Code Synthesis. + + <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/makefile b/odb/makefile deleted file mode 100644 index 16a3185..0000000 --- a/odb/makefile +++ /dev/null @@ -1,317 +0,0 @@ -# file : odb/makefile -# license : GNU GPL v3; see accompanying LICENSE file - -include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make - -# Plugin units. -# -cxx_ptun := \ -cxx-lexer.cxx \ -sql-token.cxx \ -sql-lexer.cxx \ -context.cxx \ -common.cxx \ -common-query.cxx \ -location.cxx \ -diagnostics.cxx \ -emitter.cxx \ -lookup.cxx \ -instance.cxx \ -include.cxx \ -header.cxx \ -inline.cxx \ -source.cxx \ -validator.cxx \ -processor.cxx \ -generator.cxx \ -parser.cxx \ -plugin.cxx \ -pragma.cxx - -# Relational. -# -cxx_ptun += \ -relational/changelog.cxx \ -relational/common.cxx \ -relational/common-query.cxx \ -relational/context.cxx \ -relational/processor.cxx \ -relational/header.cxx \ -relational/inline.cxx \ -relational/source.cxx \ -relational/model.cxx \ -relational/schema.cxx \ -relational/schema-source.cxx \ -relational/validator.cxx - -# Relational/MSSQL. -# -cxx_ptun += \ -relational/mssql/common.cxx \ -relational/mssql/context.cxx \ -relational/mssql/header.cxx \ -relational/mssql/inline.cxx \ -relational/mssql/source.cxx \ -relational/mssql/model.cxx \ -relational/mssql/schema.cxx - -# Relational/MySQL. -# -cxx_ptun += \ -relational/mysql/common.cxx \ -relational/mysql/context.cxx \ -relational/mysql/header.cxx \ -relational/mysql/inline.cxx \ -relational/mysql/source.cxx \ -relational/mysql/model.cxx \ -relational/mysql/schema.cxx - -# Relational/Oracle -# -cxx_ptun += \ -relational/oracle/common.cxx \ -relational/oracle/context.cxx \ -relational/oracle/header.cxx \ -relational/oracle/inline.cxx \ -relational/oracle/source.cxx \ -relational/oracle/model.cxx \ -relational/oracle/schema.cxx - -# Relational/PostgreSQL -# -cxx_ptun += \ -relational/pgsql/common.cxx \ -relational/pgsql/context.cxx \ -relational/pgsql/header.cxx \ -relational/pgsql/inline.cxx \ -relational/pgsql/source.cxx \ -relational/pgsql/model.cxx \ -relational/pgsql/schema.cxx - -# Relational/SQLite. -# -cxx_ptun += \ -relational/sqlite/common.cxx \ -relational/sqlite/context.cxx \ -relational/sqlite/header.cxx \ -relational/sqlite/inline.cxx \ -relational/sqlite/source.cxx \ -relational/sqlite/model.cxx \ -relational/sqlite/schema.cxx - -# Semantics. -# -cxx_ptun += \ -semantics/class.cxx \ -semantics/class-template.cxx \ -semantics/derived.cxx \ -semantics/elements.cxx \ -semantics/enum.cxx \ -semantics/fundamental.cxx \ -semantics/namespace.cxx \ -semantics/template.cxx \ -semantics/union.cxx \ -semantics/union-template.cxx \ -semantics/unit.cxx - -# Semantics/Relational. -# -cxx_ptun += \ -semantics/relational/changelog.cxx \ -semantics/relational/changeset.cxx \ -semantics/relational/column.cxx \ -semantics/relational/elements.cxx \ -semantics/relational/foreign-key.cxx \ -semantics/relational/index.cxx \ -semantics/relational/key.cxx \ -semantics/relational/model.cxx \ -semantics/relational/primary-key.cxx \ -semantics/relational/table.cxx - -# Traversal. -# -cxx_ptun += \ -traversal/class.cxx \ -traversal/class-template.cxx \ -traversal/derived.cxx \ -traversal/elements.cxx \ -traversal/enum.cxx \ -traversal/template.cxx \ -traversal/union-template.cxx - -# Traversal/Relational. -# -cxx_ptun += \ -traversal/relational/changelog.cxx \ -traversal/relational/key.cxx - -# Driver units. -# -cxx_dtun := odb.cxx - -# Common units. -# -cxx_ctun := \ -option-types.cxx \ -option-functions.cxx \ -profile.cxx \ -semantics/relational/name.cxx \ -semantics/relational/deferrable.cxx - -# Options file. -# -cli_tun := options.cli - -# -# -cxx_pobj := $(addprefix $(out_base)/,$(cxx_ptun:.cxx=.o)) -cxx_dobj := $(addprefix $(out_base)/,$(cxx_dtun:.cxx=.o)) -cxx_cobj := $(addprefix $(out_base)/,$(cxx_ctun:.cxx=.o) $(cli_tun:.cli=.o)) -cxx_pod := $(cxx_pobj:.o=.o.d) -cxx_dod := $(cxx_dobj:.o=.o.d) -cxx_cod := $(cxx_cobj:.o=.o.d) - - -odb := $(out_base)/odb -odb_so := $(out_base)/odb.so - -# Dummy library to force driver timestamp update when the plugin DSO -# changes. -# -odb.l := $(out_base)/odb.l -clean := $(out_base)/.clean -dist := $(out_base)/.dist - -# Import. -# -$(call import,\ - $(scf_root)/import/cli/stub.make,\ - cli: cli,cli-rules: cli_rules) - -$(call import,\ - $(scf_root)/import/libcutl/stub.make,\ - l: cutl.l,cpp-options: cutl.l.cpp-options) - -# Build. -# -$(odb): $(cxx_dobj) $(cxx_cobj) $(odb.l) $(cutl.l) -$(odb_so): $(cxx_pobj) $(cxx_cobj) $(cutl.l) - -$(cxx_pobj) $(cxx_dobj) $(cxx_cobj) $(cxx_pod) $(cxx_dod) $(cxx_cod): \ -$(cutl.l.cpp-options) - -genf := $(cli_tun:.cli=.hxx) $(cli_tun:.cli=.ixx) $(cli_tun:.cli=.cxx) -gen := $(addprefix $(out_base)/,$(genf)) - -# Don't try to depend on the installed executable. -# -ifneq ($(cli),cli) -$(gen): $(cli) -endif - -$(gen): cli := $(cli) -$(gen): cli_options += \ ---generate-modifier \ ---generate-specifier \ ---generate-description \ ---suppress-undocumented \ ---generate-file-scanner \ ---include-with-brackets \ ---include-prefix odb \ ---guard-prefix ODB \ ---cxx-prologue '\#include <odb/option-parsers.hxx>' - -$(call include-dep,$(cxx_pod) $(cxx_dod) $(cxx_cod),\ -$(cxx_pobj) $(cxx_dobj) $(cxx_cobj),$(gen)) - -# Alias for default target. -# -$(out_base)/: $(odb) - -# Install. -# -$(install): $(odb) - $(call install-exec,$<,$(install_bin_dir)/odb) - $(call install-exec,$<.so,$(install_bin_dir)/odb.so) - -# Dist. -# -$(dist): export plugin_sources := $(cxx_ptun) -$(dist): export driver_sources := $(cxx_dtun) -$(dist): common_sources_dist := $(cxx_ctun) -$(dist): export common_sources := $(common_sources_dist) $(cli_tun:.cli=.cxx) -$(dist): headers_dist = $(subst $(src_base)/,,$(shell find $(src_base) \ --name '*.hxx' -o -name '*.ixx' -o -name '*.txx')) -$(dist): gen_headers := $(cli_tun:.cli=.hxx) $(cli_tun:.cli=.ixx) -$(dist): export headers = $(sort $(headers_dist) $(gen_headers)) -$(dist): gen_dist := $(gen) -$(dist): data_dist := $(cli_tun) -$(dist): export extra_dist := $(data_dist) - -$(dist): $(gen) - $(call dist-data,$(plugin_sources) $(driver_sources) \ -$(common_sources_dist) $(headers_dist) $(gen_dist) $(data_dist)) - $(call meta-automake) - -# Clean. -# -$(clean): \ - $(odb).o.clean \ - $(addsuffix .cxx.clean,$(cxx_pobj)) \ - $(addsuffix .cxx.clean,$(cxx_dobj)) \ - $(addsuffix .cxx.clean,$(cxx_cobj)) \ - $(addsuffix .cxx.clean,$(cxx_pod)) \ - $(addsuffix .cxx.clean,$(cxx_dod)) \ - $(addsuffix .cxx.clean,$(cxx_cod)) \ - $(addprefix $(out_base)/,$(cli_tun:.cli=.cxx.cli.clean)) - $(call message,rm $$1,rm -f $$1,$(out_base)/odb.so) - $(call message,,rm -f $(out_base)/odb.l) - -# Generated .gitignore. -# -#ifeq ($(out_base),$(src_base)) -#$(odb): | $(out_base)/.gitignore -# -#$(out_base)/.gitignore: files := odb odb.so $(genf) -#$(clean): $(out_base)/.gitignore.clean -# -#$(call include,$(bld_root)/git/gitignore.make) -#endif - -# Rules. -# -$(call include,$(bld_root)/dist.make) -$(call include,$(bld_root)/meta/automake.make) -$(call include,$(bld_root)/install.make) - -$(call include,$(cli_rules)) -$(call include,$(bld_root)/cxx/cxx-d.make) -$(call include,$(bld_root)/cxx/cxx-o.make) -$(call include,$(bld_root)/cxx/o-e.make) - -# Custom rules for the plugin and the driver. -# -ifdef cxx_gnu - -$(odb) $(odb_so) \ -$(cxx_pobj) $(cxx_pod) \ -$(cxx_cobj) $(cxx_cod) \ -$(cxx_dobj) $(cxx_dod): cxx_extra_options += -std=c++0x - -$(cxx_pobj) $(cxx_pod) \ -$(cxx_cobj) $(cxx_cod): cxx_pic_options := -fPIC - -$(cxx_cobj) $(cxx_cod): cpp_options := -I$(src_root) -$(cxx_dobj) $(cxx_dod): cpp_options := -I$(src_root) '-DODB_GXX_NAME="$(cxx_gnu)"' -$(cxx_pobj) $(cxx_pod): cpp_options := -I$(src_root) \ --I$(shell $(cxx_gnu) -print-file-name=plugin)/include - -$(odb_so): - $(call message,ld $@,$(ld) \ -$(cxx_extra_options) $(ld_options) $(cxx_ld_extra_options) -shared -o $@ \ -$(foreach f,$^,$(if $(patsubst %.l,,$f),$f,$(call expand-l,$f))) $(cxx_extra_libs)) - -$(odb.l): $(odb_so) - $(call message,,touch $@) - -endif diff --git a/odb/manifest b/odb/manifest new file mode 100644 index 0000000..2ef8b08 --- /dev/null +++ b/odb/manifest @@ -0,0 +1,34 @@ +: 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: all +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 + +# @@ TMP Bump the toolchain version to 0.17.0 after it is released. +# +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..96eb95f --- /dev/null +++ b/odb/odb/buildfile @@ -0,0 +1,172 @@ +# 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 -version -options -pregenerated/**} \ + hxx{version} $libs + +# Include the generated version header into the distribution (so that we don't +# pick up an installed one) and don't remove it when cleaning in src (so that +# clean results in a state identical to distributed). +# +hxx{version}: in{version} $src_root/manifest +hxx{version}: +{ + dist = true + clean = ($src_root != $out_root) +} + +# Build options. +# +# Note: escape backslashes in gxx_name. +# +cxx.poptions += "-I$plugin_dir/include" +cxx.poptions += "-DODB_GXX_NAME=\"$regex.replace($gxx_name, '\\', '\\\\')\"" + +## 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/common-query.cxx b/odb/odb/common-query.cxx index 517c92c..0b5d063 100644 --- a/odb/common-query.cxx +++ b/odb/odb/common-query.cxx @@ -1031,14 +1031,30 @@ traverse (type& c) { // If we have the extern symbol, generate extern template declarations. // - if (!ext.empty ()) + // Without a declaration of explicit template instantiation Clang issues + // -Wundefined-var-template. Note that extern template is only available + // since C++11 and this only appears to be an issue in dynamic multi- + // database support for id_common. + // + // Note also that this break our support for multi-file circular + // dependencies (see odb-tests/common/circule/multiple/). + // + if (!ext.empty () || + (multi_dynamic && + db == database::common && + options.std () >= cxx_version::cxx11)) { 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 + const string& guard ( + !ext.empty () + ? ext + : make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF")); + + os << (!ext.empty () ? "#ifdef " : "#ifndef ") << guard << endl << endl; if (has_ptr) @@ -1055,7 +1071,7 @@ traverse (type& c) if (!reuse_abst) generate_inst (c); - os << "#endif // " << ext << endl + os << "#endif // " << guard << endl << endl; } } @@ -1128,7 +1144,7 @@ generate_inst (type& c) string const& type (class_fq_name (c)); // Explicit template instantiations. Here is what we need to - // instantiate + // instantiate: // // 1. Reuse inheritance bases all the way to the ultimate base. // Unlike poly inheritance, reuse inheritance uses the table @@ -1216,14 +1232,25 @@ generate_decl (type& c) // 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 ()) + // See query_columns_type::traverse() for background. + // + if (obj_count != 0 && multi_dynamic && + (!ext.empty () || + (multi_dynamic && + db == database::common && + options.std () >= cxx_version::cxx11))) { - os << "#ifdef " << ext << endl + const string& guard ( + !ext.empty () + ? ext + : make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF")); + + os << (!ext.empty () ? "#ifdef " : "#ifndef ") << guard << endl << endl; generate_inst (c); - os << "#endif // " << ext << endl + os << "#endif // " << guard << endl << endl; } diff --git a/odb/common-query.hxx b/odb/odb/common-query.hxx index e90dd69..e90dd69 100644 --- a/odb/common-query.hxx +++ b/odb/odb/common-query.hxx diff --git a/odb/common.cxx b/odb/odb/common.cxx index 63e49ad..63e49ad 100644 --- a/odb/common.cxx +++ b/odb/odb/common.cxx diff --git a/odb/common.hxx b/odb/odb/common.hxx index 149def7..149def7 100644 --- a/odb/common.hxx +++ b/odb/odb/common.hxx diff --git a/odb/context.cxx b/odb/odb/context.cxx index e220d0e..f678e64 100644 --- a/odb/context.cxx +++ b/odb/odb/context.cxx @@ -1472,7 +1472,7 @@ utype (semantics::data_member& m, } } - if (s->global_scope ()) + if (!s->named_p () || s->global_scope ()) break; } @@ -1882,8 +1882,13 @@ schema (semantics::scope& s) const namespace_* ns (dynamic_cast<namespace_*> (ps)); - if (ns == 0) - continue; // Some other scope. + if (ns == 0) // Some other scope. + { + if (!ps->named_p ()) + break; + + continue; + } if (ns->extension ()) ns = &ns->original (); @@ -1920,7 +1925,8 @@ schema (semantics::scope& s) const n.swap (r); } - if (r.fully_qualified () || ns->global_scope ()) + if (r.fully_qualified () || + ns->global_scope ()) // Note: namespaces always named. break; } @@ -1952,8 +1958,13 @@ table_name_prefix (semantics::scope& s) const namespace_* ns (dynamic_cast<namespace_*> (ps)); - if (ns == 0) - continue; // Some other scope. + if (ns == 0) // Some other scope. + { + if (!ps->named_p ()) + break; + + continue; + } if (ns->extension ()) ns = &ns->original (); @@ -1964,7 +1975,7 @@ table_name_prefix (semantics::scope& s) const r = n.uname () + r; } - if (ns->global_scope ()) + if (ns->global_scope ()) // Note: namespaces always named. break; } @@ -2130,6 +2141,90 @@ table_name (semantics::data_member& m, table_prefix const& p) const 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:: @@ -2866,8 +2961,13 @@ strlit (string const& str) void context:: inst_header (bool decl, bool omit_exp) { - if (decl && !ext.empty ()) - os << ext << " "; + if (decl) + { + if (!ext.empty ()) + os << ext << " "; + else + os << "extern "; + } os << "template struct"; diff --git a/odb/context.hxx b/odb/odb/context.hxx index da975f3..ec4505b 100644 --- a/odb/context.hxx +++ b/odb/odb/context.hxx @@ -17,8 +17,8 @@ #include <cstddef> // std::size_t #include <iostream> -#include <cutl/re.hxx> -#include <cutl/shared-ptr.hxx> +#include <libcutl/re.hxx> +#include <libcutl/shared-ptr.hxx> #include <odb/options.hxx> #include <odb/features.hxx> @@ -1342,6 +1342,14 @@ public: 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 diff --git a/odb/context.ixx b/odb/odb/context.ixx index 5018743..5018743 100644 --- a/odb/context.ixx +++ b/odb/odb/context.ixx diff --git a/odb/cxx-lexer.cxx b/odb/odb/cxx-lexer.cxx index 67493b7..e4e0229 100644 --- a/odb/cxx-lexer.cxx +++ b/odb/odb/cxx-lexer.cxx @@ -216,7 +216,9 @@ cxx_string_lexer () linemap_init (&line_map_); #endif -#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6 +#if BUILDING_GCC_MAJOR >= 14 + line_map_.m_round_alloc_size = ggc_round_alloc_size; +#elif BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6 line_map_.round_alloc_size = ggc_round_alloc_size; #endif diff --git a/odb/cxx-lexer.hxx b/odb/odb/cxx-lexer.hxx index ea16132..ea16132 100644 --- a/odb/cxx-lexer.hxx +++ b/odb/odb/cxx-lexer.hxx diff --git a/odb/cxx-token.hxx b/odb/odb/cxx-token.hxx index 34b28d4..34b28d4 100644 --- a/odb/cxx-token.hxx +++ b/odb/odb/cxx-token.hxx diff --git a/odb/diagnostics.cxx b/odb/odb/diagnostics.cxx index 57166bb..57166bb 100644 --- a/odb/diagnostics.cxx +++ b/odb/odb/diagnostics.cxx diff --git a/odb/diagnostics.hxx b/odb/odb/diagnostics.hxx index 91ec068..46f2272 100644 --- a/odb/diagnostics.hxx +++ b/odb/odb/diagnostics.hxx @@ -10,7 +10,7 @@ #include <cstddef> #include <iostream> -#include <cutl/fs/path.hxx> +#include <libcutl/fs/path.hxx> #include <odb/location.hxx> diff --git a/odb/emitter.cxx b/odb/odb/emitter.cxx index d6d8eac..d6d8eac 100644 --- a/odb/emitter.cxx +++ b/odb/odb/emitter.cxx diff --git a/odb/emitter.hxx b/odb/odb/emitter.hxx index 1071dab..1071dab 100644 --- a/odb/emitter.hxx +++ b/odb/odb/emitter.hxx diff --git a/odb/features.hxx b/odb/odb/features.hxx index 1093684..1093684 100644 --- a/odb/features.hxx +++ b/odb/odb/features.hxx diff --git a/odb/gcc-fwd.hxx b/odb/odb/gcc-fwd.hxx index 83d3746..83d3746 100644 --- a/odb/gcc-fwd.hxx +++ b/odb/odb/gcc-fwd.hxx diff --git a/odb/gcc.hxx b/odb/odb/gcc.hxx index af0e2a0..e5fecef 100644 --- a/odb/gcc.hxx +++ b/odb/odb/gcc.hxx @@ -163,6 +163,7 @@ gcc_tree_code_name (gcc_tree_code_type tc) {return tree_code_name[tc];} // 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. @@ -192,4 +193,23 @@ LAST_SOURCE_LINE (const line_map_ordinary* 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/generate.hxx b/odb/odb/generate.hxx index b3b9c43..b3b9c43 100644 --- a/odb/generate.hxx +++ b/odb/odb/generate.hxx diff --git a/odb/generator.cxx b/odb/odb/generator.cxx index 09a971c..f0b92ab 100644 --- a/odb/generator.cxx +++ b/odb/odb/generator.cxx @@ -9,19 +9,14 @@ #include <sstream> #include <iostream> -#include <cutl/fs/auto-remove.hxx> +#include <libcutl/fs/auto-remove.hxx> -#include <cutl/compiler/code-stream.hxx> -#include <cutl/compiler/cxx-indenter.hxx> -#include <cutl/compiler/sloc-counter.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 <cutl/xml/parser.hxx> -#include <cutl/xml/serializer.hxx> -#endif #include <odb/version.hxx> #include <odb/context.hxx> @@ -520,11 +515,16 @@ generate (options const& ops, "// Begin prologue.\n//", "//\n// End prologue."); - // Version check. + // Version check (see similar check in odb.cxx for background). // hxx << "#include <odb/version.hxx>" << endl << endl - << "#if (ODB_VERSION != " << ODB_VERSION << "UL)" << endl +#if 1 + << "#if ODB_VERSION != " << ODB_VERSION << "UL" << endl +#else + << "#if LIBODB_VERSION_FULL != " << ODB_COMPILER_VERSION << "ULL || \\" << endl + << " LIBODB_SNAPSHOT != " << ODB_COMPILER_SNAPSHOT << "ULL" << endl +#endif << "#error ODB runtime version mismatch" << endl << "#endif" << endl << endl; diff --git a/odb/generator.hxx b/odb/odb/generator.hxx index 205043b..205043b 100644 --- a/odb/generator.hxx +++ b/odb/odb/generator.hxx diff --git a/odb/header.cxx b/odb/odb/header.cxx index fad28b3..dacdd1d 100644 --- a/odb/header.cxx +++ b/odb/odb/header.cxx @@ -777,7 +777,9 @@ namespace header << "#include <odb/wrapper-traits.hxx>" << endl << "#include <odb/pointer-traits.hxx>" << endl; -#ifndef ODB_BUILD2 + // @@ TMP: drop after 2.5.0. + // +#if 0 if (ctx.options.std () == cxx_version::cxx98) { // In case of a boost TR1 implementation, we cannot distinguish diff --git a/odb/include.cxx b/odb/odb/include.cxx index 5fda7c0..5fda7c0 100644 --- a/odb/include.cxx +++ b/odb/odb/include.cxx diff --git a/odb/inline.cxx b/odb/odb/inline.cxx index 15482aa..15482aa 100644 --- a/odb/inline.cxx +++ b/odb/odb/inline.cxx diff --git a/odb/instance.cxx b/odb/odb/instance.cxx index 2d10239..2d10239 100644 --- a/odb/instance.cxx +++ b/odb/odb/instance.cxx diff --git a/odb/instance.hxx b/odb/odb/instance.hxx index 2b47939..2b47939 100644 --- a/odb/instance.hxx +++ b/odb/odb/instance.hxx diff --git a/odb/location.cxx b/odb/odb/location.cxx index 23bd7ef..23bd7ef 100644 --- a/odb/location.cxx +++ b/odb/odb/location.cxx diff --git a/odb/location.hxx b/odb/odb/location.hxx index b16018b..cc59196 100644 --- a/odb/location.hxx +++ b/odb/odb/location.hxx @@ -7,7 +7,7 @@ #include <odb/gcc-fwd.hxx> #include <cstddef> -#include <cutl/fs/path.hxx> +#include <libcutl/fs/path.hxx> struct location { diff --git a/odb/lookup.cxx b/odb/odb/lookup.cxx index a54ef15..a54ef15 100644 --- a/odb/lookup.cxx +++ b/odb/odb/lookup.cxx diff --git a/odb/lookup.hxx b/odb/odb/lookup.hxx index 86c65b2..86c65b2 100644 --- a/odb/lookup.hxx +++ b/odb/odb/lookup.hxx diff --git a/odb/odb.cxx b/odb/odb/odb.cxx index 225cc21..9899262 100644 --- a/odb/odb.cxx +++ b/odb/odb/odb.cxx @@ -18,7 +18,7 @@ # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> // CreatePipe, CreateProcess +# include <windows.h> // CreatePipe, CreateProcess, GetTemp*, MAX_PATH # include <io.h> // _open_osfhandle # include <fcntl.h> // _O_TEXT #endif @@ -31,7 +31,8 @@ #include <iostream> #include <ext/stdio_filebuf.h> -#include <cutl/fs/path.hxx> +#include <libcutl/fs/path.hxx> +#include <libcutl/fs/auto-remove.hxx> #include <odb/version.hxx> #include <odb/options.hxx> @@ -44,6 +45,7 @@ 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; @@ -90,6 +92,16 @@ struct process_info 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 @@ -259,9 +271,7 @@ main (int argc, char* argv[]) file = dd / file; // For diagnostics. } - int ac (3); - const char* av[4] = {argv[0], "--file", file.string ().c_str (), 0}; - cli::argv_file_scanner s (ac, const_cast<char**> (av), "--file"); + cli::argv_file_scanner s (file.string ()); bool first_x (true); @@ -381,11 +391,15 @@ main (int argc, char* argv[]) // 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 (int i = 1; i < argc; ++i) + for (cli::argv_file_scanner scan (argc, argv, "--options-file"); + scan.more (); ) { - string a (argv[i]); + string a (scan.next ()); size_t n (a.size ()); // -v @@ -399,14 +413,14 @@ main (int argc, char* argv[]) // else if (a == "-x") { - if (++i == argc || argv[i][0] == '\0') + const char* v; + if (!scan.more () || (v = scan.next ())[0] == '\0') { - e << argv[0] << ": error: expected argument for the -x option" << endl; + e << argv[0] << ": error: expected argument for the -x option" + << endl; return 1; } - a = argv[i]; - if (first_x) { first_x = false; @@ -414,13 +428,13 @@ main (int argc, char* argv[]) // 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; + if (v[0] != '-') + args[0] = v; else - args.push_back (a); + args.push_back (v); } else - args.push_back (a); + args.push_back (v); } // -I // @@ -430,14 +444,15 @@ main (int argc, char* argv[]) if (n == 2) // -I /path { - if (++i == argc || argv[i][0] == '\0') + 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 (argv[i]); + args.push_back (v); } } // -isystem, -iquote, -idirafter, and -framework (Mac OS X) @@ -449,14 +464,15 @@ main (int argc, char* argv[]) { args.push_back (a); - if (++i == argc || argv[i][0] == '\0') + 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 (argv[i]); + args.push_back (v); } // -D // @@ -466,14 +482,15 @@ main (int argc, char* argv[]) if (n == 2) // -D macro { - if (++i == argc || argv[i][0] == '\0') + 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 (argv[i]); + args.push_back (v); } } // -U @@ -484,14 +501,15 @@ main (int argc, char* argv[]) if (n == 2) // -U macro { - if (++i == argc || argv[i][0] == '\0') + 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 (argv[i]); + args.push_back (v); } } // Store everything else in a list so that we can parse it with the @@ -521,7 +539,6 @@ main (int argc, char* argv[]) 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 @@ -557,7 +574,6 @@ main (int argc, char* argv[]) _putenv (v.c_str ()); } #endif -#endif args[7] = "-fplugin=" + plugin.string (); } @@ -577,7 +593,7 @@ main (int argc, char* argv[]) int ac (static_cast<int> (av.size ())); cli::argv_file_scanner::option_info oi[3]; - oi[0].option = "--options-file"; + oi[0].option = "--options-file"; // Keep in case profile uses it. oi[0].search_func = 0; oi[1].option = "-p"; oi[2].option = "--profile"; @@ -604,8 +620,9 @@ main (int argc, char* argv[]) 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.version = [string] '" << ODB_COMPILER_VERSION_FULL << '\'' << endl + << "odb.checksum = [string] '" << ODB_COMPILER_VERSION_FULL << '\'' << endl + << "odb.environment = [strings] CPATH CPLUS_INCLUDE_PATH GCC_EXEC_PREFIX COMPILER_PATH" << endl; return 0; } @@ -617,13 +634,9 @@ main (int argc, char* argv[]) 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. " + ODB_COMPILER_VERSION_STR << endl + << "Copyright (c) " << ODB_COPYRIGHT << "." << endl + << "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; @@ -688,6 +701,11 @@ main (int argc, char* argv[]) args[3] = "-std=c++1z"; break; } + case cxx_version::cxx20: + { + args[3] = "-std=c++2a"; + break; + } } } @@ -718,7 +736,7 @@ main (int argc, char* argv[]) { ostringstream ostr; - ostr << ODB_COMPILER_VERSION; + ostr << ODB_COMPILER_VERSION_OLD; args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ()); } @@ -888,6 +906,14 @@ main (int argc, char* argv[]) } } + // 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)); { @@ -925,9 +951,23 @@ main (int argc, char* argv[]) // Make sure ODB compiler and libodb versions are compatible. // + // Note that the snapshot comparision is only necessary of the + // version is a pre-release but having it always won't hurt (it + // will be 0 for final versions). + // + // After some experience with requiring the exact version match we + // found it just too tedious and went back to only comparing the + // interface version (we could support both with an option; see + // also similar code in generator.cxx). + // os << "#include <odb/version.hxx>" << endl << endl - << "#if ODB_VERSION != " << ODB_VERSION << endl +#if 1 + << "#if ODB_VERSION != " << ODB_VERSION << "UL" << endl +#else + << "#if LIBODB_VERSION_FULL != " << ODB_COMPILER_VERSION << "ULL" + " || LIBODB_SNAPSHOT != " << ODB_COMPILER_SNAPSHOT << "ULL" << endl +#endif << "# error incompatible ODB compiler and runtime " << "versions" << endl << "#endif" << endl @@ -949,13 +989,34 @@ main (int argc, char* argv[]) // operator< test, used in validator. // + // Note that typeof() cannot be used in the function signature + // directly so we have to go though lt_operator_type. This means + // we get diagnostics from the compiler (followed by ours) but + // it's doesn't look bad plus C++98 support is on its way out. + // 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; + << "const T&" << endl + << "instance ();" << endl + << endl; + + if (ops.std () == cxx_version::cxx98) + { + os << "template <typename T>" << endl + << "struct lt_operator_type" << endl + << "{" << endl + << "typedef __typeof__ (instance<T> () < instance<T> ()) R;" << endl + << "};" << endl + << endl + << "template <typename T>" << endl + << "typename lt_operator_type<T>::R" << endl + << "has_lt_operator ();" << endl; + } + else + { + os << "template <typename T>" << endl + << "decltype (instance<T> () < instance<T> ())" << endl + << "has_lt_operator ();" << endl; + } os << "}" << endl << "}" << endl; @@ -1112,7 +1173,8 @@ main (int argc, char* argv[]) // TR1 wrapper/pointer traits. // -#ifndef ODB_BUILD2 + // @@ TMP: drop after 2.5.0. +#if 0 if (ops.std () == cxx_version::cxx98) os << endl << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl @@ -1321,6 +1383,11 @@ profile_paths (strings const& sargs, char const* name) 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. @@ -1552,16 +1619,14 @@ plugin_path (path const& drv, // 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) +//#elif defined(__APPLE__) // 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. + // without the .exe extension on Windows). // #ifdef _WIN32 string b (drv.leaf ().base ().string ()); @@ -1569,10 +1634,6 @@ plugin_path (path const& drv, 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 ()) @@ -1584,19 +1645,7 @@ plugin_path (path const& drv, 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; - } + path pp; #ifdef ODB_GCC_PLUGIN_DIR // Plugin should be installed into the GCC default plugin directory. @@ -1605,9 +1654,9 @@ plugin_path (path const& drv, // was only added in GCC 4.6 so in order to support 4.5 we will have to // emulate it ourselves. // - if (!lt) + //@@ TMP: drop this after 2.5.0 since we no longer support GCC < 5. + // { - //@@ BUILD2: if/when dropping old GCC should just get rid of this. #if 1 // First get the default GCC plugin directory. // @@ -1657,10 +1706,8 @@ plugin_path (path const& drv, #endif } #elif defined (ODB_PLUGIN_PATH) - // If we were given a plugin path, use that unless we are running - // via libtool. + // If we were given a plugin path, use that. // - if (!lt) { string rp (ODB_PLUGIN_PATH); if (!rp.empty ()) @@ -1835,6 +1882,134 @@ print_error (char const* name) 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) { @@ -1900,26 +2075,7 @@ start_process (char const* args[], char const* name, bool err, bool out) if (p != args) cmd_line += ' '; - // On Windows we need to protect values with spaces using quotes. - // Since there could be actual quotes in the value, we need to - // escape them. - // - string a (*p); - 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 += '"'; + append_quoted (cmd_line, *p); } // Prepare other info. diff --git a/odb/option-functions.cxx b/odb/odb/option-functions.cxx index 00d36cd..7eda934 100644 --- a/odb/option-functions.cxx +++ b/odb/odb/option-functions.cxx @@ -101,7 +101,7 @@ process_options (options& o) 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 ())); + o.changelog_file_suffix ().insert (make_pair (db, '-' + db.string ())); } // Set default --default-database value. diff --git a/odb/option-functions.hxx b/odb/odb/option-functions.hxx index a4b072c..a4b072c 100644 --- a/odb/option-functions.hxx +++ b/odb/odb/option-functions.hxx diff --git a/odb/option-parsers.hxx b/odb/odb/option-parsers.hxx index 2daa6eb..2daa6eb 100644 --- a/odb/option-parsers.hxx +++ b/odb/odb/option-parsers.hxx diff --git a/odb/option-types.cxx b/odb/odb/option-types.cxx index aac0288..c4a030b 100644 --- a/odb/option-types.cxx +++ b/odb/odb/option-types.cxx @@ -18,7 +18,8 @@ static const char* cxx_version_[] = "c++98", "c++11", "c++14", - "c++17" + "c++17", + "c++20" }; string cxx_version:: @@ -43,6 +44,8 @@ operator>> (istream& is, cxx_version& v) 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); } diff --git a/odb/option-types.hxx b/odb/odb/option-types.hxx index 4739892..869fc83 100644 --- a/odb/option-types.hxx +++ b/odb/odb/option-types.hxx @@ -22,7 +22,8 @@ struct cxx_version cxx98, cxx11, cxx14, - cxx17 + cxx17, + cxx20 }; cxx_version (value v = value (0)) : v_ (v) {} diff --git a/odb/options.cli b/odb/odb/options.cli index bb8797b..17ee438 100644 --- a/odb/options.cli +++ b/odb/odb/options.cli @@ -309,11 +309,15 @@ class options // 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}, and \cb{c++14}." + Valid values are \cb{c++98} (default), \cb{c++11}, \cb{c++14}, + \cb{c++17}, and \cb{c++20}." }; // Diagnostics. diff --git a/odb/parser.cxx b/odb/odb/parser.cxx index 58388c9..c026808 100644 --- a/odb/parser.cxx +++ b/odb/odb/parser.cxx @@ -176,6 +176,7 @@ private: unit* unit_; scope* scope_; + vector<scope*> class_scopes_; // Current hierarchy of class-like scopes. size_t error_; decl_set decls_; @@ -263,6 +264,11 @@ emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub) 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)); @@ -557,6 +563,8 @@ emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub) diagnose_unassoc_pragmas (decls); scope_ = prev_scope; + class_scopes_.pop_back (); + return *c_node; } @@ -583,6 +591,8 @@ emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub) 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. // @@ -728,6 +738,7 @@ emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub) diagnose_unassoc_pragmas (decls); scope_ = prev_scope; + class_scopes_.pop_back (); return *u_node; } @@ -942,9 +953,10 @@ collect (tree ns) if (!DECL_IS_BUILTIN (decl) || DECL_NAMESPACE_STD_P (decl)) { + tree dn (DECL_NAME (decl)); + if (trace) { - tree dn (DECL_NAME (decl)); char const* name (dn ? IDENTIFIER_POINTER (dn) : "<anonymous>"); ts << "namespace " << name << " at " @@ -952,7 +964,12 @@ collect (tree ns) << DECL_SOURCE_LINE (decl) << endl; } - collect (decl); + // 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); } } } @@ -1079,6 +1096,8 @@ emit () break; } } + + assert (class_scopes_.empty ()); } // Diagnose any position pragmas that haven't been associated. @@ -1201,6 +1220,58 @@ emit_type_decl (tree 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. @@ -1327,6 +1398,8 @@ emit_class_template (tree t, bool stub) 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. @@ -1382,6 +1455,7 @@ emit_class_template (tree t, bool stub) diagnose_unassoc_pragmas (decls); scope_ = prev_scope; + class_scopes_.pop_back (); return *ct_node; } @@ -1410,6 +1484,8 @@ emit_union_template (tree t, bool stub) 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. @@ -1465,6 +1541,7 @@ emit_union_template (tree t, bool stub) diagnose_unassoc_pragmas (decls); scope_ = prev_scope; + class_scopes_.pop_back (); return *ut_node; } @@ -2053,9 +2130,12 @@ emit_type_name (tree type, bool direct) if (i != 0) id += ", "; - // Assume type-only arguments. + // Assume integer and type-only arguments. // - id += emit_type_name (a); + if (TREE_CODE (a) == INTEGER_CST) + id += to_string (integer_value (a)); + else + id += emit_type_name (a); } id += '>'; diff --git a/odb/parser.hxx b/odb/odb/parser.hxx index b26c8f1..b26c8f1 100644 --- a/odb/parser.hxx +++ b/odb/odb/parser.hxx diff --git a/odb/plugin.cxx b/odb/odb/plugin.cxx index aff90ee..c065a8a 100644 --- a/odb/plugin.cxx +++ b/odb/odb/plugin.cxx @@ -14,8 +14,8 @@ #include <cassert> #include <iostream> -#include <cutl/re.hxx> -#include <cutl/fs/path.hxx> +#include <libcutl/re.hxx> +#include <libcutl/fs/path.hxx> #include <odb/pragma.hxx> #include <odb/parser.hxx> diff --git a/odb/pragma.cxx b/odb/odb/pragma.cxx index bd1d848..6668733 100644 --- a/odb/pragma.cxx +++ b/odb/odb/pragma.cxx @@ -1643,8 +1643,6 @@ handle_pragma (cxx_lexer& l, return; } - // base - // if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST) { error (l) << "unsigned integer expected as batch size" << endl; diff --git a/odb/pragma.hxx b/odb/odb/pragma.hxx index 45b3528..0d4d3f1 100644 --- a/odb/pragma.hxx +++ b/odb/odb/pragma.hxx @@ -13,9 +13,9 @@ #include <odb/option-types.hxx> // database -#include <cutl/container/any.hxx> -#include <cutl/container/multi-index.hxx> -#include <cutl/compiler/context.hxx> +#include <libcutl/container/any.hxx> +#include <libcutl/container/multi-index.hxx> +#include <libcutl/compiler/context.hxx> struct virt_declaration { diff --git a/odb/options.cxx b/odb/odb/pregenerated/odb/options.cxx index ec6cd7f..da22570 100644 --- a/odb/options.cxx +++ b/odb/odb/pregenerated/odb/options.cxx @@ -16,6 +16,7 @@ #include <set> #include <string> #include <vector> +#include <utility> #include <ostream> #include <sstream> #include <cstring> @@ -197,6 +198,7 @@ namespace cli else ++i_; + ++start_position_; return r; } else @@ -207,11 +209,20 @@ namespace cli 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; @@ -322,6 +333,7 @@ namespace cli { hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value); args_.pop_front (); + ++start_position_; return hold_[i_].c_str (); } } @@ -335,7 +347,10 @@ namespace cli if (args_.empty ()) return base::skip (); else + { args_.pop_front (); + ++start_position_; + } } const argv_file_scanner::option_info* argv_file_scanner:: @@ -348,6 +363,12 @@ namespace cli return 0; } + std::size_t argv_file_scanner:: + position () + { + return start_position_; + } + void argv_file_scanner:: load (const std::string& file) { @@ -460,12 +481,28 @@ namespace cli if (oi->search_func != 0) { - std::string f (oi->search_func (s2.c_str (), oi->arg)); + 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; } @@ -518,10 +555,31 @@ namespace cli struct parser<bool> { static void - parse (bool& x, scanner& s) + parse (bool& x, bool& xs, scanner& s) { - s.next (); - x = true; + 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; } }; @@ -543,6 +601,17 @@ namespace cli }; 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 @@ -556,11 +625,11 @@ namespace cli } }; - template <typename X> - struct parser<std::set<X> > + template <typename X, typename C> + struct parser<std::set<X, C> > { static void - parse (std::set<X>& c, bool& xs, scanner& s) + parse (std::set<X, C>& c, bool& xs, scanner& s) { X x; bool dummy; @@ -570,16 +639,17 @@ namespace cli } }; - template <typename K, typename V> - struct parser<std::map<K, V> > + template <typename K, typename V, typename C> + struct parser<std::map<K, V, C> > { static void - parse (std::map<K, V>& m, bool& xs, scanner& s) + 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 ('='); @@ -599,14 +669,14 @@ namespace cli if (!kstr.empty ()) { av[1] = const_cast<char*> (kstr.c_str ()); - argv_scanner s (0, ac, av); + 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); + argv_scanner s (0, ac, av, false, pos); parser<V>::parse (v, dummy, s); } @@ -619,6 +689,56 @@ namespace cli } }; + 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) @@ -626,6 +746,14 @@ namespace cli 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) @@ -635,7 +763,6 @@ namespace cli } #include <map> -#include <cstring> // options // @@ -3381,9 +3508,9 @@ struct _cli_options_map_init &::cli::thunk< options, std::uint64_t, &options::build2_metadata_, &options::build2_metadata_specified_ >; _cli_options_map_["--help"] = - &::cli::thunk< options, bool, &options::help_ >; + &::cli::thunk< options, &options::help_ >; _cli_options_map_["--version"] = - &::cli::thunk< options, bool, &options::version_ >; + &::cli::thunk< options, &options::version_ >; _cli_options_map_["-I"] = &::cli::thunk< options, std::vector<std::string>, &options::I_, &options::I_specified_ >; @@ -3409,27 +3536,27 @@ struct _cli_options_map_init &::cli::thunk< options, ::database, &options::default_database_, &options::default_database_specified_ >; _cli_options_map_["--generate-query"] = - &::cli::thunk< options, bool, &options::generate_query_ >; + &::cli::thunk< options, &options::generate_query_ >; _cli_options_map_["-q"] = - &::cli::thunk< options, bool, &options::generate_query_ >; + &::cli::thunk< options, &options::generate_query_ >; _cli_options_map_["--generate-prepared"] = - &::cli::thunk< options, bool, &options::generate_prepared_ >; + &::cli::thunk< options, &options::generate_prepared_ >; _cli_options_map_["--omit-unprepared"] = - &::cli::thunk< options, bool, &options::omit_unprepared_ >; + &::cli::thunk< options, &options::omit_unprepared_ >; _cli_options_map_["--generate-session"] = - &::cli::thunk< options, bool, &options::generate_session_ >; + &::cli::thunk< options, &options::generate_session_ >; _cli_options_map_["-e"] = - &::cli::thunk< options, bool, &options::generate_session_ >; + &::cli::thunk< options, &options::generate_session_ >; _cli_options_map_["--generate-schema"] = - &::cli::thunk< options, bool, &options::generate_schema_ >; + &::cli::thunk< options, &options::generate_schema_ >; _cli_options_map_["-s"] = - &::cli::thunk< options, bool, &options::generate_schema_ >; + &::cli::thunk< options, &options::generate_schema_ >; _cli_options_map_["--generate-schema-only"] = - &::cli::thunk< options, bool, &options::generate_schema_only_ >; + &::cli::thunk< options, &options::generate_schema_only_ >; _cli_options_map_["--suppress-migration"] = - &::cli::thunk< options, bool, &options::suppress_migration_ >; + &::cli::thunk< options, &options::suppress_migration_ >; _cli_options_map_["--suppress-schema-version"] = - &::cli::thunk< options, bool, &options::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_ >; @@ -3437,9 +3564,9 @@ struct _cli_options_map_init &::cli::thunk< options, database_map<std::set< ::schema_format> >, &options::schema_format_, &options::schema_format_specified_ >; _cli_options_map_["--omit-drop"] = - &::cli::thunk< options, bool, &options::omit_drop_ >; + &::cli::thunk< options, &options::omit_drop_ >; _cli_options_map_["--omit-create"] = - &::cli::thunk< options, bool, &options::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_ >; @@ -3459,7 +3586,7 @@ struct _cli_options_map_init &::cli::thunk< options, std::string, &options::profile_, &options::profile_specified_ >; _cli_options_map_["--at-once"] = - &::cli::thunk< options, bool, &options::at_once_ >; + &::cli::thunk< options, &options::at_once_ >; _cli_options_map_["--schema"] = &::cli::thunk< options, database_map<qname>, &options::schema_, &options::schema_specified_ >; @@ -3473,11 +3600,11 @@ struct _cli_options_map_init &::cli::thunk< options, cxx_version, &options::std_, &options::std_specified_ >; _cli_options_map_["--warn-hard-add"] = - &::cli::thunk< options, bool, &options::warn_hard_add_ >; + &::cli::thunk< options, &options::warn_hard_add_ >; _cli_options_map_["--warn-hard-delete"] = - &::cli::thunk< options, bool, &options::warn_hard_delete_ >; + &::cli::thunk< options, &options::warn_hard_delete_ >; _cli_options_map_["--warn-hard"] = - &::cli::thunk< options, bool, &options::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_ >; @@ -3500,7 +3627,7 @@ struct _cli_options_map_init &::cli::thunk< options, database_map<std::string>, &options::changelog_dir_, &options::changelog_dir_specified_ >; _cli_options_map_["--init-changelog"] = - &::cli::thunk< options, bool, &options::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_ >; @@ -3655,19 +3782,19 @@ struct _cli_options_map_init &::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, bool, &options::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, bool, &options::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, bool, &options::modifier_regex_trace_ >; + &::cli::thunk< options, &options::modifier_regex_trace_ >; _cli_options_map_["--include-with-brackets"] = - &::cli::thunk< options, bool, &options::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_ >; @@ -3675,12 +3802,12 @@ struct _cli_options_map_init &::cli::thunk< options, std::vector<std::string>, &options::include_regex_, &options::include_regex_specified_ >; _cli_options_map_["--include-regex-trace"] = - &::cli::thunk< options, bool, &options::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, bool, &options::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_ >; @@ -3691,16 +3818,16 @@ struct _cli_options_map_init &::cli::thunk< options, std::vector<std::string>, &options::x_, &options::x_specified_ >; _cli_options_map_["-v"] = - &::cli::thunk< options, bool, &options::v_ >; + &::cli::thunk< options, &options::v_ >; _cli_options_map_["--trace"] = - &::cli::thunk< options, bool, &options::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, bool, &options::sqlite_override_null_ >; + &::cli::thunk< options, &options::sqlite_override_null_ >; _cli_options_map_["--sqlite-lax-auto-id"] = - &::cli::thunk< options, bool, &options::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_ >; @@ -3708,7 +3835,7 @@ struct _cli_options_map_init &::cli::thunk< options, ::oracle_version, &options::oracle_client_version_, &options::oracle_client_version_specified_ >; _cli_options_map_["--oracle-warn-truncation"] = - &::cli::thunk< options, bool, &options::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_ >; diff --git a/odb/options.hxx b/odb/odb/pregenerated/odb/options.hxx index dce0fe0..74406a0 100644 --- a/odb/options.hxx +++ b/odb/odb/pregenerated/odb/options.hxx @@ -238,6 +238,14 @@ namespace cli // 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: @@ -255,13 +263,24 @@ namespace cli 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); - argv_scanner (int start, int& argc, char** argv, bool erase = false); + 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; @@ -278,7 +297,11 @@ namespace cli virtual void skip (); - private: + virtual std::size_t + position (); + + protected: + std::size_t start_position_; int i_; int& argc_; char** argv_; @@ -291,16 +314,19 @@ namespace cli argv_file_scanner (int& argc, char** argv, const std::string& option, - bool erase = false); + 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); + bool erase = false, + std::size_t start_position = 0); argv_file_scanner (const std::string& file, - const std::string& option); + const std::string& option, + std::size_t start_position = 0); struct option_info { @@ -317,18 +343,21 @@ namespace cli char** argv, const option_info* options, std::size_t options_count, - bool erase = false); + 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); + 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 options_count = 0, + std::size_t start_position = 0); virtual bool more (); @@ -342,6 +371,9 @@ namespace cli 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. diff --git a/odb/options.ixx b/odb/odb/pregenerated/odb/options.ixx index 87e81bc..9a78a2e 100644 --- a/odb/options.ixx +++ b/odb/odb/pregenerated/odb/options.ixx @@ -141,14 +141,29 @@ namespace cli // argv_scanner // inline argv_scanner:: - argv_scanner (int& argc, char** argv, bool erase) - : i_ (1), argc_ (argc), argv_ (argv), erase_ (erase) + 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) - : i_ (start), argc_ (argc), argv_ (argv), erase_ (erase) + 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) { } @@ -164,8 +179,9 @@ namespace cli argv_file_scanner (int& argc, char** argv, const std::string& option, - bool erase) - : argv_scanner (argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (argc, argv, erase, sp), option_ (option), options_ (&option_info_), options_count_ (1), @@ -181,8 +197,9 @@ namespace cli int& argc, char** argv, const std::string& option, - bool erase) - : argv_scanner (start, argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (start, argc, argv, erase, sp), option_ (option), options_ (&option_info_), options_count_ (1), @@ -195,8 +212,9 @@ namespace cli inline argv_file_scanner:: argv_file_scanner (const std::string& file, - const std::string& option) - : argv_scanner (0, zero_argc_, 0), + const std::string& option, + std::size_t sp) + : argv_scanner (0, zero_argc_, 0, sp), option_ (option), options_ (&option_info_), options_count_ (1), @@ -214,8 +232,9 @@ namespace cli char** argv, const option_info* options, std::size_t options_count, - bool erase) - : argv_scanner (argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (argc, argv, erase, sp), options_ (options), options_count_ (options_count), i_ (1), @@ -229,8 +248,9 @@ namespace cli char** argv, const option_info* options, std::size_t options_count, - bool erase) - : argv_scanner (start, argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (start, argc, argv, erase, sp), options_ (options), options_count_ (options_count), i_ (1), @@ -241,8 +261,9 @@ namespace cli inline argv_file_scanner:: argv_file_scanner (const std::string& file, const option_info* options, - std::size_t options_count) - : argv_scanner (0, zero_argc_, 0), + std::size_t options_count, + std::size_t sp) + : argv_scanner (0, zero_argc_, 0, sp), options_ (options), options_count_ (options_count), i_ (1), @@ -313,7 +334,7 @@ build2_metadata () } inline void options:: -build2_metadata(const std::uint64_t& x) +build2_metadata (const std::uint64_t& x) { this->build2_metadata_ = x; } @@ -325,7 +346,7 @@ build2_metadata_specified () const } inline void options:: -build2_metadata_specified(bool x) +build2_metadata_specified (bool x) { this->build2_metadata_specified_ = x; } @@ -343,7 +364,7 @@ help () } inline void options:: -help(const bool& x) +help (const bool& x) { this->help_ = x; } @@ -361,7 +382,7 @@ version () } inline void options:: -version(const bool& x) +version (const bool& x) { this->version_ = x; } @@ -379,7 +400,7 @@ I () } inline void options:: -I(const std::vector<std::string>& x) +I (const std::vector<std::string>& x) { this->I_ = x; } @@ -391,7 +412,7 @@ I_specified () const } inline void options:: -I_specified(bool x) +I_specified (bool x) { this->I_specified_ = x; } @@ -409,7 +430,7 @@ D () } inline void options:: -D(const std::vector<std::string>& x) +D (const std::vector<std::string>& x) { this->D_ = x; } @@ -421,7 +442,7 @@ D_specified () const } inline void options:: -D_specified(bool x) +D_specified (bool x) { this->D_specified_ = x; } @@ -439,7 +460,7 @@ U () } inline void options:: -U(const std::vector<std::string>& x) +U (const std::vector<std::string>& x) { this->U_ = x; } @@ -451,7 +472,7 @@ U_specified () const } inline void options:: -U_specified(bool x) +U_specified (bool x) { this->U_specified_ = x; } @@ -469,7 +490,7 @@ database () } inline void options:: -database(const std::vector< ::database>& x) +database (const std::vector< ::database>& x) { this->database_ = x; } @@ -481,7 +502,7 @@ database_specified () const } inline void options:: -database_specified(bool x) +database_specified (bool x) { this->database_specified_ = x; } @@ -499,7 +520,7 @@ multi_database () } inline void options:: -multi_database(const ::multi_database& x) +multi_database (const ::multi_database& x) { this->multi_database_ = x; } @@ -511,7 +532,7 @@ multi_database_specified () const } inline void options:: -multi_database_specified(bool x) +multi_database_specified (bool x) { this->multi_database_specified_ = x; } @@ -529,7 +550,7 @@ default_database () } inline void options:: -default_database(const ::database& x) +default_database (const ::database& x) { this->default_database_ = x; } @@ -541,7 +562,7 @@ default_database_specified () const } inline void options:: -default_database_specified(bool x) +default_database_specified (bool x) { this->default_database_specified_ = x; } @@ -559,7 +580,7 @@ generate_query () } inline void options:: -generate_query(const bool& x) +generate_query (const bool& x) { this->generate_query_ = x; } @@ -577,7 +598,7 @@ generate_prepared () } inline void options:: -generate_prepared(const bool& x) +generate_prepared (const bool& x) { this->generate_prepared_ = x; } @@ -595,7 +616,7 @@ omit_unprepared () } inline void options:: -omit_unprepared(const bool& x) +omit_unprepared (const bool& x) { this->omit_unprepared_ = x; } @@ -613,7 +634,7 @@ generate_session () } inline void options:: -generate_session(const bool& x) +generate_session (const bool& x) { this->generate_session_ = x; } @@ -631,7 +652,7 @@ generate_schema () } inline void options:: -generate_schema(const bool& x) +generate_schema (const bool& x) { this->generate_schema_ = x; } @@ -649,7 +670,7 @@ generate_schema_only () } inline void options:: -generate_schema_only(const bool& x) +generate_schema_only (const bool& x) { this->generate_schema_only_ = x; } @@ -667,7 +688,7 @@ suppress_migration () } inline void options:: -suppress_migration(const bool& x) +suppress_migration (const bool& x) { this->suppress_migration_ = x; } @@ -685,7 +706,7 @@ suppress_schema_version () } inline void options:: -suppress_schema_version(const bool& x) +suppress_schema_version (const bool& x) { this->suppress_schema_version_ = x; } @@ -703,7 +724,7 @@ schema_version_table () } inline void options:: -schema_version_table(const database_map<qname>& x) +schema_version_table (const database_map<qname>& x) { this->schema_version_table_ = x; } @@ -715,7 +736,7 @@ schema_version_table_specified () const } inline void options:: -schema_version_table_specified(bool x) +schema_version_table_specified (bool x) { this->schema_version_table_specified_ = x; } @@ -733,7 +754,7 @@ schema_format () } inline void options:: -schema_format(const database_map<std::set< ::schema_format> >& x) +schema_format (const database_map<std::set< ::schema_format> >& x) { this->schema_format_ = x; } @@ -745,7 +766,7 @@ schema_format_specified () const } inline void options:: -schema_format_specified(bool x) +schema_format_specified (bool x) { this->schema_format_specified_ = x; } @@ -763,7 +784,7 @@ omit_drop () } inline void options:: -omit_drop(const bool& x) +omit_drop (const bool& x) { this->omit_drop_ = x; } @@ -781,7 +802,7 @@ omit_create () } inline void options:: -omit_create(const bool& x) +omit_create (const bool& x) { this->omit_create_ = x; } @@ -799,7 +820,7 @@ schema_name () } inline void options:: -schema_name(const database_map<std::string>& x) +schema_name (const database_map<std::string>& x) { this->schema_name_ = x; } @@ -811,7 +832,7 @@ schema_name_specified () const } inline void options:: -schema_name_specified(bool x) +schema_name_specified (bool x) { this->schema_name_specified_ = x; } @@ -829,7 +850,7 @@ fkeys_deferrable_mode () } inline void options:: -fkeys_deferrable_mode(const database_map<deferrable>& x) +fkeys_deferrable_mode (const database_map<deferrable>& x) { this->fkeys_deferrable_mode_ = x; } @@ -841,7 +862,7 @@ fkeys_deferrable_mode_specified () const } inline void options:: -fkeys_deferrable_mode_specified(bool x) +fkeys_deferrable_mode_specified (bool x) { this->fkeys_deferrable_mode_specified_ = x; } @@ -859,7 +880,7 @@ default_pointer () } inline void options:: -default_pointer(const std::string& x) +default_pointer (const std::string& x) { this->default_pointer_ = x; } @@ -871,7 +892,7 @@ default_pointer_specified () const } inline void options:: -default_pointer_specified(bool x) +default_pointer_specified (bool x) { this->default_pointer_specified_ = x; } @@ -889,7 +910,7 @@ session_type () } inline void options:: -session_type(const std::string& x) +session_type (const std::string& x) { this->session_type_ = x; } @@ -901,7 +922,7 @@ session_type_specified () const } inline void options:: -session_type_specified(bool x) +session_type_specified (bool x) { this->session_type_specified_ = x; } @@ -919,7 +940,7 @@ profile () } inline void options:: -profile(const std::string& x) +profile (const std::string& x) { this->profile_ = x; } @@ -931,7 +952,7 @@ profile_specified () const } inline void options:: -profile_specified(bool x) +profile_specified (bool x) { this->profile_specified_ = x; } @@ -949,7 +970,7 @@ at_once () } inline void options:: -at_once(const bool& x) +at_once (const bool& x) { this->at_once_ = x; } @@ -967,7 +988,7 @@ schema () } inline void options:: -schema(const database_map<qname>& x) +schema (const database_map<qname>& x) { this->schema_ = x; } @@ -979,7 +1000,7 @@ schema_specified () const } inline void options:: -schema_specified(bool x) +schema_specified (bool x) { this->schema_specified_ = x; } @@ -997,7 +1018,7 @@ export_symbol () } inline void options:: -export_symbol(const database_map<std::string>& x) +export_symbol (const database_map<std::string>& x) { this->export_symbol_ = x; } @@ -1009,7 +1030,7 @@ export_symbol_specified () const } inline void options:: -export_symbol_specified(bool x) +export_symbol_specified (bool x) { this->export_symbol_specified_ = x; } @@ -1027,7 +1048,7 @@ extern_symbol () } inline void options:: -extern_symbol(const database_map<std::string>& x) +extern_symbol (const database_map<std::string>& x) { this->extern_symbol_ = x; } @@ -1039,7 +1060,7 @@ extern_symbol_specified () const } inline void options:: -extern_symbol_specified(bool x) +extern_symbol_specified (bool x) { this->extern_symbol_specified_ = x; } @@ -1057,7 +1078,7 @@ std () } inline void options:: -std(const cxx_version& x) +std (const cxx_version& x) { this->std_ = x; } @@ -1069,7 +1090,7 @@ std_specified () const } inline void options:: -std_specified(bool x) +std_specified (bool x) { this->std_specified_ = x; } @@ -1087,7 +1108,7 @@ warn_hard_add () } inline void options:: -warn_hard_add(const bool& x) +warn_hard_add (const bool& x) { this->warn_hard_add_ = x; } @@ -1105,7 +1126,7 @@ warn_hard_delete () } inline void options:: -warn_hard_delete(const bool& x) +warn_hard_delete (const bool& x) { this->warn_hard_delete_ = x; } @@ -1123,7 +1144,7 @@ warn_hard () } inline void options:: -warn_hard(const bool& x) +warn_hard (const bool& x) { this->warn_hard_ = x; } @@ -1141,7 +1162,7 @@ output_dir () } inline void options:: -output_dir(const std::string& x) +output_dir (const std::string& x) { this->output_dir_ = x; } @@ -1153,7 +1174,7 @@ output_dir_specified () const } inline void options:: -output_dir_specified(bool x) +output_dir_specified (bool x) { this->output_dir_specified_ = x; } @@ -1171,7 +1192,7 @@ input_name () } inline void options:: -input_name(const std::string& x) +input_name (const std::string& x) { this->input_name_ = x; } @@ -1183,7 +1204,7 @@ input_name_specified () const } inline void options:: -input_name_specified(bool x) +input_name_specified (bool x) { this->input_name_specified_ = x; } @@ -1201,7 +1222,7 @@ changelog () } inline void options:: -changelog(const database_map<std::string>& x) +changelog (const database_map<std::string>& x) { this->changelog_ = x; } @@ -1213,7 +1234,7 @@ changelog_specified () const } inline void options:: -changelog_specified(bool x) +changelog_specified (bool x) { this->changelog_specified_ = x; } @@ -1231,7 +1252,7 @@ changelog_in () } inline void options:: -changelog_in(const database_map<std::string>& x) +changelog_in (const database_map<std::string>& x) { this->changelog_in_ = x; } @@ -1243,7 +1264,7 @@ changelog_in_specified () const } inline void options:: -changelog_in_specified(bool x) +changelog_in_specified (bool x) { this->changelog_in_specified_ = x; } @@ -1261,7 +1282,7 @@ changelog_out () } inline void options:: -changelog_out(const database_map<std::string>& x) +changelog_out (const database_map<std::string>& x) { this->changelog_out_ = x; } @@ -1273,7 +1294,7 @@ changelog_out_specified () const } inline void options:: -changelog_out_specified(bool x) +changelog_out_specified (bool x) { this->changelog_out_specified_ = x; } @@ -1291,7 +1312,7 @@ changelog_dir () } inline void options:: -changelog_dir(const database_map<std::string>& x) +changelog_dir (const database_map<std::string>& x) { this->changelog_dir_ = x; } @@ -1303,7 +1324,7 @@ changelog_dir_specified () const } inline void options:: -changelog_dir_specified(bool x) +changelog_dir_specified (bool x) { this->changelog_dir_specified_ = x; } @@ -1321,7 +1342,7 @@ init_changelog () } inline void options:: -init_changelog(const bool& x) +init_changelog (const bool& x) { this->init_changelog_ = x; } @@ -1339,7 +1360,7 @@ odb_file_suffix () } inline void options:: -odb_file_suffix(const database_map<std::string>& x) +odb_file_suffix (const database_map<std::string>& x) { this->odb_file_suffix_ = x; } @@ -1351,7 +1372,7 @@ odb_file_suffix_specified () const } inline void options:: -odb_file_suffix_specified(bool x) +odb_file_suffix_specified (bool x) { this->odb_file_suffix_specified_ = x; } @@ -1369,7 +1390,7 @@ sql_file_suffix () } inline void options:: -sql_file_suffix(const database_map<std::string>& x) +sql_file_suffix (const database_map<std::string>& x) { this->sql_file_suffix_ = x; } @@ -1381,7 +1402,7 @@ sql_file_suffix_specified () const } inline void options:: -sql_file_suffix_specified(bool x) +sql_file_suffix_specified (bool x) { this->sql_file_suffix_specified_ = x; } @@ -1399,7 +1420,7 @@ schema_file_suffix () } inline void options:: -schema_file_suffix(const database_map<std::string>& x) +schema_file_suffix (const database_map<std::string>& x) { this->schema_file_suffix_ = x; } @@ -1411,7 +1432,7 @@ schema_file_suffix_specified () const } inline void options:: -schema_file_suffix_specified(bool x) +schema_file_suffix_specified (bool x) { this->schema_file_suffix_specified_ = x; } @@ -1429,7 +1450,7 @@ changelog_file_suffix () } inline void options:: -changelog_file_suffix(const database_map<std::string>& x) +changelog_file_suffix (const database_map<std::string>& x) { this->changelog_file_suffix_ = x; } @@ -1441,7 +1462,7 @@ changelog_file_suffix_specified () const } inline void options:: -changelog_file_suffix_specified(bool x) +changelog_file_suffix_specified (bool x) { this->changelog_file_suffix_specified_ = x; } @@ -1459,7 +1480,7 @@ hxx_suffix () } inline void options:: -hxx_suffix(const std::string& x) +hxx_suffix (const std::string& x) { this->hxx_suffix_ = x; } @@ -1471,7 +1492,7 @@ hxx_suffix_specified () const } inline void options:: -hxx_suffix_specified(bool x) +hxx_suffix_specified (bool x) { this->hxx_suffix_specified_ = x; } @@ -1489,7 +1510,7 @@ ixx_suffix () } inline void options:: -ixx_suffix(const std::string& x) +ixx_suffix (const std::string& x) { this->ixx_suffix_ = x; } @@ -1501,7 +1522,7 @@ ixx_suffix_specified () const } inline void options:: -ixx_suffix_specified(bool x) +ixx_suffix_specified (bool x) { this->ixx_suffix_specified_ = x; } @@ -1519,7 +1540,7 @@ cxx_suffix () } inline void options:: -cxx_suffix(const std::string& x) +cxx_suffix (const std::string& x) { this->cxx_suffix_ = x; } @@ -1531,7 +1552,7 @@ cxx_suffix_specified () const } inline void options:: -cxx_suffix_specified(bool x) +cxx_suffix_specified (bool x) { this->cxx_suffix_specified_ = x; } @@ -1549,7 +1570,7 @@ sql_suffix () } inline void options:: -sql_suffix(const std::string& x) +sql_suffix (const std::string& x) { this->sql_suffix_ = x; } @@ -1561,7 +1582,7 @@ sql_suffix_specified () const } inline void options:: -sql_suffix_specified(bool x) +sql_suffix_specified (bool x) { this->sql_suffix_specified_ = x; } @@ -1579,7 +1600,7 @@ changelog_suffix () } inline void options:: -changelog_suffix(const std::string& x) +changelog_suffix (const std::string& x) { this->changelog_suffix_ = x; } @@ -1591,7 +1612,7 @@ changelog_suffix_specified () const } inline void options:: -changelog_suffix_specified(bool x) +changelog_suffix_specified (bool x) { this->changelog_suffix_specified_ = x; } @@ -1609,7 +1630,7 @@ hxx_prologue () } inline void options:: -hxx_prologue(const database_map<std::vector<std::string> >& x) +hxx_prologue (const database_map<std::vector<std::string> >& x) { this->hxx_prologue_ = x; } @@ -1621,7 +1642,7 @@ hxx_prologue_specified () const } inline void options:: -hxx_prologue_specified(bool x) +hxx_prologue_specified (bool x) { this->hxx_prologue_specified_ = x; } @@ -1639,7 +1660,7 @@ ixx_prologue () } inline void options:: -ixx_prologue(const database_map<std::vector<std::string> >& x) +ixx_prologue (const database_map<std::vector<std::string> >& x) { this->ixx_prologue_ = x; } @@ -1651,7 +1672,7 @@ ixx_prologue_specified () const } inline void options:: -ixx_prologue_specified(bool x) +ixx_prologue_specified (bool x) { this->ixx_prologue_specified_ = x; } @@ -1669,7 +1690,7 @@ cxx_prologue () } inline void options:: -cxx_prologue(const database_map<std::vector<std::string> >& x) +cxx_prologue (const database_map<std::vector<std::string> >& x) { this->cxx_prologue_ = x; } @@ -1681,7 +1702,7 @@ cxx_prologue_specified () const } inline void options:: -cxx_prologue_specified(bool x) +cxx_prologue_specified (bool x) { this->cxx_prologue_specified_ = x; } @@ -1699,7 +1720,7 @@ schema_prologue () } inline void options:: -schema_prologue(const database_map<std::vector<std::string> >& x) +schema_prologue (const database_map<std::vector<std::string> >& x) { this->schema_prologue_ = x; } @@ -1711,7 +1732,7 @@ schema_prologue_specified () const } inline void options:: -schema_prologue_specified(bool x) +schema_prologue_specified (bool x) { this->schema_prologue_specified_ = x; } @@ -1729,7 +1750,7 @@ sql_prologue () } inline void options:: -sql_prologue(const database_map<std::vector<std::string> >& x) +sql_prologue (const database_map<std::vector<std::string> >& x) { this->sql_prologue_ = x; } @@ -1741,7 +1762,7 @@ sql_prologue_specified () const } inline void options:: -sql_prologue_specified(bool x) +sql_prologue_specified (bool x) { this->sql_prologue_specified_ = x; } @@ -1759,7 +1780,7 @@ migration_prologue () } inline void options:: -migration_prologue(const database_map<std::vector<std::string> >& x) +migration_prologue (const database_map<std::vector<std::string> >& x) { this->migration_prologue_ = x; } @@ -1771,7 +1792,7 @@ migration_prologue_specified () const } inline void options:: -migration_prologue_specified(bool x) +migration_prologue_specified (bool x) { this->migration_prologue_specified_ = x; } @@ -1789,7 +1810,7 @@ sql_interlude () } inline void options:: -sql_interlude(const database_map<std::vector<std::string> >& x) +sql_interlude (const database_map<std::vector<std::string> >& x) { this->sql_interlude_ = x; } @@ -1801,7 +1822,7 @@ sql_interlude_specified () const } inline void options:: -sql_interlude_specified(bool x) +sql_interlude_specified (bool x) { this->sql_interlude_specified_ = x; } @@ -1819,7 +1840,7 @@ hxx_epilogue () } inline void options:: -hxx_epilogue(const database_map<std::vector<std::string> >& x) +hxx_epilogue (const database_map<std::vector<std::string> >& x) { this->hxx_epilogue_ = x; } @@ -1831,7 +1852,7 @@ hxx_epilogue_specified () const } inline void options:: -hxx_epilogue_specified(bool x) +hxx_epilogue_specified (bool x) { this->hxx_epilogue_specified_ = x; } @@ -1849,7 +1870,7 @@ ixx_epilogue () } inline void options:: -ixx_epilogue(const database_map<std::vector<std::string> >& x) +ixx_epilogue (const database_map<std::vector<std::string> >& x) { this->ixx_epilogue_ = x; } @@ -1861,7 +1882,7 @@ ixx_epilogue_specified () const } inline void options:: -ixx_epilogue_specified(bool x) +ixx_epilogue_specified (bool x) { this->ixx_epilogue_specified_ = x; } @@ -1879,7 +1900,7 @@ cxx_epilogue () } inline void options:: -cxx_epilogue(const database_map<std::vector<std::string> >& x) +cxx_epilogue (const database_map<std::vector<std::string> >& x) { this->cxx_epilogue_ = x; } @@ -1891,7 +1912,7 @@ cxx_epilogue_specified () const } inline void options:: -cxx_epilogue_specified(bool x) +cxx_epilogue_specified (bool x) { this->cxx_epilogue_specified_ = x; } @@ -1909,7 +1930,7 @@ schema_epilogue () } inline void options:: -schema_epilogue(const database_map<std::vector<std::string> >& x) +schema_epilogue (const database_map<std::vector<std::string> >& x) { this->schema_epilogue_ = x; } @@ -1921,7 +1942,7 @@ schema_epilogue_specified () const } inline void options:: -schema_epilogue_specified(bool x) +schema_epilogue_specified (bool x) { this->schema_epilogue_specified_ = x; } @@ -1939,7 +1960,7 @@ sql_epilogue () } inline void options:: -sql_epilogue(const database_map<std::vector<std::string> >& x) +sql_epilogue (const database_map<std::vector<std::string> >& x) { this->sql_epilogue_ = x; } @@ -1951,7 +1972,7 @@ sql_epilogue_specified () const } inline void options:: -sql_epilogue_specified(bool x) +sql_epilogue_specified (bool x) { this->sql_epilogue_specified_ = x; } @@ -1969,7 +1990,7 @@ migration_epilogue () } inline void options:: -migration_epilogue(const database_map<std::vector<std::string> >& x) +migration_epilogue (const database_map<std::vector<std::string> >& x) { this->migration_epilogue_ = x; } @@ -1981,7 +2002,7 @@ migration_epilogue_specified () const } inline void options:: -migration_epilogue_specified(bool x) +migration_epilogue_specified (bool x) { this->migration_epilogue_specified_ = x; } @@ -1999,7 +2020,7 @@ hxx_prologue_file () } inline void options:: -hxx_prologue_file(const database_map<std::vector<std::string> >& x) +hxx_prologue_file (const database_map<std::vector<std::string> >& x) { this->hxx_prologue_file_ = x; } @@ -2011,7 +2032,7 @@ hxx_prologue_file_specified () const } inline void options:: -hxx_prologue_file_specified(bool x) +hxx_prologue_file_specified (bool x) { this->hxx_prologue_file_specified_ = x; } @@ -2029,7 +2050,7 @@ ixx_prologue_file () } inline void options:: -ixx_prologue_file(const database_map<std::vector<std::string> >& x) +ixx_prologue_file (const database_map<std::vector<std::string> >& x) { this->ixx_prologue_file_ = x; } @@ -2041,7 +2062,7 @@ ixx_prologue_file_specified () const } inline void options:: -ixx_prologue_file_specified(bool x) +ixx_prologue_file_specified (bool x) { this->ixx_prologue_file_specified_ = x; } @@ -2059,7 +2080,7 @@ cxx_prologue_file () } inline void options:: -cxx_prologue_file(const database_map<std::vector<std::string> >& x) +cxx_prologue_file (const database_map<std::vector<std::string> >& x) { this->cxx_prologue_file_ = x; } @@ -2071,7 +2092,7 @@ cxx_prologue_file_specified () const } inline void options:: -cxx_prologue_file_specified(bool x) +cxx_prologue_file_specified (bool x) { this->cxx_prologue_file_specified_ = x; } @@ -2089,7 +2110,7 @@ schema_prologue_file () } inline void options:: -schema_prologue_file(const database_map<std::vector<std::string> >& x) +schema_prologue_file (const database_map<std::vector<std::string> >& x) { this->schema_prologue_file_ = x; } @@ -2101,7 +2122,7 @@ schema_prologue_file_specified () const } inline void options:: -schema_prologue_file_specified(bool x) +schema_prologue_file_specified (bool x) { this->schema_prologue_file_specified_ = x; } @@ -2119,7 +2140,7 @@ sql_prologue_file () } inline void options:: -sql_prologue_file(const database_map<std::vector<std::string> >& x) +sql_prologue_file (const database_map<std::vector<std::string> >& x) { this->sql_prologue_file_ = x; } @@ -2131,7 +2152,7 @@ sql_prologue_file_specified () const } inline void options:: -sql_prologue_file_specified(bool x) +sql_prologue_file_specified (bool x) { this->sql_prologue_file_specified_ = x; } @@ -2149,7 +2170,7 @@ migration_prologue_file () } inline void options:: -migration_prologue_file(const database_map<std::vector<std::string> >& x) +migration_prologue_file (const database_map<std::vector<std::string> >& x) { this->migration_prologue_file_ = x; } @@ -2161,7 +2182,7 @@ migration_prologue_file_specified () const } inline void options:: -migration_prologue_file_specified(bool x) +migration_prologue_file_specified (bool x) { this->migration_prologue_file_specified_ = x; } @@ -2179,7 +2200,7 @@ sql_interlude_file () } inline void options:: -sql_interlude_file(const database_map<std::vector<std::string> >& x) +sql_interlude_file (const database_map<std::vector<std::string> >& x) { this->sql_interlude_file_ = x; } @@ -2191,7 +2212,7 @@ sql_interlude_file_specified () const } inline void options:: -sql_interlude_file_specified(bool x) +sql_interlude_file_specified (bool x) { this->sql_interlude_file_specified_ = x; } @@ -2209,7 +2230,7 @@ hxx_epilogue_file () } inline void options:: -hxx_epilogue_file(const database_map<std::vector<std::string> >& x) +hxx_epilogue_file (const database_map<std::vector<std::string> >& x) { this->hxx_epilogue_file_ = x; } @@ -2221,7 +2242,7 @@ hxx_epilogue_file_specified () const } inline void options:: -hxx_epilogue_file_specified(bool x) +hxx_epilogue_file_specified (bool x) { this->hxx_epilogue_file_specified_ = x; } @@ -2239,7 +2260,7 @@ ixx_epilogue_file () } inline void options:: -ixx_epilogue_file(const database_map<std::vector<std::string> >& x) +ixx_epilogue_file (const database_map<std::vector<std::string> >& x) { this->ixx_epilogue_file_ = x; } @@ -2251,7 +2272,7 @@ ixx_epilogue_file_specified () const } inline void options:: -ixx_epilogue_file_specified(bool x) +ixx_epilogue_file_specified (bool x) { this->ixx_epilogue_file_specified_ = x; } @@ -2269,7 +2290,7 @@ cxx_epilogue_file () } inline void options:: -cxx_epilogue_file(const database_map<std::vector<std::string> >& x) +cxx_epilogue_file (const database_map<std::vector<std::string> >& x) { this->cxx_epilogue_file_ = x; } @@ -2281,7 +2302,7 @@ cxx_epilogue_file_specified () const } inline void options:: -cxx_epilogue_file_specified(bool x) +cxx_epilogue_file_specified (bool x) { this->cxx_epilogue_file_specified_ = x; } @@ -2299,7 +2320,7 @@ schema_epilogue_file () } inline void options:: -schema_epilogue_file(const database_map<std::vector<std::string> >& x) +schema_epilogue_file (const database_map<std::vector<std::string> >& x) { this->schema_epilogue_file_ = x; } @@ -2311,7 +2332,7 @@ schema_epilogue_file_specified () const } inline void options:: -schema_epilogue_file_specified(bool x) +schema_epilogue_file_specified (bool x) { this->schema_epilogue_file_specified_ = x; } @@ -2329,7 +2350,7 @@ sql_epilogue_file () } inline void options:: -sql_epilogue_file(const database_map<std::vector<std::string> >& x) +sql_epilogue_file (const database_map<std::vector<std::string> >& x) { this->sql_epilogue_file_ = x; } @@ -2341,7 +2362,7 @@ sql_epilogue_file_specified () const } inline void options:: -sql_epilogue_file_specified(bool x) +sql_epilogue_file_specified (bool x) { this->sql_epilogue_file_specified_ = x; } @@ -2359,7 +2380,7 @@ migration_epilogue_file () } inline void options:: -migration_epilogue_file(const database_map<std::vector<std::string> >& x) +migration_epilogue_file (const database_map<std::vector<std::string> >& x) { this->migration_epilogue_file_ = x; } @@ -2371,7 +2392,7 @@ migration_epilogue_file_specified () const } inline void options:: -migration_epilogue_file_specified(bool x) +migration_epilogue_file_specified (bool x) { this->migration_epilogue_file_specified_ = x; } @@ -2389,7 +2410,7 @@ odb_prologue () } inline void options:: -odb_prologue(const database_map<std::vector<std::string> >& x) +odb_prologue (const database_map<std::vector<std::string> >& x) { this->odb_prologue_ = x; } @@ -2401,7 +2422,7 @@ odb_prologue_specified () const } inline void options:: -odb_prologue_specified(bool x) +odb_prologue_specified (bool x) { this->odb_prologue_specified_ = x; } @@ -2419,7 +2440,7 @@ odb_prologue_file () } inline void options:: -odb_prologue_file(const database_map<std::vector<std::string> >& x) +odb_prologue_file (const database_map<std::vector<std::string> >& x) { this->odb_prologue_file_ = x; } @@ -2431,7 +2452,7 @@ odb_prologue_file_specified () const } inline void options:: -odb_prologue_file_specified(bool x) +odb_prologue_file_specified (bool x) { this->odb_prologue_file_specified_ = x; } @@ -2449,7 +2470,7 @@ odb_epilogue () } inline void options:: -odb_epilogue(const database_map<std::vector<std::string> >& x) +odb_epilogue (const database_map<std::vector<std::string> >& x) { this->odb_epilogue_ = x; } @@ -2461,7 +2482,7 @@ odb_epilogue_specified () const } inline void options:: -odb_epilogue_specified(bool x) +odb_epilogue_specified (bool x) { this->odb_epilogue_specified_ = x; } @@ -2479,7 +2500,7 @@ odb_epilogue_file () } inline void options:: -odb_epilogue_file(const database_map<std::vector<std::string> >& x) +odb_epilogue_file (const database_map<std::vector<std::string> >& x) { this->odb_epilogue_file_ = x; } @@ -2491,7 +2512,7 @@ odb_epilogue_file_specified () const } inline void options:: -odb_epilogue_file_specified(bool x) +odb_epilogue_file_specified (bool x) { this->odb_epilogue_file_specified_ = x; } @@ -2509,7 +2530,7 @@ table_prefix () } inline void options:: -table_prefix(const database_map<std::string>& x) +table_prefix (const database_map<std::string>& x) { this->table_prefix_ = x; } @@ -2521,7 +2542,7 @@ table_prefix_specified () const } inline void options:: -table_prefix_specified(bool x) +table_prefix_specified (bool x) { this->table_prefix_specified_ = x; } @@ -2539,7 +2560,7 @@ index_suffix () } inline void options:: -index_suffix(const database_map<std::string>& x) +index_suffix (const database_map<std::string>& x) { this->index_suffix_ = x; } @@ -2551,7 +2572,7 @@ index_suffix_specified () const } inline void options:: -index_suffix_specified(bool x) +index_suffix_specified (bool x) { this->index_suffix_specified_ = x; } @@ -2569,7 +2590,7 @@ fkey_suffix () } inline void options:: -fkey_suffix(const database_map<std::string>& x) +fkey_suffix (const database_map<std::string>& x) { this->fkey_suffix_ = x; } @@ -2581,7 +2602,7 @@ fkey_suffix_specified () const } inline void options:: -fkey_suffix_specified(bool x) +fkey_suffix_specified (bool x) { this->fkey_suffix_specified_ = x; } @@ -2599,7 +2620,7 @@ sequence_suffix () } inline void options:: -sequence_suffix(const database_map<std::string>& x) +sequence_suffix (const database_map<std::string>& x) { this->sequence_suffix_ = x; } @@ -2611,7 +2632,7 @@ sequence_suffix_specified () const } inline void options:: -sequence_suffix_specified(bool x) +sequence_suffix_specified (bool x) { this->sequence_suffix_specified_ = x; } @@ -2629,7 +2650,7 @@ sql_name_case () } inline void options:: -sql_name_case(const database_map<name_case>& x) +sql_name_case (const database_map<name_case>& x) { this->sql_name_case_ = x; } @@ -2641,7 +2662,7 @@ sql_name_case_specified () const } inline void options:: -sql_name_case_specified(bool x) +sql_name_case_specified (bool x) { this->sql_name_case_specified_ = x; } @@ -2659,7 +2680,7 @@ table_regex () } inline void options:: -table_regex(const database_map<std::vector<std::string> >& x) +table_regex (const database_map<std::vector<std::string> >& x) { this->table_regex_ = x; } @@ -2671,7 +2692,7 @@ table_regex_specified () const } inline void options:: -table_regex_specified(bool x) +table_regex_specified (bool x) { this->table_regex_specified_ = x; } @@ -2689,7 +2710,7 @@ column_regex () } inline void options:: -column_regex(const database_map<std::vector<std::string> >& x) +column_regex (const database_map<std::vector<std::string> >& x) { this->column_regex_ = x; } @@ -2701,7 +2722,7 @@ column_regex_specified () const } inline void options:: -column_regex_specified(bool x) +column_regex_specified (bool x) { this->column_regex_specified_ = x; } @@ -2719,7 +2740,7 @@ index_regex () } inline void options:: -index_regex(const database_map<std::vector<std::string> >& x) +index_regex (const database_map<std::vector<std::string> >& x) { this->index_regex_ = x; } @@ -2731,7 +2752,7 @@ index_regex_specified () const } inline void options:: -index_regex_specified(bool x) +index_regex_specified (bool x) { this->index_regex_specified_ = x; } @@ -2749,7 +2770,7 @@ fkey_regex () } inline void options:: -fkey_regex(const database_map<std::vector<std::string> >& x) +fkey_regex (const database_map<std::vector<std::string> >& x) { this->fkey_regex_ = x; } @@ -2761,7 +2782,7 @@ fkey_regex_specified () const } inline void options:: -fkey_regex_specified(bool x) +fkey_regex_specified (bool x) { this->fkey_regex_specified_ = x; } @@ -2779,7 +2800,7 @@ sequence_regex () } inline void options:: -sequence_regex(const database_map<std::vector<std::string> >& x) +sequence_regex (const database_map<std::vector<std::string> >& x) { this->sequence_regex_ = x; } @@ -2791,7 +2812,7 @@ sequence_regex_specified () const } inline void options:: -sequence_regex_specified(bool x) +sequence_regex_specified (bool x) { this->sequence_regex_specified_ = x; } @@ -2809,7 +2830,7 @@ statement_regex () } inline void options:: -statement_regex(const database_map<std::vector<std::string> >& x) +statement_regex (const database_map<std::vector<std::string> >& x) { this->statement_regex_ = x; } @@ -2821,7 +2842,7 @@ statement_regex_specified () const } inline void options:: -statement_regex_specified(bool x) +statement_regex_specified (bool x) { this->statement_regex_specified_ = x; } @@ -2839,7 +2860,7 @@ sql_name_regex () } inline void options:: -sql_name_regex(const database_map<std::vector<std::string> >& x) +sql_name_regex (const database_map<std::vector<std::string> >& x) { this->sql_name_regex_ = x; } @@ -2851,7 +2872,7 @@ sql_name_regex_specified () const } inline void options:: -sql_name_regex_specified(bool x) +sql_name_regex_specified (bool x) { this->sql_name_regex_specified_ = x; } @@ -2869,7 +2890,7 @@ sql_name_regex_trace () } inline void options:: -sql_name_regex_trace(const bool& x) +sql_name_regex_trace (const bool& x) { this->sql_name_regex_trace_ = x; } @@ -2887,7 +2908,7 @@ accessor_regex () } inline void options:: -accessor_regex(const std::vector<std::string>& x) +accessor_regex (const std::vector<std::string>& x) { this->accessor_regex_ = x; } @@ -2899,7 +2920,7 @@ accessor_regex_specified () const } inline void options:: -accessor_regex_specified(bool x) +accessor_regex_specified (bool x) { this->accessor_regex_specified_ = x; } @@ -2917,7 +2938,7 @@ accessor_regex_trace () } inline void options:: -accessor_regex_trace(const bool& x) +accessor_regex_trace (const bool& x) { this->accessor_regex_trace_ = x; } @@ -2935,7 +2956,7 @@ modifier_regex () } inline void options:: -modifier_regex(const std::vector<std::string>& x) +modifier_regex (const std::vector<std::string>& x) { this->modifier_regex_ = x; } @@ -2947,7 +2968,7 @@ modifier_regex_specified () const } inline void options:: -modifier_regex_specified(bool x) +modifier_regex_specified (bool x) { this->modifier_regex_specified_ = x; } @@ -2965,7 +2986,7 @@ modifier_regex_trace () } inline void options:: -modifier_regex_trace(const bool& x) +modifier_regex_trace (const bool& x) { this->modifier_regex_trace_ = x; } @@ -2983,7 +3004,7 @@ include_with_brackets () } inline void options:: -include_with_brackets(const bool& x) +include_with_brackets (const bool& x) { this->include_with_brackets_ = x; } @@ -3001,7 +3022,7 @@ include_prefix () } inline void options:: -include_prefix(const std::string& x) +include_prefix (const std::string& x) { this->include_prefix_ = x; } @@ -3013,7 +3034,7 @@ include_prefix_specified () const } inline void options:: -include_prefix_specified(bool x) +include_prefix_specified (bool x) { this->include_prefix_specified_ = x; } @@ -3031,7 +3052,7 @@ include_regex () } inline void options:: -include_regex(const std::vector<std::string>& x) +include_regex (const std::vector<std::string>& x) { this->include_regex_ = x; } @@ -3043,7 +3064,7 @@ include_regex_specified () const } inline void options:: -include_regex_specified(bool x) +include_regex_specified (bool x) { this->include_regex_specified_ = x; } @@ -3061,7 +3082,7 @@ include_regex_trace () } inline void options:: -include_regex_trace(const bool& x) +include_regex_trace (const bool& x) { this->include_regex_trace_ = x; } @@ -3079,7 +3100,7 @@ guard_prefix () } inline void options:: -guard_prefix(const std::string& x) +guard_prefix (const std::string& x) { this->guard_prefix_ = x; } @@ -3091,7 +3112,7 @@ guard_prefix_specified () const } inline void options:: -guard_prefix_specified(bool x) +guard_prefix_specified (bool x) { this->guard_prefix_specified_ = x; } @@ -3109,7 +3130,7 @@ show_sloc () } inline void options:: -show_sloc(const bool& x) +show_sloc (const bool& x) { this->show_sloc_ = x; } @@ -3127,7 +3148,7 @@ sloc_limit () } inline void options:: -sloc_limit(const std::size_t& x) +sloc_limit (const std::size_t& x) { this->sloc_limit_ = x; } @@ -3139,7 +3160,7 @@ sloc_limit_specified () const } inline void options:: -sloc_limit_specified(bool x) +sloc_limit_specified (bool x) { this->sloc_limit_specified_ = x; } @@ -3157,7 +3178,7 @@ options_file () } inline void options:: -options_file(const std::string& x) +options_file (const std::string& x) { this->options_file_ = x; } @@ -3169,7 +3190,7 @@ options_file_specified () const } inline void options:: -options_file_specified(bool x) +options_file_specified (bool x) { this->options_file_specified_ = x; } @@ -3187,7 +3208,7 @@ x () } inline void options:: -x(const std::vector<std::string>& x) +x (const std::vector<std::string>& x) { this->x_ = x; } @@ -3199,7 +3220,7 @@ x_specified () const } inline void options:: -x_specified(bool x) +x_specified (bool x) { this->x_specified_ = x; } @@ -3217,7 +3238,7 @@ v () } inline void options:: -v(const bool& x) +v (const bool& x) { this->v_ = x; } @@ -3235,7 +3256,7 @@ trace () } inline void options:: -trace(const bool& x) +trace (const bool& x) { this->trace_ = x; } @@ -3253,7 +3274,7 @@ mysql_engine () } inline void options:: -mysql_engine(const std::string& x) +mysql_engine (const std::string& x) { this->mysql_engine_ = x; } @@ -3265,7 +3286,7 @@ mysql_engine_specified () const } inline void options:: -mysql_engine_specified(bool x) +mysql_engine_specified (bool x) { this->mysql_engine_specified_ = x; } @@ -3283,7 +3304,7 @@ sqlite_override_null () } inline void options:: -sqlite_override_null(const bool& x) +sqlite_override_null (const bool& x) { this->sqlite_override_null_ = x; } @@ -3301,7 +3322,7 @@ sqlite_lax_auto_id () } inline void options:: -sqlite_lax_auto_id(const bool& x) +sqlite_lax_auto_id (const bool& x) { this->sqlite_lax_auto_id_ = x; } @@ -3319,7 +3340,7 @@ pgsql_server_version () } inline void options:: -pgsql_server_version(const ::pgsql_version& x) +pgsql_server_version (const ::pgsql_version& x) { this->pgsql_server_version_ = x; } @@ -3331,7 +3352,7 @@ pgsql_server_version_specified () const } inline void options:: -pgsql_server_version_specified(bool x) +pgsql_server_version_specified (bool x) { this->pgsql_server_version_specified_ = x; } @@ -3349,7 +3370,7 @@ oracle_client_version () } inline void options:: -oracle_client_version(const ::oracle_version& x) +oracle_client_version (const ::oracle_version& x) { this->oracle_client_version_ = x; } @@ -3361,7 +3382,7 @@ oracle_client_version_specified () const } inline void options:: -oracle_client_version_specified(bool x) +oracle_client_version_specified (bool x) { this->oracle_client_version_specified_ = x; } @@ -3379,7 +3400,7 @@ oracle_warn_truncation () } inline void options:: -oracle_warn_truncation(const bool& x) +oracle_warn_truncation (const bool& x) { this->oracle_warn_truncation_ = x; } @@ -3397,7 +3418,7 @@ mssql_server_version () } inline void options:: -mssql_server_version(const ::mssql_version& x) +mssql_server_version (const ::mssql_version& x) { this->mssql_server_version_ = x; } @@ -3409,7 +3430,7 @@ mssql_server_version_specified () const } inline void options:: -mssql_server_version_specified(bool x) +mssql_server_version_specified (bool x) { this->mssql_server_version_specified_ = x; } @@ -3427,7 +3448,7 @@ mssql_short_limit () } inline void options:: -mssql_short_limit(const unsigned int& x) +mssql_short_limit (const unsigned int& x) { this->mssql_short_limit_ = x; } @@ -3439,7 +3460,7 @@ mssql_short_limit_specified () const } inline void options:: -mssql_short_limit_specified(bool x) +mssql_short_limit_specified (bool x) { this->mssql_short_limit_specified_ = x; } diff --git a/odb/processor.cxx b/odb/odb/processor.cxx index dd0a706..fb129fa 100644 --- a/odb/processor.cxx +++ b/odb/odb/processor.cxx @@ -458,8 +458,14 @@ namespace // 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)) + if ( +#if BUILDING_GCC_MAJOR >= 14 + !DECL_OBJECT_MEMBER_FUNCTION_P (f) +#else + !DECL_NONSTATIC_MEMBER_FUNCTION_P (f) +#endif + || TREE_PRIVATE (f) + || TREE_PROTECTED (f)) continue; found_type r (k == "get" @@ -568,7 +574,13 @@ namespace #endif // We are only interested in non-static member functions. // - if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f)) + if ( +#if BUILDING_GCC_MAJOR >= 14 + !DECL_OBJECT_MEMBER_FUNCTION_P (f) +#else + !DECL_NONSTATIC_MEMBER_FUNCTION_P (f) +#endif + ) continue; if ((k == "get" @@ -1395,8 +1407,6 @@ namespace string const& prefix, bool obj_ptr) { - process_wrapper (t); - if (composite_wrapper (t)) return; @@ -1665,6 +1675,16 @@ namespace } } + // 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). @@ -1675,16 +1695,16 @@ namespace { case ck_ordered: { - comp = composite (*vt); + comp = composite_wrapper (*vt); break; } case ck_map: case ck_multimap: { - comp = composite (*kt); + comp = composite_wrapper (*kt); if (comp == 0 || column_count (*comp).soft == 0) { - comp = composite (*vt); + comp = composite_wrapper (*vt); break; } @@ -1696,7 +1716,7 @@ namespace case ck_set: case ck_multiset: { - comp = composite (*vt); + comp = composite_wrapper (*vt); if (comp == 0 || column_count (*comp).soft == 0) { comp = 0; @@ -2186,8 +2206,13 @@ namespace namespace_* ns (dynamic_cast<namespace_*> (s)); - if (ns == 0) - continue; // Some other scope. + if (ns == 0) // Some other scope. + { + if (!s->named_p ()) + break; + + continue; + } if (ns->extension ()) ns = &ns->original (); @@ -2199,7 +2224,7 @@ namespace break; } - if (ns->global_scope ()) + if (ns->global_scope ()) // Note: namespaces always named. break; } @@ -2694,15 +2719,20 @@ namespace namespace_* ns (dynamic_cast<namespace_*> (s)); - if (ns == 0) - continue; // Some other scope. + 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 ()) + if (ns->global_scope ()) // Note: namespace always named. break; else continue; diff --git a/odb/processor.hxx b/odb/odb/processor.hxx index 1e70cab..1e70cab 100644 --- a/odb/processor.hxx +++ b/odb/odb/processor.hxx diff --git a/odb/profile.cxx b/odb/odb/profile.cxx index 1a10bfb..1a10bfb 100644 --- a/odb/profile.cxx +++ b/odb/odb/profile.cxx diff --git a/odb/profile.hxx b/odb/odb/profile.hxx index 8b892dc..b6e8e53 100644 --- a/odb/profile.hxx +++ b/odb/odb/profile.hxx @@ -8,7 +8,7 @@ #include <vector> #include <string> -#include <cutl/fs/path.hxx> +#include <libcutl/fs/path.hxx> #include <odb/option-types.hxx> diff --git a/odb/relational/changelog.cxx b/odb/odb/relational/changelog.cxx index 99f72da..99f72da 100644 --- a/odb/relational/changelog.cxx +++ b/odb/odb/relational/changelog.cxx diff --git a/odb/relational/common-query.cxx b/odb/odb/relational/common-query.cxx index 53321ce..53321ce 100644 --- a/odb/relational/common-query.cxx +++ b/odb/odb/relational/common-query.cxx diff --git a/odb/relational/common-query.hxx b/odb/odb/relational/common-query.hxx index c29df6b..c29df6b 100644 --- a/odb/relational/common-query.hxx +++ b/odb/odb/relational/common-query.hxx diff --git a/odb/relational/common.cxx b/odb/odb/relational/common.cxx index 5c9126c..5c9126c 100644 --- a/odb/relational/common.cxx +++ b/odb/odb/relational/common.cxx diff --git a/odb/relational/common.hxx b/odb/odb/relational/common.hxx index 01266a0..01266a0 100644 --- a/odb/relational/common.hxx +++ b/odb/odb/relational/common.hxx diff --git a/odb/relational/common.txx b/odb/odb/relational/common.txx index 82a4a4a..82a4a4a 100644 --- a/odb/relational/common.txx +++ b/odb/odb/relational/common.txx diff --git a/odb/relational/context.cxx b/odb/odb/relational/context.cxx index 3fba69b..3fba69b 100644 --- a/odb/relational/context.cxx +++ b/odb/odb/relational/context.cxx diff --git a/odb/relational/context.hxx b/odb/odb/relational/context.hxx index db9b5be..db9b5be 100644 --- a/odb/relational/context.hxx +++ b/odb/odb/relational/context.hxx diff --git a/odb/relational/context.ixx b/odb/odb/relational/context.ixx index abf1fb5..abf1fb5 100644 --- a/odb/relational/context.ixx +++ b/odb/odb/relational/context.ixx diff --git a/odb/relational/generate.hxx b/odb/odb/relational/generate.hxx index c3d19c7..e597fb8 100644 --- a/odb/relational/generate.hxx +++ b/odb/odb/relational/generate.hxx @@ -5,7 +5,7 @@ #define ODB_RELATIONAL_GENERATE_HXX #include <string> -#include <cutl/shared-ptr.hxx> +#include <libcutl/shared-ptr.hxx> #include <odb/context.hxx> #include <odb/semantics/relational/model.hxx> diff --git a/odb/relational/header.cxx b/odb/odb/relational/header.cxx index 364d48e..364d48e 100644 --- a/odb/relational/header.cxx +++ b/odb/odb/relational/header.cxx diff --git a/odb/relational/header.hxx b/odb/odb/relational/header.hxx index 30a61ea..964aff2 100644 --- a/odb/relational/header.hxx +++ b/odb/odb/relational/header.hxx @@ -47,6 +47,8 @@ namespace relational typedef typename member_base_impl<T>::member_info member_info; + using member_base_impl<T>::container; + virtual bool pre (member_info& mi) { diff --git a/odb/relational/inline.cxx b/odb/odb/relational/inline.cxx index 5e60705..5e60705 100644 --- a/odb/relational/inline.cxx +++ b/odb/odb/relational/inline.cxx diff --git a/odb/relational/inline.hxx b/odb/odb/relational/inline.hxx index 48ababf..a609cc1 100644 --- a/odb/relational/inline.hxx +++ b/odb/odb/relational/inline.hxx @@ -678,6 +678,10 @@ namespace relational 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; diff --git a/odb/relational/model.cxx b/odb/odb/relational/model.cxx index 45d555a..45d555a 100644 --- a/odb/relational/model.cxx +++ b/odb/odb/relational/model.cxx diff --git a/odb/relational/model.hxx b/odb/odb/relational/model.hxx index b7a07ea..fdfa8fd 100644 --- a/odb/relational/model.hxx +++ b/odb/odb/relational/model.hxx @@ -555,9 +555,9 @@ namespace relational } virtual string - table_options (semantics::data_member&, semantics::type&) + table_options (semantics::data_member& m, semantics::type& ct) { - return ""; + return context::table_options (m, ct); } virtual void @@ -784,9 +784,9 @@ namespace relational class_ (sema_rel::model& model): model_ (model) {} virtual string - table_options (type&) + table_options (type& c) { - return ""; + return context::table_options (c); } virtual void diff --git a/odb/relational/mssql/common.cxx b/odb/odb/relational/mssql/common.cxx index 1070d21..1070d21 100644 --- a/odb/relational/mssql/common.cxx +++ b/odb/odb/relational/mssql/common.cxx diff --git a/odb/relational/mssql/common.hxx b/odb/odb/relational/mssql/common.hxx index 42ea412..42ea412 100644 --- a/odb/relational/mssql/common.hxx +++ b/odb/odb/relational/mssql/common.hxx diff --git a/odb/relational/mssql/context.cxx b/odb/odb/relational/mssql/context.cxx index afe1aa5..afe1aa5 100644 --- a/odb/relational/mssql/context.cxx +++ b/odb/odb/relational/mssql/context.cxx diff --git a/odb/relational/mssql/context.hxx b/odb/odb/relational/mssql/context.hxx index 7701aaa..7701aaa 100644 --- a/odb/relational/mssql/context.hxx +++ b/odb/odb/relational/mssql/context.hxx diff --git a/odb/relational/mssql/header.cxx b/odb/odb/relational/mssql/header.cxx index ebdc734..ebdc734 100644 --- a/odb/relational/mssql/header.cxx +++ b/odb/odb/relational/mssql/header.cxx diff --git a/odb/relational/mssql/inline.cxx b/odb/odb/relational/mssql/inline.cxx index eb581d6..eb581d6 100644 --- a/odb/relational/mssql/inline.cxx +++ b/odb/odb/relational/mssql/inline.cxx diff --git a/odb/relational/mssql/model.cxx b/odb/odb/relational/mssql/model.cxx index 0f5a85c..0f5a85c 100644 --- a/odb/relational/mssql/model.cxx +++ b/odb/odb/relational/mssql/model.cxx diff --git a/odb/relational/mssql/schema.cxx b/odb/odb/relational/mssql/schema.cxx index c5f6bc1..c5f6bc1 100644 --- a/odb/relational/mssql/schema.cxx +++ b/odb/odb/relational/mssql/schema.cxx diff --git a/odb/relational/mssql/source.cxx b/odb/odb/relational/mssql/source.cxx index 573104d..573104d 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/odb/relational/mssql/source.cxx diff --git a/odb/relational/mysql/common.cxx b/odb/odb/relational/mysql/common.cxx index d049443..d049443 100644 --- a/odb/relational/mysql/common.cxx +++ b/odb/odb/relational/mysql/common.cxx diff --git a/odb/relational/mysql/common.hxx b/odb/odb/relational/mysql/common.hxx index b43dc0d..b43dc0d 100644 --- a/odb/relational/mysql/common.hxx +++ b/odb/odb/relational/mysql/common.hxx diff --git a/odb/relational/mysql/context.cxx b/odb/odb/relational/mysql/context.cxx index 8b3d983..8b3d983 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/odb/relational/mysql/context.cxx diff --git a/odb/relational/mysql/context.hxx b/odb/odb/relational/mysql/context.hxx index 98574f2..98574f2 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/odb/relational/mysql/context.hxx diff --git a/odb/relational/mysql/header.cxx b/odb/odb/relational/mysql/header.cxx index 27bae48..27bae48 100644 --- a/odb/relational/mysql/header.cxx +++ b/odb/odb/relational/mysql/header.cxx diff --git a/odb/relational/mysql/inline.cxx b/odb/odb/relational/mysql/inline.cxx index bfa2c94..bfa2c94 100644 --- a/odb/relational/mysql/inline.cxx +++ b/odb/odb/relational/mysql/inline.cxx diff --git a/odb/relational/mysql/model.cxx b/odb/odb/relational/mysql/model.cxx index 2ec9d8b..17ed4c0 100644 --- a/odb/relational/mysql/model.cxx +++ b/odb/odb/relational/mysql/model.cxx @@ -110,10 +110,23 @@ namespace relational member_create (base const& x): base (x) {} virtual string - table_options (semantics::data_member&, semantics::type&) + table_options (semantics::data_member& m, semantics::type& c) { + string r (relational::member_create::table_options (m, c)); + string const& engine (options.mysql_engine ()); - return engine != "default" ? "ENGINE=" + 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_; @@ -123,10 +136,23 @@ namespace relational class_ (base const& x): base (x) {} virtual string - table_options (type&) + table_options (type& c) { + string r (relational::class_::table_options (c)); + string const& engine (options.mysql_engine ()); - return engine != "default" ? "ENGINE=" + 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/relational/mysql/schema.cxx b/odb/odb/relational/mysql/schema.cxx index 60dc95b..60dc95b 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/odb/relational/mysql/schema.cxx diff --git a/odb/relational/mysql/source.cxx b/odb/odb/relational/mysql/source.cxx index 9131ea7..9131ea7 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/odb/relational/mysql/source.cxx diff --git a/odb/relational/oracle/common.cxx b/odb/odb/relational/oracle/common.cxx index 7caafc9..7caafc9 100644 --- a/odb/relational/oracle/common.cxx +++ b/odb/odb/relational/oracle/common.cxx diff --git a/odb/relational/oracle/common.hxx b/odb/odb/relational/oracle/common.hxx index 1958aab..1958aab 100644 --- a/odb/relational/oracle/common.hxx +++ b/odb/odb/relational/oracle/common.hxx diff --git a/odb/relational/oracle/context.cxx b/odb/odb/relational/oracle/context.cxx index 12ce0aa..12ce0aa 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/odb/relational/oracle/context.cxx diff --git a/odb/relational/oracle/context.hxx b/odb/odb/relational/oracle/context.hxx index 6c55853..6c55853 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/odb/relational/oracle/context.hxx diff --git a/odb/relational/oracle/header.cxx b/odb/odb/relational/oracle/header.cxx index bf50bb2..bf50bb2 100644 --- a/odb/relational/oracle/header.cxx +++ b/odb/odb/relational/oracle/header.cxx diff --git a/odb/relational/oracle/inline.cxx b/odb/odb/relational/oracle/inline.cxx index 1b6d606..1b6d606 100644 --- a/odb/relational/oracle/inline.cxx +++ b/odb/odb/relational/oracle/inline.cxx diff --git a/odb/relational/oracle/model.cxx b/odb/odb/relational/oracle/model.cxx index b65e201..b65e201 100644 --- a/odb/relational/oracle/model.cxx +++ b/odb/odb/relational/oracle/model.cxx diff --git a/odb/relational/oracle/schema.cxx b/odb/odb/relational/oracle/schema.cxx index 75100b1..75100b1 100644 --- a/odb/relational/oracle/schema.cxx +++ b/odb/odb/relational/oracle/schema.cxx diff --git a/odb/relational/oracle/source.cxx b/odb/odb/relational/oracle/source.cxx index adf9864..adf9864 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/odb/relational/oracle/source.cxx diff --git a/odb/relational/pgsql/common.cxx b/odb/odb/relational/pgsql/common.cxx index 6a59954..6a59954 100644 --- a/odb/relational/pgsql/common.cxx +++ b/odb/odb/relational/pgsql/common.cxx diff --git a/odb/relational/pgsql/common.hxx b/odb/odb/relational/pgsql/common.hxx index 1d383bf..1d383bf 100644 --- a/odb/relational/pgsql/common.hxx +++ b/odb/odb/relational/pgsql/common.hxx diff --git a/odb/relational/pgsql/context.cxx b/odb/odb/relational/pgsql/context.cxx index a9f34dd..7f99f5d 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/odb/relational/pgsql/context.cxx @@ -85,7 +85,7 @@ namespace relational insert_send_auto_id = false; delay_freeing_statement_result = false; need_image_clone = false; - generate_bulk = false; + generate_bulk = true; global_index = true; global_fkey = false; data_->bind_vector_ = "pgsql::bind*"; diff --git a/odb/relational/pgsql/context.hxx b/odb/odb/relational/pgsql/context.hxx index 64e0b1a..64e0b1a 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/odb/relational/pgsql/context.hxx diff --git a/odb/relational/pgsql/header.cxx b/odb/odb/relational/pgsql/header.cxx index ff00eaa..c3efc3e 100644 --- a/odb/relational/pgsql/header.cxx +++ b/odb/odb/relational/pgsql/header.cxx @@ -87,6 +87,20 @@ namespace relational } 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 diff --git a/odb/relational/pgsql/inline.cxx b/odb/odb/relational/pgsql/inline.cxx index 08688c3..08688c3 100644 --- a/odb/relational/pgsql/inline.cxx +++ b/odb/odb/relational/pgsql/inline.cxx diff --git a/odb/relational/pgsql/model.cxx b/odb/odb/relational/pgsql/model.cxx index 092f8bb..092f8bb 100644 --- a/odb/relational/pgsql/model.cxx +++ b/odb/odb/relational/pgsql/model.cxx diff --git a/odb/relational/pgsql/schema.cxx b/odb/odb/relational/pgsql/schema.cxx index b9c3f2e..b9c3f2e 100644 --- a/odb/relational/pgsql/schema.cxx +++ b/odb/odb/relational/pgsql/schema.cxx diff --git a/odb/relational/pgsql/source.cxx b/odb/odb/relational/pgsql/source.cxx index 580103d..b881e48 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/odb/relational/pgsql/source.cxx @@ -203,7 +203,7 @@ namespace relational traverse_numeric (member_info& mi) { os << b << ".type = pgsql::bind::numeric;" - << b << ".buffer = " << arg << "." << mi.var << "value.data ();" + << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();" << b << ".capacity = " << arg << "." << mi.var << "value.capacity ();" << b << ".size = &" << arg << "." << mi.var << "size;" @@ -224,7 +224,7 @@ namespace relational { os << b << ".type = " << char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";" - << b << ".buffer = " << arg << "." << mi.var << "value.data ();" + << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();" << b << ".capacity = " << arg << "." << mi.var << "value.capacity ();" << b << ".size = &" << arg << "." << mi.var << "size;" @@ -245,7 +245,7 @@ namespace relational traverse_varbit (member_info& mi) { os << b << ".type = pgsql::bind::varbit;" - << b << ".buffer = " << arg << "." << mi.var << "value.data ();" + << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();" << b << ".capacity = " << arg << "." << mi.var << "value.capacity ();" << b << ".size = &" << arg << "." << mi.var << "size;" diff --git a/odb/relational/processor.cxx b/odb/odb/relational/processor.cxx index aac8d79..0f60359 100644 --- a/odb/relational/processor.cxx +++ b/odb/odb/relational/processor.cxx @@ -1456,6 +1456,8 @@ namespace relational object_members_base::traverse (*pointer.obj); } + using object_members_base::traverse; // Unhide. + virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { diff --git a/odb/relational/processor.hxx b/odb/odb/relational/processor.hxx index 71b8643..71b8643 100644 --- a/odb/relational/processor.hxx +++ b/odb/odb/relational/processor.hxx diff --git a/odb/relational/schema-source.cxx b/odb/odb/relational/schema-source.cxx index 5659485..5659485 100644 --- a/odb/relational/schema-source.cxx +++ b/odb/odb/relational/schema-source.cxx diff --git a/odb/relational/schema-source.hxx b/odb/odb/relational/schema-source.hxx index d2235f5..d2235f5 100644 --- a/odb/relational/schema-source.hxx +++ b/odb/odb/relational/schema-source.hxx diff --git a/odb/relational/schema.cxx b/odb/odb/relational/schema.cxx index dd70bfa..dd70bfa 100644 --- a/odb/relational/schema.cxx +++ b/odb/odb/relational/schema.cxx diff --git a/odb/relational/schema.hxx b/odb/odb/relational/schema.hxx index c5e16c6..cd975b7 100644 --- a/odb/relational/schema.hxx +++ b/odb/odb/relational/schema.hxx @@ -442,6 +442,9 @@ namespace relational traverse (*t, true); } + using add_table::traverse; // Unhide. + using alter_table::traverse; // Unhide. + using table::names; void diff --git a/odb/relational/source.cxx b/odb/odb/relational/source.cxx index 20c431a..abc0a46 100644 --- a/odb/relational/source.cxx +++ b/odb/odb/relational/source.cxx @@ -1169,14 +1169,13 @@ traverse_object (type& c) os << ", bool top, bool dyn"; os << ")" - << "{" - << "ODB_POTENTIALLY_UNUSED (db);"; + << "{"; if (poly) - os << "ODB_POTENTIALLY_UNUSED (top);"; + os << "ODB_POTENTIALLY_UNUSED (top);" + << endl; - os << endl - << "using namespace " << db << ";" + os << "using namespace " << db << ";" << endl; if (poly) @@ -1201,7 +1200,7 @@ traverse_object (type& c) << endl; os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -1441,13 +1440,11 @@ traverse_object (type& c) << "std::size_t n," << endl << "multiple_exceptions& mex)" << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << endl << "using namespace " << db << ";" << endl; os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -1464,13 +1461,25 @@ traverse_object (type& c) << "{" << "const object_type& obj (*objs[i]);" << "callback (db, obj, callback_event::pre_persist);" - //@@ assumption: generate_grow is false + //@@ 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 - << "init (sts.image (i), obj, statement_insert" << - (versioned ? ", svm" : "") << ");" + << "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 is false + //@@ assumption: generate_grow: as above os << "binding& imb (sts.insert_image_binding ());" << "if (imb.version == 0)" << "{" @@ -1486,7 +1495,7 @@ traverse_object (type& c) if (bv || auto_id) { os << "binding& idb (sts.id_image_binding ());" - //@@ assumption: generate_grow is false + //@@ assumption: generate_grow: as above << "if (idb.version == 0)" << "{" << "bind (idb.bind, sts.id_image ());" @@ -1692,7 +1701,7 @@ traverse_object (type& c) sts = true; os << db << "::transaction& tr (" << db << "::transaction::current ());" - << db << "::connection& conn (tr.connection ());" + << db << "::connection& conn (tr.connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl; @@ -1787,7 +1796,7 @@ traverse_object (type& c) { os << db << "::transaction& tr (" << db << "::transaction::current ());" - << db << "::connection& conn (tr.connection ());" + << db << "::connection& conn (tr.connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl; @@ -1877,7 +1886,7 @@ traverse_object (type& c) if (!sts) os << db << "::transaction& tr (" << db << "::transaction::current ());" - << db << "::connection& conn (tr.connection ());" + << db << "::connection& conn (tr.connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl; @@ -1976,7 +1985,7 @@ traverse_object (type& c) if (!sts) os << db << "::transaction& tr (" << db << "::transaction::current ());" - << db << "::connection& conn (tr.connection ());" + << db << "::connection& conn (tr.connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl; @@ -2194,13 +2203,11 @@ traverse_object (type& c) << "std::size_t n," << endl << "multiple_exceptions& mex)" << "{" - << "ODB_POTENTIALLY_UNUSED (db);" - << endl << "using namespace " << db << ";" << "using " << db << "::update_statement;" << endl << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -2217,9 +2224,24 @@ traverse_object (type& c) if (opt != 0) os << "const version_type& v (version (obj));"; - os << "init (sts.id_image (i), id (obj)" << (opt != 0 ? ", &v" : "") << ");" - //@@ assumption: generate_grow false - << "init (sts.image (i), obj, statement_update);" + 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. @@ -2228,7 +2250,7 @@ traverse_object (type& c) << "binding& imb (sts.update_image_binding ());" << endl; - //@@ assumption: generate_grow false + //@@ assumption: generate_grow: as above // os << "bool u (false);" // Avoid incrementing version twice. << "if (imb.version == 0)" @@ -2239,7 +2261,7 @@ traverse_object (type& c) << "u = true;" << "}"; - //@@ assumption: generate_grow false + //@@ assumption: generate_grow: as above // os << "if (idb.version == 0)" << "{" @@ -2321,16 +2343,15 @@ traverse_object (type& c) os << ")" << "{" - << "using namespace " << db << ";" - << endl - << "ODB_POTENTIALLY_UNUSED (db);"; + << "using namespace " << db << ";"; if (poly) - os << "ODB_POTENTIALLY_UNUSED (top);"; + os << endl + << "ODB_POTENTIALLY_UNUSED (top);"; os << endl << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl; @@ -2449,10 +2470,8 @@ traverse_object (type& c) << "{" << "using namespace " << db << ";" << endl - << "ODB_POTENTIALLY_UNUSED (db);" - << endl << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl @@ -2460,7 +2479,8 @@ traverse_object (type& c) << "init (sts.id_image (i), *ids[i]);" << endl << "binding& idb (sts.id_image_binding ());" - //@@ assumption: generate_grow false + //@@ assumption: generate_grow is false or it only affects select (like + // in pgsql). << "if (idb.version == 0)" << "{" << "bind (idb.bind, sts.id_image ());" @@ -2542,7 +2562,7 @@ traverse_object (type& c) os << "using namespace " << db << ";" << endl << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -2830,10 +2850,8 @@ traverse_object (type& c) { os << "using namespace " << db << ";" << endl - << "ODB_POTENTIALLY_UNUSED (db);" - << endl << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl @@ -2846,7 +2864,8 @@ traverse_object (type& c) << "}"; os << "binding& idb (sts.id_image_binding ());" - //@@ assumption: generate_grow false + //@@ assumption: generate_grow is false or it only affects select + // (like in pgsql). << "if (idb.version == 0)" << "{" << "bind (idb.bind, sts.id_image ());" @@ -2917,7 +2936,7 @@ traverse_object (type& c) os << "}"; os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -3110,7 +3129,7 @@ traverse_object (type& c) if (!abst) { os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -3217,7 +3236,7 @@ traverse_object (type& c) if (!abst) { os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -3909,7 +3928,7 @@ traverse_object (type& c) << endl << "object_type& obj (static_cast<object_type&> (r));" << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());" << endl @@ -4135,7 +4154,7 @@ traverse_object (type& c) // os << "result< " << traits << "::object_type >" << endl << traits << "::" << endl - << "query (database&, const query_base_type& q)" + << "query (database& db, const query_base_type& q)" << "{" << "using namespace " << db << ";" << "using odb::details::shared;" @@ -4143,7 +4162,7 @@ traverse_object (type& c) << endl; os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << endl << "statements_type& sts (" << endl << "conn.statement_cache ().find_object<object_type> ());"; @@ -4224,12 +4243,12 @@ traverse_object (type& c) // erase_query // os << "unsigned long long " << traits << "::" << endl - << "erase_query (database&, const query_base_type& q)" + << "erase_query (database& db, const query_base_type& q)" << "{" << "using namespace " << db << ";" << endl << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << endl << "std::string text (erase_query_statement);" << "if (!q.empty ())" @@ -4363,16 +4382,16 @@ traverse_object (type& c) << "static_cast<select_statement*> (pq.stmt.get ())));" << endl; - os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + os << db << "::transaction& tr (" << db << "::transaction::current ());" + << "ODB_POTENTIALLY_UNUSED (tr);" << endl << "// The connection used by the current transaction and the" << endl << "// one used to prepare this statement must be the same." << endl << "//" << endl - << "assert (&conn == &st->connection ());" + << "assert (q.verify_connection (tr));" << endl << "statements_type& sts (" << endl - << "conn.statement_cache ().find_object<object_type> ());"; + << "st->connection ().statement_cache ().find_object<object_type> ());"; if (versioned) os << "const schema_version_migration& svm (" << @@ -5265,9 +5284,11 @@ traverse_view (type& c) << "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 << "::transaction::current ().connection (*db));" << endl; names (c, init_view_pointer_member_pre_names_); @@ -5514,7 +5535,7 @@ traverse_view (type& c) { os << "result< " << traits << "::view_type >" << endl << traits << "::" << endl - << "query (database&, const query_base_type& q)" + << "query (database& db, const query_base_type& q)" << "{" << "using namespace " << db << ";" << "using odb::details::shared;" @@ -5522,7 +5543,7 @@ traverse_view (type& c) << endl; os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + << db << "::transaction::current ().connection (db));" << "statements_type& sts (" << endl << "conn.statement_cache ().find_view<view_type> ());"; @@ -5662,16 +5683,16 @@ traverse_view (type& c) << "static_cast<select_statement*> (pq.stmt.get ())));" << endl; - os << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" + os << db << "::transaction& tr (" << db << "::transaction::current ());" + << "ODB_POTENTIALLY_UNUSED (tr);" << endl << "// The connection used by the current transaction and the" << endl << "// one used to prepare this statement must be the same." << endl << "//" << endl - << "assert (&conn == &st->connection ());" + << "assert (q.verify_connection (tr));" << endl << "statements_type& sts (" << endl - << "conn.statement_cache ().find_view<view_type> ());"; + << "st->connection ().statement_cache ().find_view<view_type> ());"; if (versioned) os << "const schema_version_migration& svm (" << diff --git a/odb/relational/source.hxx b/odb/odb/relational/source.hxx index f82b5ad..ba6b2be 100644 --- a/odb/relational/source.hxx +++ b/odb/odb/relational/source.hxx @@ -1370,6 +1370,8 @@ namespace relational typedef typename member_base_impl<T>::member_info member_info; + using member_base_impl<T>::container; + virtual bool pre (member_info& mi) { @@ -1751,6 +1753,8 @@ namespace relational typedef typename member_base_impl<T>::member_info member_info; + using member_base_impl<T>::container; + virtual bool pre (member_info& mi) { @@ -2025,6 +2029,8 @@ namespace relational typedef typename member_base_impl<T>::member_info member_info; + using member_base_impl<T>::container; + virtual void set_null (member_info&) = 0; @@ -2496,6 +2502,8 @@ namespace relational 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; @@ -3636,7 +3644,9 @@ namespace relational sc.push_back ( statement_column ( inv_qtable, - inv_qtable + "." + quote_id (i->name), + convert_from (inv_qtable + "." + quote_id (i->name), + i->type, + *i->member), i->type, *i->member, inv_id_cols->size () == 1 ? "id" : "")); @@ -3658,7 +3668,9 @@ namespace relational sc.push_back ( statement_column ( inv_qtable, - inv_qtable + "." + quote_id (i->name), + convert_from (inv_qtable + "." + quote_id (i->name), + i->type, + *i->member), i->type, *i->member)); } @@ -6473,6 +6485,8 @@ namespace relational rs->base = 0; } + using class_::traverse; // Unhide. + protected: semantics::class_& c_; string scope_; @@ -6553,6 +6567,7 @@ namespace relational : 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_"), @@ -6581,6 +6596,7 @@ namespace relational 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_"), diff --git a/odb/relational/sqlite/common.cxx b/odb/odb/relational/sqlite/common.cxx index 03a3599..03a3599 100644 --- a/odb/relational/sqlite/common.cxx +++ b/odb/odb/relational/sqlite/common.cxx diff --git a/odb/relational/sqlite/common.hxx b/odb/odb/relational/sqlite/common.hxx index 4d6089e..4d6089e 100644 --- a/odb/relational/sqlite/common.hxx +++ b/odb/odb/relational/sqlite/common.hxx diff --git a/odb/relational/sqlite/context.cxx b/odb/odb/relational/sqlite/context.cxx index 9a4369f..9a4369f 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/odb/relational/sqlite/context.cxx diff --git a/odb/relational/sqlite/context.hxx b/odb/odb/relational/sqlite/context.hxx index 777998b..777998b 100644 --- a/odb/relational/sqlite/context.hxx +++ b/odb/odb/relational/sqlite/context.hxx diff --git a/odb/relational/sqlite/header.cxx b/odb/odb/relational/sqlite/header.cxx index 1aafe7a..1aafe7a 100644 --- a/odb/relational/sqlite/header.cxx +++ b/odb/odb/relational/sqlite/header.cxx diff --git a/odb/relational/sqlite/inline.cxx b/odb/odb/relational/sqlite/inline.cxx index dd3274f..dd3274f 100644 --- a/odb/relational/sqlite/inline.cxx +++ b/odb/odb/relational/sqlite/inline.cxx diff --git a/odb/relational/sqlite/model.cxx b/odb/odb/relational/sqlite/model.cxx index da16ded..da16ded 100644 --- a/odb/relational/sqlite/model.cxx +++ b/odb/odb/relational/sqlite/model.cxx diff --git a/odb/relational/sqlite/schema.cxx b/odb/odb/relational/sqlite/schema.cxx index f5549b4..f5549b4 100644 --- a/odb/relational/sqlite/schema.cxx +++ b/odb/odb/relational/sqlite/schema.cxx diff --git a/odb/relational/sqlite/source.cxx b/odb/odb/relational/sqlite/source.cxx index 2624af2..5a4b9d3 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/odb/relational/sqlite/source.cxx @@ -399,7 +399,8 @@ namespace relational virtual void process_statement_columns (relational::statement_columns& cols, - statement_kind sk) + statement_kind sk, + bool) { statement_columns_common::process (cols, sk); } diff --git a/odb/relational/validator.cxx b/odb/odb/relational/validator.cxx index 50c887e..50c887e 100644 --- a/odb/relational/validator.cxx +++ b/odb/odb/relational/validator.cxx diff --git a/odb/relational/validator.hxx b/odb/odb/relational/validator.hxx index d6602f7..d6602f7 100644 --- a/odb/relational/validator.hxx +++ b/odb/odb/relational/validator.hxx diff --git a/odb/semantics.hxx b/odb/odb/semantics.hxx index 83416a6..83416a6 100644 --- a/odb/semantics.hxx +++ b/odb/odb/semantics.hxx diff --git a/odb/semantics/class-template.cxx b/odb/odb/semantics/class-template.cxx index d764b79..f8bbca4 100644 --- a/odb/semantics/class-template.cxx +++ b/odb/odb/semantics/class-template.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/class-template.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/class-template.hxx> namespace semantics diff --git a/odb/semantics/class-template.hxx b/odb/odb/semantics/class-template.hxx index bffb3f2..bffb3f2 100644 --- a/odb/semantics/class-template.hxx +++ b/odb/odb/semantics/class-template.hxx diff --git a/odb/semantics/class.cxx b/odb/odb/semantics/class.cxx index acecb35..97cf088 100644 --- a/odb/semantics/class.cxx +++ b/odb/odb/semantics/class.cxx @@ -3,7 +3,7 @@ #include <odb/gcc.hxx> // TYPE_HAS_DEFAULT_CONSTRUCTOR -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/class.hxx> namespace semantics diff --git a/odb/semantics/class.hxx b/odb/odb/semantics/class.hxx index e02337a..e02337a 100644 --- a/odb/semantics/class.hxx +++ b/odb/odb/semantics/class.hxx diff --git a/odb/semantics/derived.cxx b/odb/odb/semantics/derived.cxx index 9cf1504..771ad21 100644 --- a/odb/semantics/derived.cxx +++ b/odb/odb/semantics/derived.cxx @@ -3,7 +3,7 @@ #include <sstream> -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/derived.hxx> using namespace std; diff --git a/odb/semantics/derived.hxx b/odb/odb/semantics/derived.hxx index 60c4896..e58ec9f 100644 --- a/odb/semantics/derived.hxx +++ b/odb/odb/semantics/derived.hxx @@ -416,6 +416,8 @@ namespace semantics string fq_name (names*, string& trailer) const; + using derived_type::fq_name; // Unhide. + public: array (path const&, size_t line, diff --git a/odb/semantics/elements.cxx b/odb/odb/semantics/elements.cxx index 2d266cf..b5793d0 100644 --- a/odb/semantics/elements.cxx +++ b/odb/odb/semantics/elements.cxx @@ -3,7 +3,7 @@ #include <odb/gcc.hxx> -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/cxx-lexer.hxx> @@ -56,7 +56,7 @@ namespace semantics if (p == &s) return true; - if (p->global_scope ()) + if (!p->named_p () || p->global_scope ()) break; } @@ -476,7 +476,7 @@ namespace semantics // Look in the outer scope unless requested not to or if this is // the global scope. // - if ((flags & exclude_outer) == 0 && !global_scope ()) + if ((flags & exclude_outer) == 0 && named_p () && !global_scope ()) return scope ().lookup (name, ti, flags, hidden); return 0; diff --git a/odb/semantics/elements.hxx b/odb/odb/semantics/elements.hxx index 12164ba..699a1be 100644 --- a/odb/semantics/elements.hxx +++ b/odb/odb/semantics/elements.hxx @@ -13,11 +13,11 @@ #include <utility> // std::pair #include <cassert> -#include <cutl/fs/path.hxx> -#include <cutl/container/graph.hxx> -#include <cutl/container/pointer-iterator.hxx> -#include <cutl/compiler/type-id.hxx> -#include <cutl/compiler/context.hxx> +#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> diff --git a/odb/semantics/elements.ixx b/odb/odb/semantics/elements.ixx index 32f9ced..32f9ced 100644 --- a/odb/semantics/elements.ixx +++ b/odb/odb/semantics/elements.ixx diff --git a/odb/semantics/enum.cxx b/odb/odb/semantics/enum.cxx index 7fd8204..6432986 100644 --- a/odb/semantics/enum.cxx +++ b/odb/odb/semantics/enum.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/enum.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/enum.hxx> namespace semantics diff --git a/odb/semantics/enum.hxx b/odb/odb/semantics/enum.hxx index bfcce53..bfcce53 100644 --- a/odb/semantics/enum.hxx +++ b/odb/odb/semantics/enum.hxx diff --git a/odb/semantics/fundamental.cxx b/odb/odb/semantics/fundamental.cxx index 82446a1..ed4a67f 100644 --- a/odb/semantics/fundamental.cxx +++ b/odb/odb/semantics/fundamental.cxx @@ -3,7 +3,7 @@ #include <odb/gcc.hxx> -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/fundamental.hxx> namespace semantics diff --git a/odb/semantics/fundamental.hxx b/odb/odb/semantics/fundamental.hxx index 15b5cbb..15b5cbb 100644 --- a/odb/semantics/fundamental.hxx +++ b/odb/odb/semantics/fundamental.hxx diff --git a/odb/semantics/namespace.cxx b/odb/odb/semantics/namespace.cxx index 0e1442c..d9be903 100644 --- a/odb/semantics/namespace.cxx +++ b/odb/odb/semantics/namespace.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/namespace.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/namespace.hxx> namespace semantics diff --git a/odb/semantics/namespace.hxx b/odb/odb/semantics/namespace.hxx index b025c2e..b025c2e 100644 --- a/odb/semantics/namespace.hxx +++ b/odb/odb/semantics/namespace.hxx diff --git a/odb/semantics/relational.hxx b/odb/odb/semantics/relational.hxx index db08a61..db08a61 100644 --- a/odb/semantics/relational.hxx +++ b/odb/odb/semantics/relational.hxx diff --git a/odb/semantics/relational/changelog.cxx b/odb/odb/semantics/relational/changelog.cxx index 8cee9dd..353497f 100644 --- a/odb/semantics/relational/changelog.cxx +++ b/odb/odb/semantics/relational/changelog.cxx @@ -4,7 +4,7 @@ #include <vector> #include <sstream> -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/changelog.hxx> #include <odb/semantics/relational/model.hxx> diff --git a/odb/semantics/relational/changelog.hxx b/odb/odb/semantics/relational/changelog.hxx index 2398cf6..2398cf6 100644 --- a/odb/semantics/relational/changelog.hxx +++ b/odb/odb/semantics/relational/changelog.hxx diff --git a/odb/semantics/relational/changeset.cxx b/odb/odb/semantics/relational/changeset.cxx index e643285..b044a0c 100644 --- a/odb/semantics/relational/changeset.cxx +++ b/odb/odb/semantics/relational/changeset.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/changeset.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/changeset.hxx> diff --git a/odb/semantics/relational/changeset.hxx b/odb/odb/semantics/relational/changeset.hxx index efe2c61..efe2c61 100644 --- a/odb/semantics/relational/changeset.hxx +++ b/odb/odb/semantics/relational/changeset.hxx diff --git a/odb/semantics/relational/column.cxx b/odb/odb/semantics/relational/column.cxx index e62a460..9d4d6e5 100644 --- a/odb/semantics/relational/column.cxx +++ b/odb/odb/semantics/relational/column.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/column.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/column.hxx> diff --git a/odb/semantics/relational/column.hxx b/odb/odb/semantics/relational/column.hxx index b7a2c31..b7a2c31 100644 --- a/odb/semantics/relational/column.hxx +++ b/odb/odb/semantics/relational/column.hxx diff --git a/odb/semantics/relational/deferrable.cxx b/odb/odb/semantics/relational/deferrable.cxx index 076ff69..076ff69 100644 --- a/odb/semantics/relational/deferrable.cxx +++ b/odb/odb/semantics/relational/deferrable.cxx diff --git a/odb/semantics/relational/deferrable.hxx b/odb/odb/semantics/relational/deferrable.hxx index b2f888d..b2f888d 100644 --- a/odb/semantics/relational/deferrable.hxx +++ b/odb/odb/semantics/relational/deferrable.hxx diff --git a/odb/semantics/relational/elements.cxx b/odb/odb/semantics/relational/elements.cxx index 192c882..de1878a 100644 --- a/odb/semantics/relational/elements.cxx +++ b/odb/odb/semantics/relational/elements.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/elements.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/elements.hxx> #include <odb/semantics/relational/column.hxx> diff --git a/odb/semantics/relational/elements.hxx b/odb/odb/semantics/relational/elements.hxx index 61cd4f3..4036942 100644 --- a/odb/semantics/relational/elements.hxx +++ b/odb/odb/semantics/relational/elements.hxx @@ -10,18 +10,12 @@ #include <string> #include <cassert> -#include <cutl/container/graph.hxx> -#include <cutl/container/pointer-iterator.hxx> -#include <cutl/compiler/context.hxx> +#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 <cutl/xml/parser.hxx> -#include <cutl/xml/serializer.hxx> -namespace cutl {namespace xml {typedef parser content;}} -#endif #include <odb/semantics/relational/name.hxx> diff --git a/odb/semantics/relational/elements.txx b/odb/odb/semantics/relational/elements.txx index 2362d48..2362d48 100644 --- a/odb/semantics/relational/elements.txx +++ b/odb/odb/semantics/relational/elements.txx diff --git a/odb/semantics/relational/foreign-key.cxx b/odb/odb/semantics/relational/foreign-key.cxx index b4c85f9..0357d95 100644 --- a/odb/semantics/relational/foreign-key.cxx +++ b/odb/odb/semantics/relational/foreign-key.cxx @@ -4,7 +4,7 @@ #include <ostream> #include <istream> -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/foreign-key.hxx> diff --git a/odb/semantics/relational/foreign-key.hxx b/odb/odb/semantics/relational/foreign-key.hxx index 32179fa..32179fa 100644 --- a/odb/semantics/relational/foreign-key.hxx +++ b/odb/odb/semantics/relational/foreign-key.hxx diff --git a/odb/semantics/relational/index.cxx b/odb/odb/semantics/relational/index.cxx index 7e6bb94..2329f3a 100644 --- a/odb/semantics/relational/index.cxx +++ b/odb/odb/semantics/relational/index.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/index.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/index.hxx> diff --git a/odb/semantics/relational/index.hxx b/odb/odb/semantics/relational/index.hxx index 68648cb..68648cb 100644 --- a/odb/semantics/relational/index.hxx +++ b/odb/odb/semantics/relational/index.hxx diff --git a/odb/semantics/relational/key.cxx b/odb/odb/semantics/relational/key.cxx index 318fe96..3511618 100644 --- a/odb/semantics/relational/key.cxx +++ b/odb/odb/semantics/relational/key.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/key.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/key.hxx> #include <odb/semantics/relational/column.hxx> diff --git a/odb/semantics/relational/key.hxx b/odb/odb/semantics/relational/key.hxx index 814d2ec..814d2ec 100644 --- a/odb/semantics/relational/key.hxx +++ b/odb/odb/semantics/relational/key.hxx diff --git a/odb/semantics/relational/model.cxx b/odb/odb/semantics/relational/model.cxx index b300274..8763045 100644 --- a/odb/semantics/relational/model.cxx +++ b/odb/odb/semantics/relational/model.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/model.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/model.hxx> diff --git a/odb/semantics/relational/model.hxx b/odb/odb/semantics/relational/model.hxx index 02d1863..02d1863 100644 --- a/odb/semantics/relational/model.hxx +++ b/odb/odb/semantics/relational/model.hxx diff --git a/odb/semantics/relational/name.cxx b/odb/odb/semantics/relational/name.cxx index 6eb2e16..6eb2e16 100644 --- a/odb/semantics/relational/name.cxx +++ b/odb/odb/semantics/relational/name.cxx diff --git a/odb/semantics/relational/name.hxx b/odb/odb/semantics/relational/name.hxx index 5268b4a..5268b4a 100644 --- a/odb/semantics/relational/name.hxx +++ b/odb/odb/semantics/relational/name.hxx diff --git a/odb/semantics/relational/primary-key.cxx b/odb/odb/semantics/relational/primary-key.cxx index 043374f..235340f 100644 --- a/odb/semantics/relational/primary-key.cxx +++ b/odb/odb/semantics/relational/primary-key.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/primary-key.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/primary-key.hxx> diff --git a/odb/semantics/relational/primary-key.hxx b/odb/odb/semantics/relational/primary-key.hxx index 114f682..114f682 100644 --- a/odb/semantics/relational/primary-key.hxx +++ b/odb/odb/semantics/relational/primary-key.hxx diff --git a/odb/semantics/relational/table.cxx b/odb/odb/semantics/relational/table.cxx index b9700b1..3bf763d 100644 --- a/odb/semantics/relational/table.cxx +++ b/odb/odb/semantics/relational/table.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/relational/table.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/relational/table.hxx> diff --git a/odb/semantics/relational/table.hxx b/odb/odb/semantics/relational/table.hxx index 1c4efcf..1c4efcf 100644 --- a/odb/semantics/relational/table.hxx +++ b/odb/odb/semantics/relational/table.hxx diff --git a/odb/semantics/template.cxx b/odb/odb/semantics/template.cxx index d49cf20..f492be0 100644 --- a/odb/semantics/template.cxx +++ b/odb/odb/semantics/template.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/template.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/template.hxx> namespace semantics diff --git a/odb/semantics/template.hxx b/odb/odb/semantics/template.hxx index 11fe340..11fe340 100644 --- a/odb/semantics/template.hxx +++ b/odb/odb/semantics/template.hxx diff --git a/odb/semantics/union-template.cxx b/odb/odb/semantics/union-template.cxx index f2c3f94..21fc9c0 100644 --- a/odb/semantics/union-template.cxx +++ b/odb/odb/semantics/union-template.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/union-template.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/union-template.hxx> namespace semantics diff --git a/odb/semantics/union-template.hxx b/odb/odb/semantics/union-template.hxx index 3e719b7..3e719b7 100644 --- a/odb/semantics/union-template.hxx +++ b/odb/odb/semantics/union-template.hxx diff --git a/odb/semantics/union.cxx b/odb/odb/semantics/union.cxx index 980dfa4..007ef57 100644 --- a/odb/semantics/union.cxx +++ b/odb/odb/semantics/union.cxx @@ -1,7 +1,7 @@ // file : odb/semantics/union.cxx // license : GNU GPL v3; see accompanying LICENSE file -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/union.hxx> namespace semantics diff --git a/odb/semantics/union.hxx b/odb/odb/semantics/union.hxx index 79adc42..79adc42 100644 --- a/odb/semantics/union.hxx +++ b/odb/odb/semantics/union.hxx diff --git a/odb/semantics/unit.cxx b/odb/odb/semantics/unit.cxx index fe191bc..4f92aed 100644 --- a/odb/semantics/unit.cxx +++ b/odb/odb/semantics/unit.cxx @@ -3,7 +3,7 @@ #include <odb/gcc.hxx> -#include <cutl/compiler/type-info.hxx> +#include <libcutl/compiler/type-info.hxx> #include <odb/semantics/unit.hxx> namespace semantics diff --git a/odb/semantics/unit.hxx b/odb/odb/semantics/unit.hxx index cfccbff..cfccbff 100644 --- a/odb/semantics/unit.hxx +++ b/odb/odb/semantics/unit.hxx diff --git a/odb/source.cxx b/odb/odb/source.cxx index b2a39be..b2a39be 100644 --- a/odb/source.cxx +++ b/odb/odb/source.cxx diff --git a/odb/sql-lexer.cxx b/odb/odb/sql-lexer.cxx index ab6f549..ab6f549 100644 --- a/odb/sql-lexer.cxx +++ b/odb/odb/sql-lexer.cxx diff --git a/odb/sql-lexer.hxx b/odb/odb/sql-lexer.hxx index 9d24233..9d24233 100644 --- a/odb/sql-lexer.hxx +++ b/odb/odb/sql-lexer.hxx diff --git a/odb/sql-lexer.ixx b/odb/odb/sql-lexer.ixx index 9179804..9179804 100644 --- a/odb/sql-lexer.ixx +++ b/odb/odb/sql-lexer.ixx diff --git a/odb/sql-token.cxx b/odb/odb/sql-token.cxx index da9ecb2..da9ecb2 100644 --- a/odb/sql-token.cxx +++ b/odb/odb/sql-token.cxx diff --git a/odb/sql-token.hxx b/odb/odb/sql-token.hxx index c34de21..c34de21 100644 --- a/odb/sql-token.hxx +++ b/odb/odb/sql-token.hxx diff --git a/odb/sql-token.ixx b/odb/odb/sql-token.ixx index 5118c9f..5118c9f 100644 --- a/odb/sql-token.ixx +++ b/odb/odb/sql-token.ixx diff --git a/odb/traversal.hxx b/odb/odb/traversal.hxx index 7f421b1..7f421b1 100644 --- a/odb/traversal.hxx +++ b/odb/odb/traversal.hxx diff --git a/odb/traversal/class-template.cxx b/odb/odb/traversal/class-template.cxx index b04b625..b04b625 100644 --- a/odb/traversal/class-template.cxx +++ b/odb/odb/traversal/class-template.cxx diff --git a/odb/traversal/class-template.hxx b/odb/odb/traversal/class-template.hxx index 18e1e5b..18e1e5b 100644 --- a/odb/traversal/class-template.hxx +++ b/odb/odb/traversal/class-template.hxx diff --git a/odb/traversal/class.cxx b/odb/odb/traversal/class.cxx index 80c8b80..80c8b80 100644 --- a/odb/traversal/class.cxx +++ b/odb/odb/traversal/class.cxx diff --git a/odb/traversal/class.hxx b/odb/odb/traversal/class.hxx index de86cc0..de86cc0 100644 --- a/odb/traversal/class.hxx +++ b/odb/odb/traversal/class.hxx diff --git a/odb/traversal/derived.cxx b/odb/odb/traversal/derived.cxx index a0acab8..a0acab8 100644 --- a/odb/traversal/derived.cxx +++ b/odb/odb/traversal/derived.cxx diff --git a/odb/traversal/derived.hxx b/odb/odb/traversal/derived.hxx index b7648aa..b7648aa 100644 --- a/odb/traversal/derived.hxx +++ b/odb/odb/traversal/derived.hxx diff --git a/odb/traversal/elements.cxx b/odb/odb/traversal/elements.cxx index f95917a..f95917a 100644 --- a/odb/traversal/elements.cxx +++ b/odb/odb/traversal/elements.cxx diff --git a/odb/traversal/elements.hxx b/odb/odb/traversal/elements.hxx index c4236ce..d67a6d8 100644 --- a/odb/traversal/elements.hxx +++ b/odb/odb/traversal/elements.hxx @@ -4,7 +4,7 @@ #ifndef ODB_TRAVERSAL_ELEMENTS_HXX #define ODB_TRAVERSAL_ELEMENTS_HXX -#include <cutl/compiler/traversal.hxx> +#include <libcutl/compiler/traversal.hxx> #include <odb/semantics/elements.hxx> namespace traversal diff --git a/odb/traversal/enum.cxx b/odb/odb/traversal/enum.cxx index fa33f92..fa33f92 100644 --- a/odb/traversal/enum.cxx +++ b/odb/odb/traversal/enum.cxx diff --git a/odb/traversal/enum.hxx b/odb/odb/traversal/enum.hxx index cd141f2..cd141f2 100644 --- a/odb/traversal/enum.hxx +++ b/odb/odb/traversal/enum.hxx diff --git a/odb/traversal/fundamental.hxx b/odb/odb/traversal/fundamental.hxx index 974e74f..974e74f 100644 --- a/odb/traversal/fundamental.hxx +++ b/odb/odb/traversal/fundamental.hxx diff --git a/odb/traversal/namespace.hxx b/odb/odb/traversal/namespace.hxx index 223322b..223322b 100644 --- a/odb/traversal/namespace.hxx +++ b/odb/odb/traversal/namespace.hxx diff --git a/odb/traversal/relational.hxx b/odb/odb/traversal/relational.hxx index a78e26b..a78e26b 100644 --- a/odb/traversal/relational.hxx +++ b/odb/odb/traversal/relational.hxx diff --git a/odb/traversal/relational/changelog.cxx b/odb/odb/traversal/relational/changelog.cxx index 149d93d..149d93d 100644 --- a/odb/traversal/relational/changelog.cxx +++ b/odb/odb/traversal/relational/changelog.cxx diff --git a/odb/traversal/relational/changelog.hxx b/odb/odb/traversal/relational/changelog.hxx index 4b7f18f..4b7f18f 100644 --- a/odb/traversal/relational/changelog.hxx +++ b/odb/odb/traversal/relational/changelog.hxx diff --git a/odb/traversal/relational/changeset.hxx b/odb/odb/traversal/relational/changeset.hxx index 3cc522a..3cc522a 100644 --- a/odb/traversal/relational/changeset.hxx +++ b/odb/odb/traversal/relational/changeset.hxx diff --git a/odb/traversal/relational/column.hxx b/odb/odb/traversal/relational/column.hxx index b9c586d..b9c586d 100644 --- a/odb/traversal/relational/column.hxx +++ b/odb/odb/traversal/relational/column.hxx diff --git a/odb/traversal/relational/elements.hxx b/odb/odb/traversal/relational/elements.hxx index 9ecdc6a..2b43ab0 100644 --- a/odb/traversal/relational/elements.hxx +++ b/odb/odb/traversal/relational/elements.hxx @@ -4,7 +4,7 @@ #ifndef ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX #define ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX -#include <cutl/compiler/traversal.hxx> +#include <libcutl/compiler/traversal.hxx> #include <odb/semantics/relational/elements.hxx> namespace traversal diff --git a/odb/traversal/relational/foreign-key.hxx b/odb/odb/traversal/relational/foreign-key.hxx index b8ccba3..b8ccba3 100644 --- a/odb/traversal/relational/foreign-key.hxx +++ b/odb/odb/traversal/relational/foreign-key.hxx diff --git a/odb/traversal/relational/index.hxx b/odb/odb/traversal/relational/index.hxx index 2277fee..2277fee 100644 --- a/odb/traversal/relational/index.hxx +++ b/odb/odb/traversal/relational/index.hxx diff --git a/odb/traversal/relational/key.cxx b/odb/odb/traversal/relational/key.cxx index 15a131c..15a131c 100644 --- a/odb/traversal/relational/key.cxx +++ b/odb/odb/traversal/relational/key.cxx diff --git a/odb/traversal/relational/key.hxx b/odb/odb/traversal/relational/key.hxx index d0ba2d7..d0ba2d7 100644 --- a/odb/traversal/relational/key.hxx +++ b/odb/odb/traversal/relational/key.hxx diff --git a/odb/traversal/relational/model.hxx b/odb/odb/traversal/relational/model.hxx index 03d70b1..03d70b1 100644 --- a/odb/traversal/relational/model.hxx +++ b/odb/odb/traversal/relational/model.hxx diff --git a/odb/traversal/relational/primary-key.hxx b/odb/odb/traversal/relational/primary-key.hxx index dcefab6..dcefab6 100644 --- a/odb/traversal/relational/primary-key.hxx +++ b/odb/odb/traversal/relational/primary-key.hxx diff --git a/odb/traversal/relational/table.hxx b/odb/odb/traversal/relational/table.hxx index d80e37e..d80e37e 100644 --- a/odb/traversal/relational/table.hxx +++ b/odb/odb/traversal/relational/table.hxx diff --git a/odb/traversal/template.cxx b/odb/odb/traversal/template.cxx index 21a846d..21a846d 100644 --- a/odb/traversal/template.cxx +++ b/odb/odb/traversal/template.cxx diff --git a/odb/traversal/template.hxx b/odb/odb/traversal/template.hxx index 28a64d5..28a64d5 100644 --- a/odb/traversal/template.hxx +++ b/odb/odb/traversal/template.hxx diff --git a/odb/traversal/union-template.cxx b/odb/odb/traversal/union-template.cxx index 9d72fcd..9d72fcd 100644 --- a/odb/traversal/union-template.cxx +++ b/odb/odb/traversal/union-template.cxx diff --git a/odb/traversal/union-template.hxx b/odb/odb/traversal/union-template.hxx index 10d1d49..10d1d49 100644 --- a/odb/traversal/union-template.hxx +++ b/odb/odb/traversal/union-template.hxx diff --git a/odb/traversal/union.hxx b/odb/odb/traversal/union.hxx index 2d1a7af..2d1a7af 100644 --- a/odb/traversal/union.hxx +++ b/odb/odb/traversal/union.hxx diff --git a/odb/traversal/unit.hxx b/odb/odb/traversal/unit.hxx index a90418a..a90418a 100644 --- a/odb/traversal/unit.hxx +++ b/odb/odb/traversal/unit.hxx diff --git a/odb/validator.cxx b/odb/odb/validator.cxx index bf9aa6b..aa45294 100644 --- a/odb/validator.cxx +++ b/odb/odb/validator.cxx @@ -1644,6 +1644,10 @@ namespace bool v (inst != error_mark_node); + // Old version of has_lt_operator() relied on full instantiation + // while the new one is based on SFINAE. + // +#if 0 if (v && DECL_TEMPLATE_INSTANTIATION (inst) && !DECL_TEMPLATE_INSTANTIATED (inst)) @@ -1667,6 +1671,7 @@ namespace global_dc->printer->buffer->stream = s; v = (ec == errorcount); } +#endif if (!v) { diff --git a/odb/validator.hxx b/odb/odb/validator.hxx index d0aeac9..d0aeac9 100644 --- a/odb/validator.hxx +++ b/odb/odb/validator.hxx diff --git a/odb/odb/version.hxx b/odb/odb/version.hxx new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/odb/odb/version.hxx diff --git a/odb/odb/version.hxx.in b/odb/odb/version.hxx.in new file mode 100644 index 0000000..2bf2ae7 --- /dev/null +++ b/odb/odb/version.hxx.in @@ -0,0 +1,54 @@ +// file : odb/version.hxx.in +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_COMPILER_VERSION // Note: using the version macro itself. + +// New numeric version format is AAAAABBBBBCCCCCDDDE where: +// +// AAAAA - major version number +// BBBBB - minor version number +// CCCCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example: +// +// Version AAAAABBBBBCCCCCDDDE +// +// 0.1.0 0000000001000000000 +// 0.1.2 0000000001000020000 +// 1.2.3 0000100002000030000 +// 2.2.0-a.1 0000200001999990010 +// 3.0.0-b.2 0000299999999995020 +// 2.2.0-a.1.z 0000200001999990011 +// +#define ODB_COMPILER_VERSION $odb.version.project_number$ULL +#define ODB_COMPILER_VERSION_STR "$odb.version.project$" +#define ODB_COMPILER_VERSION_ID "$odb.version.project_id$" +#define ODB_COMPILER_VERSION_FULL "$odb.version$" + +#define ODB_COMPILER_SNAPSHOT $odb.version.snapshot_sn$ULL + +// Old/deprecated numeric 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 +// +#define ODB_COMPILER_VERSION_OLD 2049976 + +// ODB interface version: minor, major, and alpha/beta versions. +// +#define ODB_VERSION 20476 + +#endif // ODB_COMPILER_VERSION 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/odb/version.hxx b/odb/version.hxx deleted file mode 100644 index edc58a1..0000000 --- a/odb/version.hxx +++ /dev/null @@ -1,37 +0,0 @@ -// 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 20470 -#define ODB_VERSION_STR "2.5-b.20" - -// 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 2049970 -#define ODB_COMPILER_VERSION_STR "2.5.0-b.20" - -#endif // ODB_VERSION_HXX |